profile image

L o a d i n g . . .

article thumbnail image
Published 2021. 5. 5. 22:09

Chunk

Malloc에 메모리 할당을 요청하면 넓은 메모리의 영역("힙")을 다양한 크기의 덩어리("chunk")로 나눈다.

32bit에서는 해당 청크가 8byte 단위로 할당이 되고, 64bit에서는 16byte 단위로 할당된다. 일반적으로 malloc()을 호출하고 반환되는 주소를 이용하여 데이터를 입력하게 되는데, 사실 반환되는 주소는 청크의 시작부분이 아닌 페이로드의 주소이다.

 

페이로드바로 위에 meta-data를 포함하는 청크 헤더가 존재한다. 이 헤더에서 현재 청크의 사이즈가 몇인지, 인접한 청크의 사이즈는 얼마인지가 들어있다.

 

Chunk는 크게 3가지 타입을 가진다.

 - In-use chunk : 애플리케이션에서 할당받아서 사용중인 덩어리

 - Free chunk : 응용프로그램에서 시스템에 반환한 덩어리

 - Top chunk : Arena의 가장 상단에 있는 덩어리이다.

 

Allocator는 메모리를 할당할 때 Free chunks 중에서 사용가능한 chunk가 있는지 확인한다.

 

 


Struct of malloc_chunk

malloc()은 각 chunk를 관리하기 위해 malloc_chunk 구조체를 이용한다.

구조체 malloc_chunk은 6개의 정보를 관리한다.

struct malloc_chunk {
 
  INTERNAL_SIZE_T      mchunk_prev_size;  /* Size of previous chunk (if free).  */
  INTERNAL_SIZE_T      mchunk_size;       /* Size in bytes, including overhead. */
 
  struct malloc_chunk* fd;         /* double links -- used only if free. */
  struct malloc_chunk* bk;
 
  /* Only used for large blocks: pointer to next larger size.  */
  struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */
  struct malloc_chunk* bk_nextsize;
};

prev_size : 이전 Chunk가 Free chunk가 되면, 이전 Chunk의 크기가 저장된다. 반대로 이전 Chunk가 In-use chunk가 되면, 이전 Chunk의 사용자 데이터가 배치될 수 있다.

 

size : In-use chunk의 크기를 저장한다. 그리고 필드의 맨 끝 3bit는 flag 정보를 나타낸다.

 - PREV_INUSE [P](0x1) 플래그 : 인접한 이전 청크가 사용중일 때 사용됌.

 - IS_MMAPPED [M](0x2) 플래그 : 해당 청크를 mmap()으로 부터 할당받았은 청크일 경우 사용됌.

 - NON_MAIN_ARENA [A](0x4) 플래그 : 현재 청크가 main_arena에서 관리하지 않을 경우에 사용됌.

 

Free chunk는 크기와 히스토리에 따라 다양한 목록에 저장되며, 이러한 목록들을 "bins"라고 한다.

 - fd(Forward pointer)는 동일한 bin에 존재하는 다음 Free Chunk의 포인터를 가진다.

 - bk(Backward pointer)는 동일한 bin에서 이전 Free Chunk의 포인터를 가진다.

 - fd_nextsize, bk_nextsize는 large bin에서 사용하는 포인터이다.

 - fd,bk, fd_nextsize, bk_nextsize는 chunk가 Free chunk일 경우에만 사용된다.

 

모든 청크의 크기는 MALLOC_ALIGNMENT(2 * sizeof(size_t))의 배수이다.

 - 32bit의 경우 size_t의 크기가 4byte이기 때문에 chunk의 크기는 8의 배수가 된다.

 

 


In-use chunk

할당자로부터 메모리를 할당을 받아서 사용중인 메모리 덩어리

 - 이전의 Chunk가 free 상태일 때, 이전의 Chunk의 크기가 prev_size에 저장된다.

 - 해당 chunk의 크기가 size에 저장되고, 필드의 맨 끝 3bit는 flag 정보를 나타낸다.

출처 : lazenca

 

다음 코드를 이용하여 어플리케이션의 메모리에서 in-use chunk를 확인하겠다.

 

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
   
void main(){
    char *heap1 = malloc(136);
    char *heap2 = malloc(80);
 
    free(heap1);
 
    char *heap3 = malloc(136);
    read(STDIN_FILENO,heap3,136);
}

1. malloc()에 크기가 136 byte와 80byte인 메모리 할당을 요청한다.

2. malloc()으로 부터 반환된 포인터를 heap1, heap2에 저장한다.

3. free()에 heap1이 가리키는 메모리의 해제를 요청한다.

4. 그리고 다시 malloc()에 크기가 136byte의 메모리의 할당을 요청한다.

5. 반환된 포인터는 heap3에 저장한다.

6. read()를 이용하여 heap3가 가리키는 메모리에 데이터를 입력한다.

 

 

main 을 디스어셈블한 모습

 

 

다음과 같이 breakpoints를 설정하여 메모리 변경을 확인한다.

 

Before malloc (first breakpoint)

before

 

 

after

첫번째 Breakpoint에서는 malloc()이 호출되기 전이다.

 - 시스템은 Heap 공간이 필요한 경우에만 프로세스에 해당 공간을 맵핑한다.즉, 기본적으로 맵핑되어 있지 않다.

 - malloc()이 호출된 후에 이 프로세스에 Heap 공간이 매핑되었다.

 

 

Allocation of the first heap

할당자로부터 할당받은 첫번째 Heap의 포인터는 0x602010이다. 145(0x91)가 size(0x602008)에 저장되어 있다.

 

