wk8/9 heap
Theme: UAF, tcache poisoning (to-do: unsolved)
Week 8: glibc <2.32
thread and needle (50)
Given
A program
Usage: Interact with the program for the flag
libc.so.6
Goal
Enter the heap base address in decimal for the flag.
Solve
How do we find the base address of the heap? By leveraging the fact that tcache_perthread_struct (of the main thread) is the first item allocated on the heap. Assuming this is true, &tcache_perthread_struct coincides with the heap's base address.
Let's leak &tcache_perthread_struct by using the fact that heap chunks freed into tcache have their metadata overwritten like this:
8 bytes 8 bytes
cached .____________________________.____________________________.
allocation | fd | &tcache_perthread_struct |
`----------------------------`----------------------------`The second 8-bytes contains &tcache_perthread_struct. Our goal is to leak this.
We will allocate a chunk and free it into tcache bin using program logic. Then, we will read this chunk's second 8-bytes, which happens to be "stitch length".
Static debugging of the program reveals that user input for heap base is interpretted as a decimal integer. So, we will convert &tcache_perthread_struct into decimal, and enter it in the next round of menu choice
Key Points
tcache_perthread_struct
What is it: a structure that is tcache itself
Details: it's often the first thing allocated onto the heap
Why do we care: Since it's the first thing allocated on the heap, its address coincides with heap base address.
Questions
Memory
How to tell how many threads there are in a program?
Through dynamic debugging like
gdb:info threads
How do you determine page portion of a memory address?
In modern 64-bit systems, page sizes are usually
0x1000bytesThis means the least-sig 3 bits are for page offsets
The rest of the bits form the page number
Tcache
Is tcache_perthread_struct what we think of as tcache? Or is it just a structure within tcache?
It is tcache itself. Contains tcache bins and counts for each.
What is tcache_perthread_struct and what does it contain?
Array of ptrs to tcache bins 1. Each tcache bin (
tcache_entry) contains: ptr to head of singly-linked list, integer counterNumber of tcache bins (
TCACHE_MAX_BINS; 64 by default)Default features: each bin contains a max of 7 entries; total 64 bins, ranging from 0x20 to 0x410 sizes in increments of 0x10.
sneaky leak (50)
Given
The program
Usage: Interact with for the flag
libc.so.6
Usage: For the offset of main_arena symbol
Goal
Enter system's absolute address through menu option 4 to get the flag
Solve
To break ASLR for system(), we must first leak a glibc address, such as main_arena. To calculate the offset, we simply subtract main_arena's offset (grep from the provided glibc file libc.so.6) from its leaked address.
Why do we choose to leak main_arena and not any other glibc address? Because this program allows for really big memory allocations that can be freed into the unsorted bin, and we know that the first freed chunk in the unsorted bin will have its forward and backward pointers point to main_arena, a glibc address.
Our strategy is to leak main_arena by allocating a chunk that we'll free into the unsorted bin:
Note: read leak is only possible provided the metadata does not get overwritten when the chunk is reallocated. Luckily this is the case in our program!
First, we allocate a chunk that we know will be large enough to go into the unsorted bin when freed. The threshold is 0x420 bytes. By statically debugging the program on Binja, we find that by entering index 65, we will allocate a chunk of size 0x420, which means this chunk will get kicked into unsorted bin once freed.
So, we allocate this chunk by choosing menu option 3 for allocate_idx and entering 65. Then, we enter menu option 1 for free_idx and input 65 when prompted for index.
Next, we read the metadata by calling menu option 2 for read_idx. These are the first 8 bytes (forward pointer), which contains the address of main_arena. We calculate glibc_base offset using the usual equation of base = absolute - offset. Then, we calculate system's absolute address (use the provided libc.so.6 to find system's offset). Once we have it (in decimal), we choose menu option 4 and enter it for the flag.
Key Points
For the first chunk freed into unsorted bin, its forward and backward ptrs will point to main_arena, a glibc address
Since the unsorted bin is a doubly-linked, circular list, when we free more chunks in, the first chunk will point - forward and backward - to the last chunk, not at main_arena.
Questions
What is main arena?
The arena for the main thread. It's the default. Single-threaded programs only have this arena.
What is arena?
An arena is a structure used in
glibcto manage heap memoryEvery arena has its own set of bins: fastbins, unsorted, large, small (not tcache as tcache is per-thread)
An arena can be shared between many threads
contained within a struct called
malloc_state
What is the unsorted bin?
One per arena
When a chunk does not fit into tcache and fast bins, it goes into this temporary bin before being sorted into small or large bins
Doubly-linked, circular list (same as small and large bins)
Circular means the first and last chunks are 0x420 bytes
It is pointed to by the arena's
malloc_statestruct, which lives in glibc's memory section (specifically in .data segment)
Week 9: new glibc (≥ 2.32)
Note: We can't use free hook strategy with new glibc versions anymore.
Sneaky heap leak (50)
Solve
Allocate then free a chunk into tcache.
We assume that because this chunk is the first thing to be allocated, its page is same as heap base. How to get its page address? Use its next pointer after it gets freed into tcache. It's safe-linked, because it's the last item in the tcache bin, its next chunk is NULL. This gives it the property that the next pointer is simply its page address.
We choose an index that will give us a chunk of appropriate size that will get freed into tcache. We choose index =2. Allocate, free it, then allocate it. Now read it. Take those bytes, shift it to the left by 12 as per the safe-linking encryption algorithm, and that will be the page address, which we reason is the same as heap base.
Choose menu option 4 and enter the address (in decimal) to obtain flag.
Notes
Concepts
How to leak a glibc addr, heap-style?
allocate a super large chunk > free it and it goes into unsorted bin
Why do we need it to be in the unsorted bin?? Bc the unsorted bin's head and tail point to glibc's
Note on metadata for unsorted/large/small bins: the 2nd quadword is a back pointer (cuz these bins are circular doubly-linked lists)
Pitfall: when you free a super large chunk that does not go into the tcache, this chunk might not go into unsorted bin. It might just be reclaimed by the heap.
use-after-free
Free several chunks into the same bin
These freed chunks' metadata will contain pointers to each other
If you realloacate a chunk and its metadata remains unchanged, you can read its forward ptr, which is the next chunk's addr (on the heap) > you just leaked a heap addr
Further: if that heap addr contains data that is a glibc addr > read it > you just broke ASLR!
Why do we care about &tcache_perthread_struct ?
Significance:
It's often the first thing allocated on the heap - which means it's probably on the same page (size of multiples of 0x1000) as the heap > reading the page portion of the struct's addr, we leak the heap base addr!
Where can we find it?
When a heap chunk is freed into tcache bin, its second quad-word gets set to &tcache_perthread_struct
Exploit Tricks
print() does not know the length of the thing it's printing; it relies on a null-byte terminator!
Exploit by: Buffer Overflow
You have two chunks. First chunk you freed and reallocated. The second chunk has data you want to read. Given they are adjacent in memory, you can use a heap buffer overflow to write to the first chunk and clobber the second. When you print something from the first chunk, make sure you clobber the null-byte so that print() will continue printing, all the way onto data from the second chunk.
Last updated