fastbin_dup.c


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <stdio.h>
#include <stdlib.h>
 
int main()
{
    printf("This file demonstrates a simple double-free attack with fastbins.\n");
 
    printf("Allocating 3 buffers.\n");
    int *= malloc(8);
    int *= malloc(8);
    int *= malloc(8);
 
    printf("1st malloc(8): %p\n", a);
    printf("2nd malloc(8): %p\n", b);
    printf("3rd malloc(8): %p\n", c);
 
    printf("Freeing the first one...\n");
    free(a);
 
    printf("If we free %p again, things will crash because %p is at the top of the free list.\n", a, a);
    // free(a);
 
    printf("So, instead, we'll free %p.\n", b);
    free(b);
 
    printf("Now, we can free %p again, since it's not the head of the free list.\n", a);
    free(a);
 
    printf("Now the free list has [ %p, %p, %p ]. If we malloc 3 times, we'll get %p twice!\n", a, b, a, a);
    printf("1st malloc(8): %p\n"malloc(8));
    printf("2nd malloc(8): %p\n"malloc(8));
    printf("3rd malloc(8): %p\n"malloc(8));
}
 
 
 
cs







위 소스코드는 double free bug에 대해서 설명하고 있다.

double free bug는 라이브러리 2.3.2 이하에서 발생한다.


그럼 하나씩 분석해보자.


우선 malloc()을 통해  a,b,c 포인터 변수에 각각 8 byte 크기만큼 할당해준다.


This file demonstrates a simple double-free attack with fastbins.

Allocating 3 buffers.

1st malloc(8): 0x804b410

2nd malloc(8): 0x804b420

3rd malloc(8): 0x804b430


할당된 주소는 위와 같이 a,b,c 순으로 0x804b410, 0x804b420, 0x804b430이다.




각각 0x8 크기만큼 할당했는데 왜 할당된 공간의 주소 차이는 0x16 씩 차이가 나는가?

그 이유는 malloc_chunk 구조체를 보면 알 수 있다.


struct malloc_chunk {

  INTERNAL_SIZE_T      prev_size;  /* Size of previous chunk (if free).  */

  INTERNAL_SIZE_T      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;

};




malloc()을 통해 메모리를 할당받게 되면 바로 데이터를 쓸 수 있는 공간이 할당되는 것이 아니라 prev_size와 size가 나오고 나서 그 뒤에 data 영역이 할당되게 된다. 만약 free()가 될 경우, data 영역은 사라지게 되며 이 공간을 fd와 bk가 덮어쓰게 된다. 

malloc()

 prev_size

size 

data 





free()

 prev_size

size 

fd 

bk 

 




prev_size : flag를 제외한 이전 chunk의 크기
size : 현재 chunk의 크기
fd(foward) : 이전 chunk의 주소를 카리키는 포인터
bk(backward) : 다음 chunk의 주소를 가리키는 포인터


따라서 위의 경우 0x8만큼 할당 받았으나 prev_size와 size의 공간이 존재하기 때문에 실질적으로는 0x10만큼 크기를 차지하게 되는 것이다.


