4

I'm reading Shellcoder's Handbook to learn more about exploitation and overflows. I reached the chapter on Heap Overflows. The book mentions that a heap is split into chunks where each chunk contains two important pieces of info:

  • The size of the previous chunk, if allocated
  • Size of current chunck
  • Data

The following image is taken from a Blackhat presentation enter image description here

I made a small demo to test this. Below is the code:

    1 #include <stdio.h>
    2 #include <string.h>
    3 #include <stdlib.h>
    4
    5 int
    6 main (int argc, char *argv[]){
    7     char *buf, *buf2;
    8
    9     buf = (char *) malloc(1024);
   10     buf2 = (char *) malloc(1024);
   11
   12     printf("buf=%p\n", buf);
   13     printf("buf2=%p\n", buf2);
   14     strcpy(buf, argv[1]);
   15     strcpy(buf2, argv[2]);
   16     printf("buf=%s\n", buf);
   17     printf("buf2=%s\n", buf2);
   18     free(buf2);
   19     return 0;
   20 }

I've placed a breakpoint on line 18 and checked the memory. Here's the dump after running the following command:

./basicheap $(python -c 'print("A"*1000 + " " +"XXXXABCDEFGH")')

This is the memory dump of buf1:

[0x08048543]> pxw 0x50 @ [fcnvar.local_1ch]-8
0x0804b158  0x00000000 0x00000411 0x41414141 0x41414141  ........AAAAAAAA
0x0804b168  0x41414141 0x41414141 0x41414141 0x41414141  AAAAAAAAAAAAAAAA
0x0804b178  0x41414141 0x41414141 0x41414141 0x41414141  AAAAAAAAAAAAAAAA
0x0804b188  0x41414141 0x41414141 0x41414141 0x41414141  AAAAAAAAAAAAAAAA
0x0804b198  0x41414141 0x41414141 0x41414141 0x41414141  AAAAAAAAAAAAAAAA

This is the memory dump of buf2:

[0x08048543]> pxw 0x50 @ [fcnvar.local_20h]-8
0x0804b568  0x00000000 0x00000411 0x58585858 0x44434241  ........XXXXABCD
0x0804b578  0x48474645 0x00000000 0x00000000 0x00000000  EFGH............
0x0804b588  0x00000000 0x00000000 0x00000000 0x00000000  ................
0x0804b598  0x00000000 0x00000000 0x00000000 0x00000000  ................
0x0804b5a8  0x00000000 0x00000000 0x00000000 0x00000000  ................

The output shows 0x411 as the size of the both chunk, which is 1024 + 17 bits for whatever. This is the the first (and only) piece of information in the header for both chunks.

However, I don't see any info relating to any previous chunks. In Shellcoder's, the author is trying to demonstrate how one can overflow buf1 to overwrite the header info of buf2.

Did the writers of glibc forgo the size of previous chunk in the header info, or is it something I'm missing?

P.S: The build command I used was gcc -no-pie -g basicheap.c -o basicheap

7

The diagram you linked to seems to be wrong. The size of the previous chunk is stored in the current chunk iff, the previous chunk is free.

This image is more appropriate of what an allocated heap chunk looks like.

enter image description here

Source: https://sploitfun.wordpress.com/2015/02/10/understanding-glibc-malloc/

Lets take the following code to demonstrate this.

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main (int argc, char *argv[])
{
    char *buf1, *buf2, *buf3;
    buf1 = (char *) malloc(1024);
    buf2 = (char *) malloc(1024);
    buf3 = (char *) malloc(1024);

    printf("buf1=%p\n", buf1);
    printf("buf2=%p\n", buf2);
    printf("buf3=%p\n", buf3);

    memset(buf1, 'A', 1024);
    memset(buf2, 'B', 1024);
    memset(buf3, 'C', 1024);

    free(buf2);
    return 0;
}

Note: Instead of two chunks, I'm taking three chunks or otherwise freeing the first may coalesce it into the top chunk. We will free the middle chunk and examine the chunk contents before and after the free call is executed.

The binary was compiled with gcc -m32 -g ./heap.c. Debug the binary in gdb as you would normally and set a breakpoint on line 20 at the free(buf2) call.

enter image description here

When the breakpoint hit, let's examine the three chunks.

enter image description here

malloc returns the pointer to chunk + 8, so we subtracted 8 from each of the addresses. Additionally, I've also highlighted the chunk sizes (2nd member of a heap chunk) in the above image.

However the chunk sizes are not actually 0x409 . Glibc heap chunks are always aligned to 8 bytes which means the last three bits are always zero. Hence, these three bits are used for storing other information (A, M and P bits).

0x409 when converted to binary comes out to be 100 0000 1001.

enter image description here

To get the true chunk size we have to discard the last three bits (A M P) i.e. we should consider only 100 0000 1000. When this is converted to decimal we get 1032.

enter image description here

The true chunk size is indeed 1032. 1024 bytes for our data and the other 8 for the first two members.

Coming back to gdb, lets step over the free call and examine the chunks once again.

enter image description here

Chunk 2 has been freed. Let's have a look at chunk 3 (buf3).

enter image description here

The first member of chunk 3 now contains 0x408 which is 1032 in decimal - this is the size of chunk 2. Thus the previous size is stored in chunk 3 only after chunk 2 has been freed, not when it is in-use.

The P bit I was referring before stands for PREV_INUSE. This bit is set when the previous chunk is in use. Since the previous chunk is now free this bit is set to 0. As a result the second member now also contains 0x408 (instead of 0x409) which equals 100 0000 1000.

enter image description here

As shown in the image above, the P bit is unset. Hope, this clears your understanding.

  • This was superb! Thank you so much for the effort! – Solidak Sep 9 '18 at 9:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.