CTF大赛题解

CTF大赛题解:
https://github.com/Jinmo/ctfs/tree/master/2021/0ctf
ioa
fa51r-re
vp
https://gist.github.com/Riatre/7626e9bbfbdd08ab0e9306722e3b8b2e
ioa
https://github.com/mephi42/ctf
future
tile
https://github.com/IRS-Cybersec/ctfdump
checkin.md
listbook.md
uc_baaaby.md
https://github.com/hzqmwne/my-ctf-challenges
Secure Storage
lalamblambdadambda
https://github.com/zsxsoft/my-ctf-challenges
Soracon
https://github.com/Septyem/My-Public-CTF-Challenges
cloudpass
how2mutate
https://github.com/Jinmo/ctfs
pwn
rev
https://github.com/perfectblue/ctf-writeups
checkin
cloudpass
future
gasmachine
guthib
how2mutate
listbook
music
onelinephp
pypypypy
secure_storage
singer
uc_baaaby
uc_goood
uc_masteeer
vp
zer0lfsr-
https://github.com/waderwu/My-CTF-Challenges
0XStream
1linephp
2rm1
https://github.com/ceclin/0ctf-2021-2rm1-soln
2rm1
https://github.com/GANGE666/MyCTFChallenges/tree/master/0ctf_tctf_2021_quals
FA51R_RE&PWN
FEA
https://github.com/jwang-a/CTF/tree/master/Writeups/0CTF2021
HashCollision
https://blog.v1me.cn/2021/07/06/TCTF-2021-Music-writeup/
Music
https://gist.github.com/0xKira/e865709fc47c328ffd6fac3da9d36f44
uc_baaaby
uc_masteeer
uc_goood
https://rkm0959.tistory.com/229
zer0lfsr 2021 Full Writeup
https://github.com/rkm0959/Cryptography_Writeups/tree/main/2021/0ctfquals
zer0lfsr_plus.py
https://github.com/rkm0959/Cryptography_Writeups/tree/main/2021
zer0lfsr
https://gist.github.com/as3617/50d598ede736d81bc57804e4d19700e5
1linephp
https://github.com/hzqmwne/my-ctf-challenges
Secure Storage
lalamblambdadambda
https://github.com/IRS-Cybersec/ctfdump/tree/master/0ctf_quals%202021
uc_baaby
listbook
checkin
https://github.com/mephi42/ctf/tree/master/2021.07.03_0CTF_TCTF_2021_Quals
future
tile
https://blog.csdn.net/weixin_52091458/article/details/118533862
singer
https://blog.csdn.net/weixin_45004513/article/details/118493745?spm=1001.2014.3001.5501
pypypypy

https://github.com/awesome-ctf/TCTF2021-Guthib
https://api.github.com/repos/awesome-ctf/TCTF2021-Guthib/commits/6442
what first 4 hash digits
https://github.com/awesome-ctf/TCTF2021-Guthib/commit/6ae87cd1d9b35cfdca4f56f8d8ac78508a404a8b
-> parent -> parent

参考:
RMI 协议介绍:https://xz.aliyun.com/t/8247
https://github.com/waderwu/attackRmi
https://blog.v1me.cn/2021/07/06/TCTF-2021-Music-writeup/

OTHER
For those who are looking forward to the solution of zer0lfsr+/++, please follow rkm0959 and hellman1908 on twitter, I think they will post their wirteups soon.

You can read ZIP spec, or this might help https://gynvael.coldwind.pl/?id=523

pypypypy has many solutions, but they share an idea that, once you have a dict object on the stack, you can use FORMAT_VALUE to convert the dict to string, then build numbers to get the dict keys or construct other strings.
Solutions differ for how to get the dict and what dict to get. I’ll conclude and update later.