자세한 내용은 이 블로그(https://bpsecblog.wordpress.com/2016/10/06/heap_vuln/) 에 설명이 잘 되어있으니  참조하기 바란다. 




gdb를 통해 구조를 직접 확인하면 좀 더 이해하기가 쉽다.


다음은 free하기 전 a, b, c의 주소이다.


Breakpoint 1, 0x08048507 in main ()

(gdb) x/32wx 0x804b408

0x804b408: 0x00000000 0x00000011 0x00000000 0x00000000

0x804b418: 0x00000000 0x00000011 0x00000000 0x00000000

0x804b428: 0x00000000 0x00000011 0x00000000 0x00000000

0x804b438: 0x00000000 0x00020bc9 0x00000000 0x00000000

0x804b448: 0x00000000 0x00000000 0x00000000 0x00000000

0x804b458: 0x00000000 0x00000000 0x00000000 0x00000000


0x804b410이 a의 data 영역이므로, 0x804b408은 prev_size, 0x804b40c는 size인 것을 알 수 있다. 


여기서 size가 0x10이 아닌 0x11인 이유는 병합이 이루어지지 않도록 하기 위해 PREV_INUSE 플래그가 설정되어있기 때문이다. 이와 마찬가지로 b, c 도 a와 비슷하게 할당되게 된다.





printf("Freeing the first one...\n");
free(a);


free(a)를 통해 먼저 a의 데이터가 삭제된다. 

이전 chunk가 존재하지 않기 때문에 fd는 0이 된다.


0x08048550 in main ()

(gdb) x/32wx 0x804b408

0x804b408: 0x00000000 0x00000011 0x00000000 0x00000000

0x804b418: 0x00000000 0x00000011 0x00000000 0x00000000

0x804b428: 0x00000000 0x00000011 0x00000000 0x00000000





그 다음 주석처리 되어 있는 free(a)를 주석을 푼 뒤 프로그램을 실행시켜보았다.


printf("If we free %p again, things will crash because %p is at the top of the free list.\n", a, a);
free(a);




그 결과 double-free 에러가 발생하였다.


This file demonstrates a simple double-free attack with fastbins.

Allocating 3 buffers.

1st malloc(8): 0x804b410

2nd malloc(8): 0x804b420

3rd malloc(8): 0x804b430

Freeing the first one...

If we free 0x804b410 again, things will crash because 0x804b410 is at the top of the free list.

*** Error in `./fastbin_dup2': double free or corruption (fasttop): 0x0804b410 ***

======= Backtrace: =========

/lib/i386-linux-gnu/libc.so.6(+0x67257)[0xb7e72257]

/lib/i386-linux-gnu/libc.so.6(+0x6d577)[0xb7e78577]

/lib/i386-linux-gnu/libc.so.6(+0x6dd31)[0xb7e78d31]

./fastbin_dup2[0x8048574]

/lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf7)[0xb7e23637]

./fastbin_dup2[0x80483c1]

======= Memory map: ========

08048000-08049000 r-xp 00000000 08:01 941684     /root/pwn/fastbin_dup2

08049000-0804a000 r--p 00000000 08:01 941684     /root/pwn/fastbin_dup2

0804a000-0804b000 rw-p 00001000 08:01 941684     /root/pwn/fastbin_dup2


해당 에러가 발생하는 이유는  이미 한 번의 free(a)를 통해 a가 free list의 맨 위에 있는 상태에서 또 다시 free(a)가 이루어졌기 때문이다.



그렇기 때문에 다음과 같이 free를 해주어야 한다.


printf("So, instead, we'll free %p.\n", b);
free(b);
 
printf("Now, we can free %p again, since it's not the head of the free list.\n", a);
free(a);


위의 경우 free(b)를 한 후에 free(a)를 하게 되므로 free list의 최상위가 a가 아닌 b가 되므로 a를 한 번 더 free 할 수 있게 된다.





1. free(a)


이전 chunk가 존재하지 않으므로 a의 fd는 0이 된다.


0x08048550 in main ()

1: x/3i $pc-5

   0x804854b <main+176>: call   0x8048350 <free@plt>

=> 0x8048550 <main+181>: add    esp,0x10

   0x8048553 <main+184>: sub    esp,0x4

2: x/12xw 0x804b408

0x804b408: 0x00000000 0x00000011 0x00000000 0x00000000

0x804b418: 0x00000000 0x00000011 0x00000000 0x00000000

0x804b428: 0x00000000 0x00000011 0x00000000 0x00000000

(gdb) 





2. free(b)


chunk list에 이전에 free된 a가 존재하므로 a의 주소인 0x804b408이 b의 fd에 들어가게 된다.


0x08048587 in main ()

1: x/3i $pc-5

   0x8048582 <main+231>: call   0x8048350 <free@plt>

=> 0x8048587 <main+236>: add    esp,0x10

   0x804858a <main+239>: sub    esp,0x8

2: x/12xw 0x804b408

0x804b408: 0x00000000 0x00000011 0x00000000 0x00000000

0x804b418: 0x00000000 0x00000011 0x0804b408 0x00000000

0x804b428: 0x00000000 0x00000011 0x00000000 0x00000000





3. free(a)


chunk list의 최상위가 a가 아니기 때문에 한 번더 free(a)가 가능하다. 

chunk list에 이전 chunk인 b가 존재하므로 b의 주소인 0x804b418이 a의 fd에 들어가게 된다.


Breakpoint 4, 0x080485a8 in main ()

1: x/3i $pc-5

   0x80485a3 <main+264>: call   0x8048350 <free@plt>

=> 0x80485a8 <main+269>: add    esp,0x10

   0x80485ab <main+272>: sub    esp,0xc

2: x/12xw 0x804b408

0x804b408: 0x00000000 0x00000011 0x0804b418 0x00000000

0x804b418: 0x00000000 0x00000011 0x0804b408 0x00000000

0x804b428: 0x00000000 0x00000011 0x00000000 0x00000000





프로그램 실행 결과


This file demonstrates a simple double-free attack with fastbins.

Allocating 3 buffers.

1st malloc(8): 0x804b410

2nd malloc(8): 0x804b420

3rd malloc(8): 0x804b430

Freeing the first one...

If we free 0x804b410 again, things will crash because 0x804b410 is at the top of the free list.

So, instead, we'll free 0x804b420.

Now, we can free 0x804b410 again, since it's not the head of the free list.

Now the free list has [ 0x804b410, 0x804b420, 0x804b410 ]. If we malloc 3 times, we'll get 0x804b410 twice!

1st malloc(8): 0x804b410

2nd malloc(8): 0x804b420

3rd malloc(8): 0x804b410





+ Recent posts