I am learning about heap overflow attacks, but I am encountering a problem. Based on the knowledge I found online and my own guesses, I believe that a heap overflow attack involves overwriting metadata in the heap, such as a pointer to a function, to execute the code that the attacker wants to run. However, when I use GDB to inspect memory allocated by malloc, I find that the header of the heap only contains the size allocated by malloc, And the footer only contains the remaining available size. Why???
Here is the process I followed to inspect the memory: source c:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char const *argv[])
{
char *buf,*buf2;
buf=(char*)malloc(1024);
buf2=(char*)malloc(1024*2);
free(buf2);
strcpy(buf,argv[1]);
printf("Done.");
return 0;
}
gcc heap2.c -o heap2 -g
┌──(kali㉿kali)-[~/Desktop/book_c]
└─$ gdb heap2
GNU gdb (Debian 15.2-1+b1) 15.2
Copyright (C) 2024 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from heap2...
(gdb)
(gdb) b main
Breakpoint 1 at 0x1178: file heap2.c, line 8.
(gdb)
(gdb) run AAAAAAAA
Starting program: /home/kali/Desktop/book_c/heap2 AAAAAAAA
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Breakpoint 1, main (argc=2, argv=0x7fffffffdd88) at heap2.c:8
8 buf=(char*)malloc(1024);
(gdb)
(gdb) n
9 buf2=(char*)malloc(1024*2);
(gdb)
(gdb) x/32wx buf-32
0x555555559280: 0x00000000 0x00000000 0x00000000 0x00000000
0x555555559290: 0x00000000 0x00000000 0x00000411 0x00000000
0x5555555592a0: 0x00000000 0x00000000 0x00000000 0x00000000
0x5555555592b0: 0x00000000 0x00000000 0x00000000 0x00000000
0x5555555592c0: 0x00000000 0x00000000 0x00000000 0x00000000
0x5555555592d0: 0x00000000 0x00000000 0x00000000 0x00000000
0x5555555592e0: 0x00000000 0x00000000 0x00000000 0x00000000
0x5555555592f0: 0x00000000 0x00000000 0x00000000 0x00000000
(gdb)
Why only one size? I guess that the metadata might be at the end, so I inspected the memory at the end of the allocated block.
(gdb) x/32wx buf-32+0x00000411-1
0x555555559690: 0x00000000 0x00000000 0x00000000 0x00000000
0x5555555596a0: 0x00000000 0x00000000 0x00020961 0x00000000
0x5555555596b0: 0x00000000 0x00000000 0x00000000 0x00000000
0x5555555596c0: 0x00000000 0x00000000 0x00000000 0x00000000
0x5555555596d0: 0x00000000 0x00000000 0x00000000 0x00000000
0x5555555596e0: 0x00000000 0x00000000 0x00000000 0x00000000
0x5555555596f0: 0x00000000 0x00000000 0x00000000 0x00000000
0x555555559700: 0x00000000 0x00000000 0x00000000 0x00000000
(gdb)
There are no pointers at all!!!! Why????
So, I guessed that the first allocated block might be special. I continued to inspect the next allocated block.
Execute the n command
(gdb) n
11 free(buf2);
(gdb)
Inspect the header of the block.
(gdb) x/32wx buf-32+0x00000411-1
0x555555559690: 0x00000000 0x00000000 0x00000000 0x00000000
0x5555555596a0: 0x00000000 0x00000000 0x00000811 0x00000000
0x5555555596b0: 0x00000000 0x00000000 0x00000000 0x00000000
0x5555555596c0: 0x00000000 0x00000000 0x00000000 0x00000000
0x5555555596d0: 0x00000000 0x00000000 0x00000000 0x00000000
0x5555555596e0: 0x00000000 0x00000000 0x00000000 0x00000000
0x5555555596f0: 0x00000000 0x00000000 0x00000000 0x00000000
0x555555559700: 0x00000000 0x00000000 0x00000000 0x00000000
(gdb) x/32wx buf2-32
0x555555559690: 0x00000000 0x00000000 0x00000000 0x00000000
0x5555555596a0: 0x00000000 0x00000000 0x00000811 0x00000000
0x5555555596b0: 0x00000000 0x00000000 0x00000000 0x00000000
0x5555555596c0: 0x00000000 0x00000000 0x00000000 0x00000000
0x5555555596d0: 0x00000000 0x00000000 0x00000000 0x00000000
0x5555555596e0: 0x00000000 0x00000000 0x00000000 0x00000000
0x5555555596f0: 0x00000000 0x00000000 0x00000000 0x00000000
0x555555559700: 0x00000000 0x00000000 0x00000000 0x00000000
(gdb)
There are still no pointers at all!!!! Why????
So, I made another guess and executed free(buf2). I hope this will give me some clues.
(gdb) n
12 strcpy(buf,argv[1]);
(gdb)
head:
(gdb) x/32wx buf2-32
0x555555559690: 0x00000000 0x00000000 0x00000000 0x00000000
0x5555555596a0: 0x00000000 0x00000000 0x00020961 0x00000000
0x5555555596b0: 0x00000000 0x00000000 0x00000000 0x00000000
0x5555555596c0: 0x00000000 0x00000000 0x00000000 0x00000000
0x5555555596d0: 0x00000000 0x00000000 0x00000000 0x00000000
0x5555555596e0: 0x00000000 0x00000000 0x00000000 0x00000000
0x5555555596f0: 0x00000000 0x00000000 0x00000000 0x00000000
0x555555559700: 0x00000000 0x00000000 0x00000000 0x00000000
(gdb)
end:
(gdb) x/32wx buf2-32+0x00000811-1
0x555555559ea0: 0x00000000 0x00000000 0x00000000 0x00000000
0x555555559eb0: 0x00000000 0x00000000 0x00020151 0x00000000
0x555555559ec0: 0x00000000 0x00000000 0x00000000 0x00000000
0x555555559ed0: 0x00000000 0x00000000 0x00000000 0x00000000
0x555555559ee0: 0x00000000 0x00000000 0x00000000 0x00000000
0x555555559ef0: 0x00000000 0x00000000 0x00000000 0x00000000
0x555555559f00: 0x00000000 0x00000000 0x00000000 0x00000000
0x555555559f10: 0x00000000 0x00000000 0x00000000 0x00000000
(gdb)
I have no clues....
Is this because GDB has some limitation? Or is there another reason?
Of course, the most important thing is how heap overflow attacks are implemented? How can I reproduce this attack?
The behavior you're encountering is not due to GDB limitations, but rather how modern memory allocators like glibc's ptmalloc or jemalloc manage memory.
Here a exemple of heap overflow:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
char *buf1 = (char *)malloc(32); // Allocate 32 bytes
char *buf2 = (char *)malloc(32); // Allocate another 32 bytes
printf("buf1: %p\n", buf1);
printf("buf2: %p\n", buf2);
// Overflow buf1 to overwrite buf2's metadata
memset(buf1, 'A', 60);
printf("buf1: %s\n", buf1);
printf("buf2: %s\n", buf2);
// Trigger corruption
free(buf2);
return 0;
}
Compiling your program:
$ gcc heap_overflow.c -o heap_overflow -g
And run it:
$ $ ./heap_overflow
buf1: 0x55cf8911a2a0
buf2: 0x55cf8911a2d0
buf1: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
buf2: AAAAAAAAAAAA
double free or corruption (out)
Aborted (core dumped)
buf2 got some values of buf1.
I learned pwn heap overflow with this sources:
Try to do some CTF about this, or read some "CTF write ups about heap overflow"