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.
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): class MMapBreakpoint(gdb.Breakpoint): class AfterMMapBreakpoint(gdb.Breakpoint): class ScEntryBreakpoint(gdb.Breakpoint): imm = insn.operands[0] class TestBreakpoint(gdb.Breakpoint): class AfterEncBreakpoint(gdb.Breakpoint): gdb.execute(‘source gdb_script.gdb’) I also added the following gdb script: 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 def wtf(x, mul): def rev_wtf(res, mul): def rev_enc(arg): from z3 import * x = BitVec(‘x’, 32) #x = BitVecVal(u32(“b963ff79”.decode(‘hex’)), 32) v0 = y #print(“v2:”, hex(simplify(v2).as_long())) v3 = x + 6; #print(“v0:”, hex(simplify(v0).as_long())) v6 = 3 * ((v2 ^ v4) & 0xffff) #print(“v6:”, hex(simplify(v6).as_long())) v8 = (v7 + (v0 ^ v3)) & 0xffff; #print(“v8:”, hex(simplify(v8 * 2).as_long())) temp1 = ((v4 ^ v9) & 0xffff) | ((v9 ^ v2) << 16) #print(“temp1:”, hex(simplify(temp1).as_long())) s = Solver() password = p32(m[x].as_long()) + p32(m[y].as_long()) https://onlinesequencer.net/ handle SIGALRM SIGTRAP nostop noprint nopass b *0x40137d catch signal SIGTRAP continue handle SIGALRM ignore nostop 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 https://githubmemory.com/repo/awesome-ctf/TCTF2021-Guthib/activity?page=16
return unpack(‘
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
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
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':
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
def init(self, addr):
gdb.Breakpoint.init(self, spec=f"*({addr})", type=gdb.BP_HARDWARE_BREAKPOINT)def stop(self):
return False
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
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’)
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
0x06 is pop es
the one-byte instruction I used to trigger SIGILL
tmp = (mul * x) & 0xFFFFFFFF
if tmp != 0:
return (tmp - (tmp >> 16)) & 0xFFFF
else:
return (1 - mul - x) & 0xFFFF
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
res0 = (arg[0] >> 16) & 0xFFFF
res1 = arg[0] & 0xFFFF
res2 = (arg[1] >> 16) & 0xFFFF
res3 = arg[1] & 0xFFFFv4v6 = (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 pwn import *
y = BitVec(‘y’, 32)
#y = BitVecVal(u32(“322bc5d3”.decode(‘hex’)), 32)
v1 = 7 * LShR(x, 16)
v2 = If(UGE(v1, 0), (v1 & 0xffff) - LShR(v1, 16), -6 - LShR(x, 16))
v4 = LShR(v0, 16) + 5;
v5 = 4 * (v0 & 0xffff);
v0 = If(UGE(v5, 0), (v5 & 0xffff) - LShR(v5, 16), -3 - v0)
#print(“v4:”, hex(simplify(v4).as_long()))
v7 = If(UGE(v6, 0), (v6 & 0xffff) - LShR(v6, 16), -2 - ((v2 ^ v4) & 0xffff))
#print(“v7:”, hex(simplify(v7).as_long()))
v9 = If(UGE(2v8, 0), ((2v8) & 0xffff) - LShR(2*v8, 16), ~v8)
#print(“v9:”, hex(simplify(v9).as_long()))
temp2 = (((v9 + v7) ^ v3) << 16) | (((v9 + v7) ^ v0) & 0xffff)
#print(“temp2:”, hex(simplify(temp2).as_long()))
s.add(temp1 == BitVecVal(0x91a2179e, 32))
s.add(temp2 == BitVecVal(0x34b8fbcf, 32))
print s.check()
m = s.model()print(password.encode(‘hex’))
r < fea_input
b *0x7ffff7d0f418
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
x/2wx $rbp - 0x1c
continuex/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 SIGTRAP stop
b *0x40137d
r < fea_input
b *0x7ffff7d0f413
c
c
handle SIGTRAP nostop pass
b *0x7ffff7d0f418
c
x/2wx $rbp - 0x1c
c
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