할당자에 의해 할당되는 청크의 크기는 MALLOC_ALIGNMENT의 배수가 된다. 이 시스템은 64bit이며, size_t의 크기가 8byte이기 때문에 할당되는 청크의 크기는 모두 16의 배수가 되어야한다. 136(0x88)은 16의 배수가 아니며, 해당 수와 가장 가까운 16의 배수는 144(0x88)이다. 

 

그리고 해당 값에 PREV_INUSE [P](0x1) 플래그를 더한 값이 145(0x91)이다.

 

* 할당이 가능한 메모리의 크기가 top chunk(0x602098)에 저장된다.

 

 

Allocation of the second heap

할당자로부터 할당받은 두번째 Heap의 포인터는 0x6020a0이다.

 

해당 청크의 크기는 97(0x61)이다.

 - 애플리케이션이 요청한 크기는 80(0x50)이다.

 - 청크를 관리하기 위해 필요한 메타데이터(fd,bk)를 저장하기 위해 요청된 크기에 16을 더해서 메모리를 할당한다.

 - 그리고 해당 청크 앞에 사용중인 청크가 있기 때문에 그 값에 PREV_INUSE [P](0x1) 플래그가 더해진다.

 

 

Free the first heap

프로세스는 free()함수를 이용하여 첫번째 덩어리(0x602010)를 해제한다.

 - 0x602010 ~ 0x602018 메모리에 fd, bk 값이 저장된다.

 - 그리고 두번째 chunk의 prev_size에 해제된 chunk의 크기(0x90)가 저장되고, size에는 PREV_INUSE [P](0x1) 플래그 값이 빠진 값이 저장된다.

 

 

Allocation of the thrid heap

할당자로부터 할당받은 세번째 Heap의 포인터는 0x602010이다. 이 포인터는 처음 할당 된 포인터와 동일하다.

 - 할당자는 메모리의 할당을 요청받으면 free chunk를 먼저 사용한다.

 - 다시 할당받은 chunk는 이전에 저장된 데이터가 초기화 되지 않고 그대로 존재한다.

 

변경되는 값은 두번째 chunk의 size값이며, 0x60에서 0x61로 변경된다 (PREV_INUSE [P](0x1) 플래그 값이 추가됌).

 

 

After read() function

새로 할당받은 heap 메모리는 정상적으로 사용가능하다.

 - 값을 입력하게 되면 이전에 저장되어 있던 값을 덮어쓰게 된다.

 

 


Free chunk

할당자에게 반환된 chunk

 

 - fd와 bk는 각각 해제된 다음 청크, 해제된 이전 청크를 가리키는 포인터이다. bins 에서 free 청크를 관리하기 위해 필요함.

 - fd_nextsize, bk_nextsize는 오직 큰 블록에서만 사용된다.

 

출처 : 라젠카

 

 

다음 코드를 이용하여 free chunk의 변화를 확인해보자.

#include <stdio.h>
#include <stdlib.h>
  
void main(){
        char *heap1 = malloc(128);
        char *tmp2 = malloc(8);
        char *heap2 = malloc(128);
        char *tmp3 = malloc(8);
        char *heap3 = malloc(128);
        char *tmp1 = malloc(8);
  
        free(heap1);
        free(heap2);
        free(heap3);
}

 - malloc()에 3개의 128byte, 3개의 8byte의 메모리 할당을 요청한다.

 - 그리고 크기가 128byte인 메모리들을 해제한다.

 

 

Breakpoints

메모리의 변화를 확인하기 위해 다음과 같이 Breakpoint들을 설정

 

 

Allocated memory - first breakpoint

할당자가 할당한 메모리는 다음과 같다.

 - chunk의 크기가 128바이트인 메모리가 3개(0x602010, 0x6020c0, 0x602170) 할당되었다.

 - chunk의 크기가 8바이트인 메모리가 3개(0x6020a0, 0x602150, 0x602200) 할당되었다.

 

 

First free chunk

heap1(0x602010)이 해제되면 해당 chunk에 fd, bk 값이 저장된다.

 - 이전 청크의 크기(0x90)는 tmp2(0x6020a0)의 "prev_size"에 저장되며, "size"에는 PREV_INUSE [P] (0x1) 플래그의 값을 뺀 값(0x20)이 저장된다.

 

 

Second free chunk

heap2(0x6020c0)가 해제되면 해당 chunk에 fd, bk값이 저장된다.

 - fd(0x6020c0)에 저장되는 값은 해당 chunk 앞에 있는 Free chunk의 mchunkptr(0x602000)이다.

 - 그리고 heap1의 bk(0x602018)에 heap2의 mchunkptr(0x6020b0)이 저장된다.

 

 

Third free chunk

heap3(0x602170)가 해제되면 fd(0x602170)에 해당 chunk 앞에 있는 Free chunk의 mchunkptr(0x6020b0)이 저장한다.

 - heap2의 bk(0x6020c8)에 heap3의 mchunkptr(0x602160)이 저장된다.

 

 

 

 

참고
드림핵 - Heap exploitation
https://wogh8732.tistory.com/180
https://www.lazenca.net/pages/viewpage.action?pageId=51970061

'Hacking > Pwnable' 카테고리의 다른 글

[Heap exploitation] Heap Overflow  (0) 2021.05.08
[Pwnable] Heap3  (0) 2021.05.06
[Pwnable] Heap1  (0) 2021.05.05
[P4C] pwnable problem writeup3  (0) 2021.04.29
[P4C] pwnable problem writeup2  (0) 2021.04.29
복사했습니다!