In this video we're learning how to link fake chunks into the smallbins by leveraging the House of
Lore.
Before we begin, let's flesh out our understanding of malloc's smallbins.
Much like the unsortedbin, each smallbin is a doubly linked, circular list of free chunks.
The head of each smallbin resides in an arena with subsequent forward and backward pointers held
inline in the first 2 quadwords of free chunk user data.
Unlike the unsortedbin, each smallbin only holds free chunks of a specific size, from 0x20
through 0x3f0, overlapping the fastbin range.
For example, the 0x20 smallbin only holds 0x20-sized chunks.
The 0x200 smallbin holds 0x200-sized chunks, etc..
If you're curious as to how fast chunks can end up in the smallbins, it is possible to move chunks
from the fastbins into the unsortedbin via a function called malloc_consolidate(), which we'll cover
in a different technique.
Smallbins operate in a first in, first out or FIFO manner, meaning that chunks sorted into a
smallbin from the unsortedbin are linked into the front of the list, and chunks allocated from a
smallbin come from the back of the list.
If you ever need to refresh your memory on the smallbins, check out the "Smallbins" header under
the "Arenas" section of the HeapLAB PDF.
Also under the "Arenas" section is the arena layout diagram, which shows where the head of each
small bin resides in an arena.
Because allocations from the smallbins are made from the tail of each bin, linking a fake chunk
into one requires the same operation we conducted against the unsortedbin, overwriting a free chunk's
backward pointer.
Make a copy of your House of Lore unsortedbin exploit or edit it in place as I'll be doing, or you
can work from the commented exploit script I mentioned in the previous video.
Most of our exploit will remain the same, but first we'll need to sort our victim chunk into the
smallbins so that we can leverage the write-after-free bug to tamper with smallbin metadata rather than
unsortedbin metadata.
After the line on which we free chunk A, add a call to the malloc() helper function that requests any
chunk size larger than chunk A. If we request the same size or less, chunk A will be allocated or remaindered.
A larger request will cause a chunk A to be sorted into the appropriate smallbin.
In my case, chunk A is 0xa0-sized, so I'll request a 0xb0-sized chunk.
Comment out the rest of the exploit for now while we get our bearings, then run your script with the
GDB and NOASLR options.
Break in GDB with ctrl+c then inspect the heap with the 'vis' command.
We can see 3 chunks on this heap, the first 2 are chunks A and B respectively.
The third was allocated from the top chunk when we made that last request that sorted chunk A.
pwndbg has annotated chunk A as being linked into the 0xa0 smallbin, which we can confirm with the
'smallbins' command.
The first 2 quadwords of chunk A' user data have been repurposed as smallbin forward and backward
pointers. Chunk B's prev_inuse flag has been cleared and the last quadword of chunk A's user data has
been repurposed as a prev_size field, which now belongs to chunk B.
All we need to do to link our fake chunk into the 0xa0 smallbin is use the write-after-free bug to overwrite
chunk A's backward pointer with the address of our fake chunk.
So let's close GDB and return to our editor.
Start by uncommenting the line that corrupts chunk A's, bk, there's no need to modify it because
allocations from the smallbins use the same partial unlinking process as the unsortedbin.
This means we can still ignore the fd and just provide a counterfeit bk.
You might be thinking that we need to change the size field of our fake chunk, but this isn't actually
the case.
There are no size field integrity checks on allocations from the smallbins.
To prove this, I'll set our fake chunk size to zero, that's one less constraint vs. the unsortedbin
variant of the House of Lore.
Go ahead and uncomment the next call to the malloc() helper function.
We will need to change the amount of memory we request here to match the smallbin we're tampering with.
In my case, I need to request an 0xa0-sized chunk, that's 0x98 bytes of user data.
We can't use the same shortcuts we did with the unsortedbin because each smallbin holds the same
sized chunks and are strictly first in, first out.
So we'll need to make 2 requests for 0xa0-sized chunks.
The first will allocate chunk A and the second should allocate our fake chunk.
Once you've made those changes, run your script with the usual options.
Unfortunately, GDB stops and we can see from the presence of malloc_printerr() in the backtrace panel
that we've triggered one of malloc's exploit mitigations.
To find out which one, switch to the _int_malloc() frame and use the 'context code' command.
This one is misleading, you could be forgiven for thinking that we'd somehow triggered a fastbin-related
mitigation.
But if we switch to the malloc_printerr() frame itself, we can see that the error string passed to
it actually reads "smallbin double linked list corrupted".
The call was actually made from this code.
Note the "goto errout" line, which corresponds to this label here.
The check we're failing is this one, you might recognize it as one half of the safe unlinking checks.
It's following chunk A's' bk and checking that the fd at the destination points back to
chunk A.
Of course, chunk A's bk points to our fake chunk, the fd of which is null.
So we've traded one mitigation for another.
To solve this problem, we need a heap leak, which fortunately this binary has already provided.
Close GDB and return to your editor.
We need to modify the third quadword of our username, which forms the fd of our fake chunk.
It must point to chunk A, which is the first chunk on the heap, so we can just use the "heap" variable
here that holds this program's heap start address.
Remember, that "heap" variable is populated by the heap leak at program start up.
Of course, we have to deal with this mitigation twice, once when we allocate chunk A and a second time
when we allocate our fake chunk.
We've already provided a bk for our fake chunk to avoid a segfaul when it's partially unlinked, but
we'll also need to place an fd near the location it points to that holds the address of our fake chunk.
Having added those pointers to your fake chunk, comment out of the last two calls to the malloc() helper
function so that we can check on our fake chunk in memory before making those final requests.
Run your exploit script with the GDB and NOASLR options, break with ctrl+c, and let's inspect
our handiwork with 'dq &user'.
Our fake chunk doesn't have a valid size field, but we know that's not going to be a problem.
Its fd points to chunk A on the heap.
Remember that we tampered with chunk A's bk, pointing it to our fake chunk.
We need our fake chunks fd to point back to chunk A to satisfy the partial, unlinking check when
chunk A is allocated.
Our fake chunk's bk points to writable memory to ensure we don't segfault during its partial unlink
and we've provided an fd at that location, which points back to our fake chunk, which should satisfy
the partial unlinking check when we allocate it.
It looks like we're ready to go.
So let's head back to our editor
where we'll uncomment those last 3 lines.
The first requests chunk A from the 0xa0 smallbin, the second requests our fake chunk from the same
smallbin, and the third does exactly what it did in the unsortedbin variant of the House of Lore,
edit our fake chunk, overwriting the target data.
So let's check that everything works by running our exploit under production conditions this time.
And using option 4 to print the target.
It reveals that we've successfully made our arbitrary write with the House of Lore smallbins variant.
To summarize, we leveraged a write-after-free bug to overwrite a b belonging to a free chunk in
the smallbins. We overwrote it with the address of a fake chunk we'd prepared elsewhere in memory.
We allocated that victim chunk from the tail of its smallbin and in doing so had to ensure that our
fake chunk's fd pointed to the victim chunk.
This satisfied the partial safe unlinking check.
We then allocated our fake chunk from the same smallbin and used it to make an arbitrary write.
For this to work, our fake chunk had to pass the same partial safe unlinking check, which we managed
by pointing our fake chunk's bk at a location which held an fd pointing back to our fake chunk.
There are no size field integrity checks on allocations from the smallbins, so we didn't have to provide
a fake size field.
Just like in the unsortedbin variant of the House of Lore, you can use misaligned fake chunks if you
want, and you can even chain together multiple fake chunks.
Even though a fake size field isn't required, the smallbins House of Lore variant still has tougher
constraints than the unsortedbin variant.
We need tighter control over more memory
and we also need a heap leak.
If you want to revisit the smallbins House of Lore variant, you can download a commented solution
script from this lecture's resources.
And of course, the HeapLAB PDF contains an overview of the House of Lore along with the malloc internals
we've covered so far.
In the next video, we'll learn how to leverage the largebins House of Lore variant.
I'll see you there.