Our second technique is known as the House of Lore, which also originates from the malloc maleficarum.
The idea is simply to link a fake chunk into the unsortedbin, smallbins or largebins.
But rather than freeing our fake chunk, as in the House of Spirit, and rather than leveraging a double-
free, as in the fastbin dup, we'll be leveraging a different bug class to tamper with malloc metadata.
Navigate to the house_of_lore directory, once again we'll be checking the security stance of our target,
the house_of_lore binary, with checksec.
It reveals that we're dealing with the usual full RELRO, stack canaries and NX protections. The
binary is non-position-independent, giving us somewhere to target the arbitrary write primitive we'll
be crafting, and it's linked against GLIBC version 2.25.
Remember that if you want to remind yourself which exploit mitigations a particular GLIBC version
implements, you can browse the "Mitigations" section of the HeapLAB PDF.
Now we know what we're dealing with, let's move on to some dynamic analysis, load the house_of_lore
binary into GDB and start it using 'run' or 'r'.
It generously hands us both a libc and heap leak before asking us to input a username. I'm sure
you've figured out by now that asking for a username is my way of letting you craft fake chunk metadata
in the program's data section.
And that's no different here.
For now, I'll just send her some garbage data.
Having received our username, the house_of_lore program prints the numbered menu that we all know
and love.
Option 1 allows us to request up to 6 chunks of heap memory via malloc, and it looks like this time
we're limited to so-called normal chunk sizes, 0x90 and above, but with an imposed cap of
chunks that will fit into the 0x400 largebin.
We'll be learning more about the largebins as we go, but for now, take that to mean we can request
chunks with sizes up to 0x430 inclusive.
Option 2, as usual, lets us free chunks by index starting at zero.
Option 3 allows us to edit chunks by index, meaning that we can write into their user data region.
And, of course, option 4 prints the target data.
Let's take a look at the heap, break in GDB with ctrl+c, then issue the 'vis' command to print
a color map of the default heap.
That 0x430-sized chunk takes a lot of screen real estate, but if I scroll up we can see the start
of the two chunks I requested with sizes 0x90 and 0x430.
The 0x90 chunk is linked into the unsortedbin because I freed it with option 2 and we can see the
data I wrote into the 0x430 chunk here.
We can see where our username is in relation to the target data by dumping quadwords at the "user" struct
with the 'dq' command.
As has often been the case in this course, the "username" field sits just before our target in the program's
data section, confirmed with the 'xinfo' command.
Just like in the House of Spirit's example, we'll be forging fake chunk metadata here shortly.
For now, I'm going to leave you to get familiar with the house_of_lore program and of course, find
the bug, pause the video now whilst you do so.
If you found the bug, then good work, if not, I can tell you that the house_of_lore program suffers
from what's commonly known as a use-after-free bug. Use-after-free, shortened to UAF, is an umbrella term
for bugs that occur when a program treats freed heap memory as if it were still in use.
They're more common than you might think, UAF bugs have been found in iOS, Mac OS, Android, Microsoft
Edge, Google Chrome and the Linux kernel. UAF bugs come in different flavors,
you may be able to read from a free chunk, potentially leaking heap or arena addresses.
Perhaps you can write into a free chunk, corrupting malloc metadata, just like we did in the malloc
testbed when we were learning the unsortedbin attack in HeapLAB Part 1.
It's also possible that a program is making calls through a freed object's vtable pointer, in which
case you may be able to replace that object with data you control to hijack the flow of execution.
Our house_of_lore program suffers from the second variety of UAF bug, a write-after-free.
Let me show you how it manifests.
I'll load the house_of_lore binary into GDB and run it.
I'll enter some garbage data into the "username" field, then request a 0x90-sized chunk with
option 1 from the program menu. To avoid consolidation with the top chunk when I free that first chunk,
I'll also request a second.
I'll free the chunk at index zero.
Break with ctrl+c, then inspect the heap with 'vis'.
As expected, there are two 0x90-sized chunks on the default heap, the first of which is free
and linked into the unsortedbin, confirmed with pwndbg's 'unsortedbin' command. Note the unsortedbin
forward and backward pointers in the first and second quadwords of this chunk's user data.
I'll continue with 'c', hit return to print to the program menu again and select option 3.
I'll choose to edit the chunk at index zero, the same chunk that I've just freed.
Normally at this point, our practice binaries would tell us that we can't do this, but this program
has a write-after-free bug and allows us to continue editing the chunk, even though it's free.
I'll send enough data to corrupt both forward and backward pointers of the free chunk, break with
ctrl+c and inspect the heap again with 'vis'.
It's clear that my data has overwritten both the unsorted chunk's fd & bk. Unfortunately the
version of pwndbg I'm using can't keep up with the situation and neither annotates the free chunk
as being in the unsortedbin in the 'vis' command output, nor in the 'unsortedbin' command output.
Nonetheless, we can confirm this manually by inspecting the main arena, we'll use this command to dump
memory at the main arenas "bins" field, the first entry of which is the head of the main arena's
unsortedbin.
We can see that both the unsortedbin's fd & bk still point to the first chunk on the heap.
It's merely a shortcoming of the pwndbg library
that means it won't annotate it as such.
So it seems that we can leverage this write-after-free bug to tamper with inline malloc metadata.
The House of Lore technique takes advantage of situations like this to link fake chunks into different
bins.
And we're going to start by targeting the unsortedbin.
As it happens, we've actually done this before, hopefully you recall the unsortedbin attack that
we learned in Part 1.
The premise was that we somehow overwrote an unsorted chunk's backward pointer, replacing it with
a pointer to a location into which the address of the unsortedbin would be written when the victim
chunk was allocated or sorted.
Perhaps unknowingly at the time, by doing this, we were linking a fake chunk into the unsortedbin,
of course that fake chunk would never be allocated because we were only interested in the outcome of
the unsortedbin attack.
We even took advantage of the fact that attempting to allocate or sort the fake chunk would trigger
one of malloc's exploit mitigations in the House of Orange technique.
But this time, we do want to allocate the fake chunk, so let's break out the pwntools library and see
what we can come up with.
Make a copy of the pwntools_template.py Python script and open it in your editor of choice.
We've got the usual helper functions, malloc() takes a "size" argument and wraps option 1 of the program
menu. free() takes an "index" argument and wraps option 2 and edit() takes both "index" and "data" arguments
and wraps option 3.
Scrolling to the bottom of the template script reveals some example uses of those helper functions.
Let's keep the code that deals with the username prompt, makes those 2 requests to malloc for chunks
A and B and frees chunk A, remove the call to edit() for now.
That should leave us with 2 normal chunks on the default heap, the first of which is free and linked
into the unsortedbin. The template uses a 0xa0-sized chunk and a 0x90 chunk
but there's nothing special about those sizes,
I just thought we could use an 0xa0 chunk in our example this time.
In reality, any normal sized chunk will do.
Run your script with the GDB and NOASLR options.
Break with ctrl+c and inspect the heap with the 'vis' command.
As expected, there's our free 0xa0 chunk in the unsortedbin, followed by an allocated 0x90 chunk.
The write-after-free bug will allow us to write into the 0xa0 chunk now, so let's use it to overwrite the
chunk's backward pointer with the address of a fake chunk we'll prepare in the program's "username" field.
Close GDB with quit or ctrl+d and return to your editor.
Add a call to the edit() helper function after the call to free().
We'll take advantage of the write-after-free bug to edit chunk A, sending 1 quadword of unimportant
data to overwrite its unsortedbin fd, then a pointer to our soon to be fake chunk to overwrite its
bk.
Once you've added that line, run your script again with the usual options.
Break and inspect the heap.
The 0xa0 chunk is still linked into the unsortedbin, but thanks to the write-after-free bug, its bk
now points to what will become our fake chunk, overlapping the target data.
The 'unsortedbin' command recognizes that the unsortedbin linked list has indeed been corrupted.
We can see that the unsortedbin bk in the main arena points to chunk A, that 0xa0-sized chunk
on the default heap and that chunk A's bk points to the "user" struct.
Right now, there isn't any valid chunk metadata at that location, but if we were to request an 0xa0
sized chunk right now, the following would happen:
malloc would see that the 0xa0 smallbin was empty and begin searching the unsortedbin for an 0xa0 chunk.
Unsortedbin searches are conducted from back to front, so malloc would follow the unsortedbin
bk in the main arena to the 0xa0 chunk on the heap, it would see that its size field matches the request
size, partially unlink it and allocate it.
Let's request an 0xa0-sized chunk now. Continue in GDB, alt+` to the program window, hit return to
print the menu and use option 1 to request 0x98 bytes of user data.
Return to the GDB window, break and inspect the heap and unsortedbin again.
pwndbg still labels the a chunk as being linked into the unsortedbin because the unsortedbin fd
in the main arena still points to it, but more importantly, the unsortedbin bk in the main arena
now points to the "user" struct where we'll forge our fake chunk.
Indeed, if we dump memory at the "user" struct, we see the address of the unsortedbin written into
our fake chunk's fd, a side effect of the partial unlinking process and what we've been calling
the unsortedbin attack.
We know from experience that if we were to make another request that prompted malloc to search the
unsortedbin, it would follow the unsortedbin's bk to the "user" struct, where the lack of valid size
field would trigger one of malloc's exploit mitigations.
All we need to do is provide a valid size field at that location to ensure the mitigation isn't triggered,
allowing us to allocate our fake chunk.
So let's return to our editor to modify the data we send to the "username" field, where we'll prepare our
fake chunk metadata.
First, let's not forget we need to add that request for an 0xa0-sized chunk we made manually a moment
ago.
Next, clear the "username" variable and replace it with the following:
1 quadword of any value that will form the unused prev_size field of our fake chunk and a second quadword
that will form our fake chunk's size field.
This can be any size as long as we're able to request it.
There's just one more piece of this puzzle.
During the partial unlink that will occur when we allocate our fake chunk, malloc will follow its
bk and copy the address of the unsortedbin over the fd at its destination, another unsortedbin
attack, if you will.
This means we have to supply a pointer to writable memory in our fake chunk's bk, since the binary
is non-position-independent, we can just use an address in its writable segment.
I'll use the address of the "user" struct minus 16 so we can see the effect of that second unsortedbin
attack.
Once you've made those changes, go ahead and run your exploit script with the usual options.
Break and issue the 'unsortedbin' command to confirm that the unsortedbin's bk points to our fake
chunk in the "user" struct.
Inspect memory at that location with the 'dq' command where we can see our counterfeit size field
here and our counterfeit bk here, pointing to writable memory. Both of these precede the target
data here.
The only thing left to do is request a chunk size that matches our fake chunk, malloc will search
the unsortedbin, come across our fake trunk, determine that it is an exact fit for our request and
allocate it.
So let's add that final request to our exploit a script, close GDB and return to your editor.
Even though we could add another request after the one for an 0xa0-sized chunk, we can take a shortcut
here and just request our fake chunk directly.
The 0xa0 chunk will be sorted into the 0xa0 smallbin and malloc will continue searching the unsortedbin,
following the 0xa0 chunk's corrupt bk and finding our fake chunk all in one go.
Change the size of this request from 0x98 to 0xd8 or enough user data to match the
size of your fake chunk, if it's not 0xe0-sized. I'll label, this chunk "overlap" because it overlaps
our target.
Then use the edit() helper function to write 4 quadwords of filler into your fake chunk to cover the
distance between the start of the fake chunk's user data and the target.
Then send whatever you want to overwrite the target data with.
Once you've added those lines, run your exploit with the GDB and NOASLR options, alt+` to
the program window, hit return to print to the menu and pick option 4 to print the target data,
which should reveal that we've successfully made an arbitrary write using the House of Lore
If you'd like to see what sort of state we've left the main arena in, you can switch back to GDB,
break with ctrl+c and inspect the unsortedbin.
The unsortedbin bk has adopted the same value that we set our counterfeit bk to, the address of
our fake chunk minus 16.
If we dump memory at that location, we can see that the address of the unsortedbin has been copied
over the fd malloc thought was there.
So we can get an unsortedbin attack for free, as it were, when we allocate our fake chunk.
Remember that I just picked this location arbitrarily so we could see this effect,
I could have pointed the counterfeit bk to any writable memory in the VA space.
There are some interesting things to note about the House of Lore when applied to the unsortedbin like
this.
The first is that much like allocating fake chunks from the fastbins, you can misalign your fake chunk.
If you're trying to find a naturally occurring size field, you can also use invalid flag combinations,
including a set forth-least significant bit.
The second thing is that because you control the fake chunk's bk you can potentially point it
at another fake chunk, then allocate that and so on.
You could even point the counterfeit bk at the same fake chunk and allocate it multiple times, provided
you repaired or did not disturb the bk between allocations.
Lastly, if you check the "Mitigations" section in the PDF, you'll notice that the unsortedbin attack
is mitigated in GLIBC version 2.29, which also affects the House of Lore.
So to summarize what we've just done, we applied the House of Lore technique to the unsortedbin.
We began by leveraging a write-after-free bug to tamper with an unsorted chunk's backward pointer, much
like an unsortedbin attack.
We pointed the victim chunk's bk at a fake chunk we'd prepared elsewhere in memory that overlapped
our target data.
The fake chunk must fulfill 2 constraints:
The first is that its size field has to pass the unsortedbin size sanity check, meaning it should
take a value between the minimum valid chunk size and the total amount of memory the current thread's
arena has checked out from the kernel, a value held in the arena's "system_mem" field.
The second constraint is that the fake chunk's backward pointer must hold the address of writable memory
since it will be referenced and used in a write operation during the partial unlink.
Once we'd satisfied both of these constraints, we simply allocated our fake chunk from the
unsortedbin by making a matching sized request, then used it to overwrite our target data.
And that's the House of Law unsortedbin edition. If you want to refresh your memory on it later you can
download a commented pwntools exploit from this lecture's resources.
And don't forget that this technique is outlined in the PDF, where you'll also find an unsortedbin
flowchart which depicts the paths malloc can take when searching and unsortedbin.
Join me in the next video where we'll see how we can apply the House of Lore to the smallbins.