// Name: tcache_poison.c
// Compile: gcc -o tcache_poison tcache_poison.c -no-pie -Wl,-z,relro,-z,now
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
void *chunk = NULL;
unsigned int size;
int idx;
setvbuf(stdin, 0, 2, 0);
setvbuf(stdout, 0, 2, 0);
while (1) {
printf("1. Allocate\n");
printf("2. Free\n");
printf("3. Print\n");
printf("4. Edit\n");
scanf("%d", &idx);
switch (idx) {
case 1:
printf("Size: ");
scanf("%d", &size);
chunk = malloc(size);
printf("Content: ");
read(0, chunk, size - 1);
break;
case 2:
free(chunk);
break;
case 3:
printf("Content: %s", chunk);
break;
case 4:
printf("Edit chunk: ");
read(0, chunk, size - 1);
break;
default:
break;
}
}
return 0;
}
RELRO가 적용되어있어 hook을 이용할 생각을 하자!
코드를 보면 사용자가 입력한 size만큼 할당을 받을 수 있고, 해제할 수 있으며, 청크의 값을 출력하거나 조작하는 것도 모두 가능합니다.
청크를 해제하는 case 2를 보면 청크를 해제하고 chunk 포인터를 초기화하지 않으므로 다시 해제하는 것이 가능하다. 즉, Double Free 취약점이 존재한다.
또한, chunk 포인터를 초기화하지 않으므로 해제된 청크의 데이터를 case 4에서 조작할 수 있습니다. 이를 이용하면 Double Free 보호 기법을 우회할 수 있다.
hook을 one_gadget으로 덮어 쉘을 실행하도록 해보자 그러기 위해서는 libc 주소도 구해야하는데 일단 이를 위해 Tcache Duplication을 이용해하는데 key 값을 조작해 Tcache Duplication 일으켜 보자
첫 청크를 할당받고 해제하면 tcache_put 이 호출되어 해제된 청크 + 8 위치(key)에 tcache_entry값이 들어가게됩니다.
[그림 1-1]에서 edit을 통해 tcache_entry 값 1byte가 A로 덮여 보호 기법을 우회할 수 있습니다.
key 하위 1byte가 0x41로 덮임
edit("A"*9)을 통해 첫 청크를 두 번 해제할 수 있습니다. [그림 1-3]을 보면 계속 실행가능하다.
-> tcache에 Duplication 발생 중
edit("A"*9) 코드를 주석 처리하고 실행하면 [그림 1-4] 처럼 에러를 뱉어내고 종료됩니다.
코드를 보면 setvbuf 함수에 인자로 stdin, stdout을 전달하는데, 이 포인터 변수들은 각각 libc 내부의 IO_2_1_stdin과 IO_2_1_stdout을 가리킵니다. 따라서 이 중 한 변수의 값을 읽으면, 그 값을 이용하여 libc의 주소를 계산할 수 있습니다.
[그림 1-5]를 실행하면 [그림 1-6] 결과에서 libc 안에 있는 _IO_2_1_stdout_ 주소를 잘 받아온다.
alloc(0x50,"\x60") 에서 \x60을 넣는 이유는 [그림 1-7]을 보면 확인할 수 있다.
[그림 1-7]은 alloc(0x50,p64(stdout))을 실행 후 상황이다.
alloc(0x50,"B")을 통해 0x21db260에 "B"라는 문자를 쓴다. 이 말은 malloc과 동시에 할당받은 주소 값에 문자를 입력하는데 0x601010의 값은 _IO_2_1_stdout_ 주소를 가리키고있어 이 주소가 바뀌면 문제가 발생한다. 따라서 하위 1byte가 \x60이므로 \x60을 넣어줘야한다.
libc base 주소를 통해 free_hook과 one_gadget 주소를 구할 수 있다.
tcache duplication을 통해 free_hook 주소에 one_gadget을 덮고 free 함수를 호출하면 쉘이 실행된다.
여기서 주의할 점은 앞서 오염시킨 tcacht[0x50]을 재사용해서는 안된다는 것입니다.Tcache Poisoning으로 stdout에 청크를 할당받을 때, stdout의 fd는 _IO_2_1_stdout_이었습니다.
따라서 이 상태에서 0x30의 크기로 다시 할당을 요청하면, _IO_2_1_stdout_에 청크를 할당받게 됩니다. 해당 구조체는 표준 출력과 관련하여 중요한 역할을 하므로, 임의로 값을 조작해서는 안됩니다. 이런 경우에는 다른 크기의 tcache를 대상으로 공격을 시도하는 것이 좋습니다.
최종 익스 코드이다.
remote로 바꾸고 libc 경로, 원가젯 바꾸고 익스 ㄱㄱ
성공!
'Dreamhack - pwnable' 카테고리의 다른 글
tcache_dup2 (0) | 2023.02.01 |
---|---|
tcache_dup (0) | 2023.02.01 |
uaf_overwrite (0) | 2023.01.28 |
ptmalloc2 (0) | 2023.01.28 |
basic_exploitation_003 (0) | 2023.01.24 |