2025. 5. 5. 20:40ㆍHacking/Pwnable
외부 스터디에서 받은 문서이기도 하고, 마침 다른 친구들(feat. 스승님과 aegis friends..) 보니까 이거 많이 보는 거 같아서 나도 보게 되었고, 해석을 하나씩 해보려고 한다. 포너블 알못이라 이 문서만 해도 얻어가는게 있었다.
원본 출처 : https://github.com/shellphish/how2heap
GitHub - shellphish/how2heap: A repository for learning various heap exploitation techniques.
A repository for learning various heap exploitation techniques. - shellphish/how2heap
github.com
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
fprintf(stderr, "This file doesn't demonstrate an attack, but shows the nature of glibc's allocator.\n");
fprintf(stderr, "glibc uses a first-fit algorithm to select a free chunk.\n");
fprintf(stderr, "If a chunk is free and large enough, malloc will select this chunk.\n");
fprintf(stderr, "This can be exploited in a use-after-free situation.\n");
fprintf(stderr, "Allocating 2 buffers. They can be large, don't have to be fastbin.\n");
char* a = malloc(0x512);
char* b = malloc(0x256);
char* c;
fprintf(stderr, "1st malloc(0x512): %p\n", a);
fprintf(stderr, "2nd malloc(0x256): %p\n", b);
fprintf(stderr, "we could continue mallocing here...\n");
fprintf(stderr, "now let's put a string at a that we can read later \"this is A!\"\n");
strcpy(a, "this is A!");
fprintf(stderr, "first allocation %p points to %s\n", a, a);
fprintf(stderr, "Freeing the first one...\n");
free(a);
fprintf(stderr, "We don't need to free anything again. As long as we allocate smaller than 0x512, it will end up at %p\n", a);
fprintf(stderr, "So, let's allocate 0x500 bytes\n");
c = malloc(0x500);
fprintf(stderr, "3rd malloc(0x500): %p\n", c);
fprintf(stderr, "And put a different string here, \"this is C!\"\n");
strcpy(c, "this is C!");
fprintf(stderr, "3rd allocation %p points to %s\n", c, c);
fprintf(stderr, "first allocation %p points to %s\n", a, a);
fprintf(stderr, "If we reuse the first allocation, it now holds the data from the third allocation.\n");
}
이게 first_fit.c 풀 코드이고 한 문단씩 해석을 해보자면,
fprintf(stderr, "This file doesn't demonstrate an attack, but shows the nature of glibc's allocator.\n");
fprintf(stderr, "glibc uses a first-fit algorithm to select a free chunk.\n");
fprintf(stderr, "If a chunk is free and large enough, malloc will select this chunk.\n");
fprintf(stderr, "This can be exploited in a use-after-free situation.\n");
이 파일은 공격 방식을 설명하려고 작성된 건 아니나, glibc의 할당의 본질을 보여주기 위해 작성되었다. glibc는 free chunk를 고르기 위해 first-fit 알고리즘을 사용한다. 만약 chunk가 free 상태이고 충분히 크다면 malloc 동적 할당은 이 chunk를 선택한다. 이것은 use-after-free 상황에서 익스플로잇 될 수 있음.
- free chunk란?
=> 프로그램에서 free 포인터를 호출하면, 포인터가 가리키는 메모리 블록은 chunk(해제) 상태가 된다
-> 해당 chunk는 더 이상 사용되지 않으며, "free list", 메모리 재사용을 위한 큐에 등록이 되고, 이 free chunk는 이후에 malloc() 호출이 오면 다시 할당에 재사용될 수 있다. 힙 오버플로우나 Use-After-Free, double free 등의 취약점에서 free chunk를 조작함으로써 malloc의 동작을 바꾸거나, 임의 주소를 할당받게 하거나, 함수 포인터를 덮는 것이 가능해진다.
=> 즉 익스플로잇의 핵심적인 타깃이 됨
- Use-after-free situation?
=> free()로 해제된 메모리를 지속적으로 사용/접근하려는 행위
fprintf(stderr, "Allocating 2 buffers. They can be large, don't have to be fastbin.\n");
char* a = malloc(0x512);
char* b = malloc(0x256);
char* c;
2 버퍼에 할당한다. 2 버퍼는 클 수 있으나 fastbin이 될 필요는 없다.
- fastbin이란?
=> 작은 크기(보통 0x20~0x70 사이)의 해제된 chunk들을 저장해두는 빠른 재사용용 리스트(freelist)임. glibc는 힙을 관리하기 위해 크기별로 여러 개의 fin(free list)를 운영하는데, fastin은 작은 chunk(max가 0x80)이고, 나머지 tcache(glibc 2.26 이상부터 등장), small bin, large bin, unsorted bin과 같이 더 큰 chunk들을 관리한다.
fprintf(stderr, "1st malloc(0x512): %p\n", a);
fprintf(stderr, "2nd malloc(0x256): %p\n", b);
fprintf(stderr, "we could continue mallocing here...\n");
fprintf(stderr, "now let's put a string at a that we can read later \"this is A!\"\n");
strcpy(a, "this is A!");
fprintf(stderr, "first allocation %p points to %s\n", a, a);
-> 첫번째로 0x512 malloc 동적 할당(프로그램이 실행 중에 필요한 크기의 메모리를 힙(heap) 영역에서 직접 요청하여 사용)
-> 두번째로 0x256 malloc 동적 할당
-> 우리는 계속해서 동적할당을 할 수 있다.
-> 가독성을 위해 this is A!라는 문자열을 넣어준다.
-> p 포인터 주소에는 this is A!라는 문자열이 있다. (포인터가 이 문자열을 가리킨다, stderr는 표준 오류 출력 스트림으로 콘솔로 바로 출력되며, printf()와 달리 오류 메시지나 디버깅 용도로 사용한다. 동적 할당이 제대로 되었는지, 포인터가 유효한 주소를 가리키는 지 또한 그 안에 어떤 데이터가 들어 있는지 확인 가능)
fprintf(stderr, "Freeing the first one...\n");
free(a);
-> 첫 번째 malloc 할당을 해제한다.
fprintf(stderr, "We don't need to free anything again. As long as we allocate smaller than 0x512, it will end up at %p\n", a);
-> 우리는 굳이 해제할 이유가 없어. 0x512보다 작기만 하면 할당된 메모리는 %p(a)주소로 할당이 될 거야.
=> 만약 특정 크기의 chunk가 tcache에 있고, 그것을 malloc()으로 다시 요청하면, 항상 같은 주소(a)로 할당되고, 따라서 더 이상 free()를 반복할 필요 없이, 지속적으로 같은 주소를 재사용할 수 있는 상태가 됨
fprintf(stderr, "So, let's allocate 0x500 bytes\n");
c = malloc(0x500);
fprintf(stderr, "3rd malloc(0x500): %p\n", c);
fprintf(stderr, "And put a different string here, \"this is C!\"\n");
strcpy(c, "this is C!");
fprintf(stderr, "3rd allocation %p points to %s\n", c, c);
fprintf(stderr, "first allocation %p points to %s\n", a, a);
fprintf(stderr, "If we reuse the first allocation, it now holds the data from the third allocation.\n");
-> 0x512보다 작기만 하면 되니까 0x500 바이트를 할당해보자.
-> 0x500 동적 할당
-> 3번째 할당(0x500) -> 그리고 우린 아까와 다른 문자열(this is A!가 아닌)을 여기에 넣을거야 this is C!
-> 세 번째 malloc()으로 할당한 포인터 c의 주소와 그 안의 문자열 내용을 출력함.
-> 첫 번째 malloc()으로 할당한 포인터 c의 주소와 그 안의 문자열 내용을 출력함.
-> c에 할당한 데이터가 a에도 그대로 보인다는 건, a와 c가 같은 주소를 가리킨다는 뜻이고, 첫 번째 malloc으로 얻은 chunk a를 free()하고, 이후 비슷한 크기로 malloc()한 c가 그 chunk를 재사용함
'Hacking > Pwnable' 카테고리의 다른 글
[Hacking] / [Pwnable - How2Heap] Heap exploitation : fastbin_dup.c (0) | 2025.05.11 |
---|---|
[Hacking] / [Pwnable - How2Heap] Heap exploitation : calc_tcache_idx.c (0) | 2025.05.06 |
[WriteUp] / [DreamHack] Basic_rop_x64 (0) | 2025.04.16 |
[WriteUp] / [DreamHack] Return to Library (0) | 2025.04.16 |
[WriteUp] / [DreamHack] ssp_001 (0) | 2025.04.16 |