pypypypy has many solutions, but they share an idea that, once you have a dict object on the stack, you can use FORMAT_VALUE to convert the dict to string, then build numbers to get the dict keys or construct other strings.
Solutions differ for how to get the dict and what dict to get.

  1. Used by most teams: class and dict: apply class multiple times on anything to get type, and you can use mro or getattribute (get mro or base or bases…) in type.dict to get object. Then you have object.dict[‘getattribute’] that can do anything.
  2. Unintended solution 1: [].reduce_ex(3)[0].globals
  3. Unintended solution 2: Since co_consts is an empty tuple, its memory offset to other const things is fixed (start a docker and run id(what_you_want)-id(())). Therefore you can get things such as object, or even module ‘posix’ via LOAD_CONST. And this solution actually saves (at least) one ‘gift’.
  4. Unintended solution 3: Although LOAD_ATTR uses co_names which is not an empty tuple, its offset is still fixed. Such offset may depends on the environment heavily, but you can leak things and find out it.
  5. Mine used by no teams: trigger exception and you will have the exception’s class at TOS, then get object from mro, and the following is the same.

Essentially if you push twice I believe the commit history is overwritten, so you need to go back to the first push
Visit github api (“public events on repository”), you can find all push commit fork those in the page, then you’ll find that there are two pushes with their hashes - and you can find the commit from there
A sec I’ll give some screenshots

Btw it wasn’t until late that we realised https://guthib.com/.git/HEAD was a scam

alternative solution: https://githubmemory.com/repo/awesome-ctf/TCTF2021-Guthib/activity?page=16
(visualisation of api)

what first 4 hash digits
https://github.com/awesome-ctf/TCTF2021-Guthib/commit/6ae87cd1d9b35cfdca4f56f8d8ac78508a404a8b
-> parent -> parent

remote, help()() can show help, but cannot spawn less because python know it isn’t connected to tty
view source code of pydoc about how it determines when to use less
fun thing:help()() and enter app, pydoc will reload app.py and trigger the input sequence again

My brief solution to lfsr+/lfsr++: G3->G2->G1. G3 with Fast Correlation Attack (using correlation of the combiner, z[i] + z[i+64-9] + z[i+64-6] + z[i+64-3] + z[i+64] = 0, take the linear equation for z[i] only if it holds for all 5 shifts -> error ~0.15, then solve using LeeBrickel ISD from sage). For G2 I used a linear approximation of f and solved the resulting LPN instance (48-bit key) using BKW/LF: two 12-bit reductions and 24-bit walsh-transform. G1 using z3 when half of the outputs are given - not stable though.

flag{musiking}

FEA gdb script
from struct import pack, unpack
from sys import stdout
import capstone
from time import sleep

sc_ep = None
sc_addr = None
after_mmap_bp = None
sc_entry_bp = None
after_enc_bp = None
after_enc_easy_bp = None

def u32(x):
return unpack(‘

def u64(x):
return unpack(‘

class MMapBreakpoint(gdb.Breakpoint):
def init(self):
gdb.Breakpoint.init(self, spec=“mmap”)

def stop(self):
    global after_mmap_bp
    inf = gdb.selected_inferior()
    rsi = int(gdb.selected_frame().read_register('rsi'))
    rdx = int(gdb.selected_frame().read_register('rdx'))
    rcx = int(gdb.selected_frame().read_register('rcx'))
    rsp = int(gdb.selected_frame().read_register('rsp'))
    if rsi != 0x100000 or rdx != 7 or rcx != 34:
        # return False to continue automatically after the breakpoint
        return False
    ret = u64(inf.read_memory(rsp, 8))
    after_mmap_bp = AfterMMapBreakpoint(ret)
    return False

class AfterMMapBreakpoint(gdb.Breakpoint):
def init(self, addr):
gdb.Breakpoint.init(self, spec=f"*({addr})")

def stop(self):
    global sc_addr, sc_ep, sc_entry_bp
    if sc_addr is None:
        rax = int(gdb.selected_frame().read_register('rax'))
        sc_addr = rax
        sc_entry_bp = ScEntryBreakpoint(sc_addr + sc_ep)
        sleep(1) # for alarm in the binary
    return False

class ScEntryBreakpoint(gdb.Breakpoint):
def init(self, addr):
gdb.Breakpoint.init(self, spec=f"*({addr})", type=gdb.BP_HARDWARE_BREAKPOINT)

def stop(self):
    global sc_addr, sc_ep, after_enc_bp, after_enc_easy_bp
    gdb.execute(f'dump memory dump.bin 0x{sc_addr:x} 0x{sc_addr+0x100000:x}')
    inf = gdb.selected_inferior()
    md = capstone.Cs(capstone.CS_ARCH_X86, capstone.CS_MODE_64)
    md.detail = True

    # patch int3 -> 0x06 (pop es, illegal on x64)
    pos = 0
    while pos < sc_ep:
        insn = md.disasm(bytes(inf.read_memory(sc_addr + pos, 15)), offset=pos, count=1).__next__()
        next_pos = pos + insn.size
        # print("gdb: 0x%03x: %-5s %s" %(insn.address, insn.mnemonic, insn.op_str))
        if insn.mnemonic.startswith('j'):
            if insn.mnemonic == 'jmp':
                imm = insn.operands[0]
                assert imm.type == capstone.x86.X86_OP_IMM
                next_pos = imm.value.imm
            else:
                1/0
        elif insn.mnemonic == 'call':
            imm = insn.operands[0]
            assert imm.type == capstone.x86.X86_OP_IMM
            f = imm.value.imm
            dest_code = bytes(inf.read_memory(sc_addr + f, 10))
            if dest_code.startswith(b'\xC3'):
                pass
            elif dest_code.startswith(b'\x48\x83\x04\x24\x01'):
                next_pos += 1
            else:
                pass
        elif insn.mnemonic == 'int3':
            print(f'Patching int3 at 0x{pos:x}')
            assert bytes(inf.read_memory(sc_addr + pos, 1)) == b'\xCC'
            inf.write_memory(sc_addr + pos, b'\x06')
        elif insn.mnemonic == 'ret':
            break
        pos = next_pos      

    pos = sc_ep
    call_cntr = 0
    while True:
        insn = md.disasm(bytes(inf.read_memory(sc_addr + pos, 15)), offset=pos, count=1).__next__()
        next_pos = pos + insn.size
        print("gdb: 0x%03x: %-5s %s" %(insn.address, insn.mnemonic, insn.op_str))
        if insn.mnemonic.startswith('j'):
            if insn.mnemonic == 'jmp':

imm = insn.operands[0]
assert imm.type == capstone.x86.X86_OP_IMM
next_pos = imm.value.imm
else:
1/0
elif insn.mnemonic == ‘call’:
imm = insn.operands[0]
assert imm.type == capstone.x86.X86_OP_IMM
f = imm.value.imm
dest_code = bytes(inf.read_memory(sc_addr + f, 10))
if dest_code.startswith(b’\xC3’):
pass
elif dest_code.startswith(b’\x48\x83\x04\x24\x01’):
next_pos += 1
else:
pass
elif insn.mnemonic == ‘int3’:
print(f’Patching int3 at 0x{pos:x}‘)
assert bytes(inf.read_memory(sc_addr + pos, 1)) == b’\xCC’
inf.write_memory(sc_addr + pos, b’\x06’)
elif insn.mnemonic == ‘ret’:
break
pos = next_pos

    pos = sc_ep
    call_cntr = 0
    while True:
        insn = md.disasm(bytes(inf.read_memory(sc_addr + pos, 15)), offset=pos, count=1).__next__()
        next_pos = pos + insn.size
        print("gdb: 0x%03x: %-5s %s" %(insn.address, insn.mnemonic, insn.op_str))
        if insn.mnemonic.startswith('j'):
            if insn.mnemonic == 'jmp':
                imm = insn.operands[0]
                assert imm.type == capstone.x86.X86_OP_IMM
                next_pos = imm.value.imm
            else:
                1/0
        elif insn.mnemonic == 'call':
            imm = insn.operands[0]
            assert imm.type == capstone.x86.X86_OP_IMM
            f = imm.value.imm
            dest_code = bytes(inf.read_memory(sc_addr + f, 10))
            if dest_code.startswith(b'\xC3'):
                pass
            elif dest_code.startswith(b'\x48\x83\x04\x24\x01'):
                next_pos += 1
            else:
                if call_cntr == 0:
                    print('do_syscall')
                elif call_cntr == 1:
                    print('encrypt_easy')
                    after_enc_easy = next_pos
                elif call_cntr == 2:
                    print('encrypt_hard')
                    after_enc_hard = next_pos
                    break
                call_cntr += 1
        pos = next_pos
    print(f'Setting next BP at 0x{after_enc_hard:x}')
    after_enc_bp = AfterEncBreakpoint(sc_addr + after_enc_hard)
    after_enc_easy_bp = TestBreakpoint(sc_addr + after_enc_easy)
    print('input:')
    return False

class TestBreakpoint(gdb.Breakpoint):
def init(self, addr):
gdb.Breakpoint.init(self, spec=f"*({addr})", type=gdb.BP_HARDWARE_BREAKPOINT)

def stop(self):
    return False

class AfterEncBreakpoint(gdb.Breakpoint):
def init(self, addr):
gdb.Breakpoint.init(self, spec=f"*({addr})", type=gdb.BP_HARDWARE_BREAKPOINT)

def stop(self):
    global sc_addr, sc_ep
    inf = gdb.selected_inferior()
    rbp = int(gdb.selected_frame().read_register('rbp'))
    enc_a = u32(bytes(inf.read_memory(rbp - 0x1c, 4)))
    enc_b = u32(bytes(inf.read_memory(rbp - 0x1c + 4, 4)))
    print(f'enc: 0x{enc_a:08x}, 0x{enc_b:08x}')
    gdb.execute('q')
    return True

gdb.execute(‘source gdb_script.gdb’)
with open(‘gdb_args.txt’, ‘r’) as f:
args = f.readline().strip().split()
sc_ep = int(args[0])
gdb.execute(‘starti’) # < some_inp
MMapBreakpoint()
gdb.execute(‘continue’)

I also added the following gdb script:
handle SIGILL nopass
catch signal SIGILL
condition b p n u m ∗ ( u n s i g n e d c h a r ∗ ) bpnum *(unsigned char*) bpnum(unsignedchar)rip == 0x06
commands
silent
set (unsigned)0xDEAD0000^=0xDEADBEEF
set r i p = rip= rip=rip+1
continue
end


condition b p n u m ∗ ( u n s i g n e d c h a r ∗ ) bpnum *(unsigned char*) bpnum(unsignedchar)rip == 0x06

to not silence real SIGILLs in case I have bugs in my code
0x06 is pop es
the one-byte instruction I used to trigger SIGILL


def wtf(x, mul):
tmp = (mul * x) & 0xFFFFFFFF
if tmp != 0:
return (tmp - (tmp >> 16)) & 0xFFFF
else:
return (1 - mul - x) & 0xFFFF

def rev_wtf(res, mul):
found = False
rev_val = None
for i in range(0x10000):
if wtf(i, mul) == res:
assert not found
found = True
rev_val = i
return rev_val

def rev_enc(arg):
res0 = (arg[0] >> 16) & 0xFFFF
res1 = arg[0] & 0xFFFF
res2 = (arg[1] >> 16) & 0xFFFF
res3 = arg[1] & 0xFFFF

v4v6 = (res0 ^ res1) & 0xFFFF
v9 = wtf(v4v6, 3)
word3v5 = (res2 ^ res3) & 0xFFFF
v10 = (v9 + word3v5) & 0xFFFF
v11 = wtf(v10, 2)
v4 = res0 ^ v11
v5 = (res2 ^ (v11 + v9)) & 0xFFFF
v6 = res1 ^ v11
v12 = (res3 ^ (v11 + v9)) & 0xFFFF
word0 = rev_wtf(v4, 7)
word1 = (v5 - 6) & 0xFFFF
word2 = (v6 - 5) & 0xFFFF
word3 = rev_wtf(v12, 4)
res_a = (word0 << 16) | word1
res_b = (word2 << 16) | word3
return res_a, res_b

from z3 import *
from pwn import *

x = BitVec(‘x’, 32)
y = BitVec(‘y’, 32)

#x = BitVecVal(u32(“b963ff79”.decode(‘hex’)), 32)
#y = BitVecVal(u32(“322bc5d3”.decode(‘hex’)), 32)

v0 = y
v1 = 7 * LShR(x, 16)
v2 = If(UGE(v1, 0), (v1 & 0xffff) - LShR(v1, 16), -6 - LShR(x, 16))

#print(“v2:”, hex(simplify(v2).as_long()))

v3 = x + 6;
v4 = LShR(v0, 16) + 5;
v5 = 4 * (v0 & 0xffff);
v0 = If(UGE(v5, 0), (v5 & 0xffff) - LShR(v5, 16), -3 - v0)

#print(“v0:”, hex(simplify(v0).as_long()))
#print(“v4:”, hex(simplify(v4).as_long()))

v6 = 3 * ((v2 ^ v4) & 0xffff)
v7 = If(UGE(v6, 0), (v6 & 0xffff) - LShR(v6, 16), -2 - ((v2 ^ v4) & 0xffff))

#print(“v6:”, hex(simplify(v6).as_long()))
#print(“v7:”, hex(simplify(v7).as_long()))

v8 = (v7 + (v0 ^ v3)) & 0xffff;
v9 = If(UGE(2v8, 0), ((2v8) & 0xffff) - LShR(2*v8, 16), ~v8)

#print(“v8:”, hex(simplify(v8 * 2).as_long()))
#print(“v9:”, hex(simplify(v9).as_long()))

temp1 = ((v4 ^ v9) & 0xffff) | ((v9 ^ v2) << 16)
temp2 = (((v9 + v7) ^ v3) << 16) | (((v9 + v7) ^ v0) & 0xffff)

#print(“temp1:”, hex(simplify(temp1).as_long()))
#print(“temp2:”, hex(simplify(temp2).as_long()))

s = Solver()
s.add(temp1 == BitVecVal(0x91a2179e, 32))
s.add(temp2 == BitVecVal(0x34b8fbcf, 32))
print s.check()
m = s.model()

password = p32(m[x].as_long()) + p32(m[y].as_long())

print(password.encode(‘hex’))

https://onlinesequencer.net/


handle SIGALRM SIGTRAP nostop noprint nopass

b *0x40137d
r < fea_input
b *0x7ffff7d0f418

catch signal SIGTRAP
condition b p n u m ∗ ( u n s i g n e d c h a r ∗ ) bpnum *(unsigned char*) bpnum(unsignedchar)rip == 0xcc
commands
silent
set (unsigned)0xDEAD0000^=0xDEADBEEF
set r i p = rip= rip=rip+1
continue
end

continue
x/2wx $rbp - 0x1c
continue

x/2wx $rbp - 0x1c will print out the 2 values when it reaches the b *0x7ffff7d0f418 breakpoint
then i just ran my z3 script and got the correct answer
ran it on the original so i knew i twas right
also from this i found this: https://sourceware.org/gdb/onlinedocs/gdb/Signals.html#Signals
When a signal stops your program, the signal is not visible to the program until you continue. Your program sees the signal then, if pass is in effect for the signal in question at that time. In other words, after GDB reports a signal, you can use the handle command with pass or nopass to control whether your program sees that signal when you continue.
so i think if you use handle you make it stop at the SIGTRAP
then do handle SIGTRAP pass
so the handler runs
but the catchpoint is easier

handle SIGALRM ignore nostop
handle SIGTRAP stop
b *0x40137d
r < fea_input
b *0x7ffff7d0f413
c
c
handle SIGTRAP nostop pass
b *0x7ffff7d0f418
c
x/2wx $rbp - 0x1c
c


pwndbg is just some scripts on top of gdb the core is gdb, which is pretty bad


my ioa: https://gist.github.com/Riatre/7626e9bbfbdd08ab0e9306722e3b8b2e
tl;dr signed index compare in req_vip(vip), check_vip(vip), kickout_user(vip) when accessing dhcp_pool.bitmap; heap is not very far from .bss and the offset won’t change due to fork; use check_vip to read master_key; use kickout_user to change dhcp_pool.total_ips to 0x7FFFFFFF, gaining arbitrary r/w on heap; make another connection, read list_vip to find out struct address, overwrite its rbuf ptr for truly arbitary write; __free_hook

https://githubmemory.com/repo/awesome-ctf/TCTF2021-Guthib/activity?page=16

你可能感兴趣的:(Linux,CTF,python)