Skip to content

Conversation

@D4R30
Copy link
Contributor

@D4R30 D4R30 commented Nov 26, 2025

I've come up with a tcache-based technique, and I did not find any House of ... or CTF that can achieve both of its objectives. Basically, it takes advantage of an out-of-bounds tcache metadata writing (both the pointer and counter) to achieve the following objectives:

  • To write an arbitrary decimal value to an arbitrary location on heap, relative to the tcache_perthread_struct, which is allocated on heap. The most powerful use case would be a chunk overlapping attack.
  • To write a freeing chunk's pointer to an arbitrary location on heap, again relative to tcache_perthread_struct. A powerful use case would be chunk edits (tcache poisoning, fastbin corruption, etc.)

And with these prerequisites:

  • The ability to write a large value (anything higher than 64) on an arbitrary location
  • Libc leak
  • Being able to malloc/free with sizes higher than TCache maximum chunk size (0x408)

In the PoC, I demonstrated these two primitives to trigger a chunk overlapping, and a simple arbitrary pointer write.

I've thoroughly explained the technique in this blog post: https://d4r30.github.io/heap-exploit/2025/11/25/tcache-relative-write.html
Here's the summary:

The core concept of TCache relative writing is around the fact that when the allocator is recording a tcache chunk in tcache_perthread_struct, TCache mechanism does not enforce enough check and restraint on the computed tcachebin indice (tc_idx), thus WHERE the tcachebin count and head pointer can be written are not restricted by the allocator by any means. The allocator treats extended bin indices as valid in both tcache_put and tcache_get scenarios. If we’re somehow able to write a huge value on one of the fields of mp_ (tcache_bins from malloc_par), by requesting a chunk size higher than TCache range, we can control the place that a tcachebin pointer and counter is going to be written. Considering the fact that a tcache_perthread_struct is normally placed on heap, one can perform a TCache relative write on an arbitrary point located after the tcache metadata chunk (Even on tcache->entries list to poison tcache metadata). By writing the new freed tcache chunk’s pointer, we can combine this technique with other techniques like tcache poisoning or fastbin corruption and to trigger a heap leak. By writing the new counter, we can poison tcache->entries, and write arbitrary value into an arbitrary location of heap, with the right amount of mallocs and frees. With all these combined, one is able to create impactful chains of exploits, using this technique as their foundation.

This technique works on 2.30 and higher versions. The latest version tested is 2.41.

There's also another blog post that mainly discusses mp_ hijack, but does not discuss the arbitrary counter-writing capability. It is more beginner-friendly though, as it provides a more complete discussion of tcache basics: https://4xura.com/binex/pwn-mp_-exploiting-malloc_par-to-gain-tcache-bin-control/

One issue
As it is obvious in tcache_relative_write.c, when the PoC wants to compute &mp_.tcache_bins, a constant is used to compute the distance between unsortedbin and the tcache_bins field. This was the best possible option I had in my mind, because neither mp_ nor main_arena are exported. The difference between &main_arena and &mp_ are also different among glibc versions. If you had any idea on how to remove the constant and make the PoC more portable, please let me know. I've manually calculated and tested all the relative constants for 2.31-2.41, so I expect it will pass all the checks.

Relative offset for &mp_.tcache_bins computation in all versions: 0x938
Relative offset for &mp_.tcache_bins computation in 2.35-2.37 versions: 0x918
Relative offset for &mp_.tcache_bins computation in 2.32-2.34 versions: 0x930
Relative offset for &mp_.tcache_bins computation in 2.31 version: 0x910
@Kyle-Kyle
Copy link
Contributor

This is my understanding of this technique:

  1. requires the ability to overwrite mp_.tcache_bins (so need to know its address)
  2. then tcache will be tricked into thinking its metadata (tcache_perthread_struct) is larger than it actually is and read/write out of bound.
  3. profit

If my understanding is correct, then this is very similar to a technique that was quite popular ~2019. Back then, it was overwriting the fastbin's limit (also in mp_) using the unsortedbin attack (provides the write-x-where primitive) and then allocating pointers out of bound.

I think we should include this.

Also, I have a question/suggestions:

  • You mention that we need "The ability to write a large value (>64) on an arbitrary location". I haven't checked the source code, but I wonder what happens if we do an unaligned write? Do we still need to write a large value? (e.g., if we write 1 to &mp_.tcache_bins+1)
  • I think you didn't explain the objective "To write an arbitrary value into an arbitrary location on heap" well. I have two interpretations of it: 1. free a pointer (A) that is large enough so that you can place it in a location you specify on the heap, the result is the value A is on the heap 2. free a pointer A, UAF overwrite its fwd pointer to B (actually arbitrary value), do allocation again, then B will be on heap. Which one do you mean here? And please clearly explain it in the files.
  • please don't say arbitrary location on heap when you actually mean "outside the boundary of tcache_perthread_struct". (Let's say I want to write to heapbase+0x1000000, it'd be hard to allocate that huge amount to place the pointer there.
  • It'd be great if you can explain that it is possible to obtain the write-x-where primitive using other techniques (e.g., large_bin_attack etc)

@D4R30
Copy link
Contributor Author

D4R30 commented Nov 27, 2025

Thanks for the comment.

So first of all, I think you've missed the most important objective of the attack. This technique is not just about writing a pointer out-of-bounds; it is also about the ability to write the counter out-of-bounds. I've demonstrated a chunk overlapping attack using this capability, in the how2heap PoCs and the blog post.

it was overwriting the fastbin's limit (also in mp_) using the unsortedbin attack (provides the write-x-where primitive) and then allocating pointers out of bound.

You should also note that global_max_fast is not a member of mp_. It is actually a separate global variable.

About the similarity to the fastbins relative write (which is used in the popular technique House of Husk), you're absolutely right. This technique is similar to the fastbins attack because it overwrites bins limit to achieve out-of-bounds write. But here are the facts that I believe make this technique unique and different from the already mentioned:

  • It is not just about a pointer write. tcache_perthread_struct also stores the counter variable, which is used by TCache relative write to write a decimal value out-of-bounds. This was not present in the classic fastbins relative write. It also cannot be easily derived from the fastbins technique in my opinion. So the out-of-bounds counter-writing is the most important capability in this technique.

  • It is differently implemented, has different use cases and is stronger, because it is particularly used to overwrite metadata/data on heap. The House of Husk technique was restricted to the structures present after the fastbins array, and a printf call was required to actually trigger the exploit chain. TCache relative write is more fundamental and opens the doors to wide range of exploit chains, because you now have the ability to manipulate chunks metadata.

I didn't find the global_max_fast technique on this repository. Maybe we could add it to this PR and change the title to Relative write techniques, or we can just do it in the next PR.

For your questions/suggestions:

  • No it doesn't necessarily have to be an aligned write. For example, If you can write a half-word, lets say, on the second most significant half-word, yes it still works. The goal is just to make mp_.tcache_bins a large value. So it doesn't necessarily have to an aligned write.

  • By the "To write an arbitrary value into an arbitrary location on heap" I mean the out-of-bounds counter (decimal value) writing. There are actually two objectives: Out-of-bounds decimal writing and out-of-bounds pointer writing. Your first interpretation is true for the second one: Freeing chunk A that has the A->size large enough, so that A is written to the selected location. But for the out-of-bounds decimal writing: Freeing chunk A that has the A->size large enough, so that the counter is updated out-of-bounds. I've written all the calculations required to actually know what size you have to allocate and free to write the counter or the pointer to the target location.

  • Yes it does make the request size huge, but it still is possible to make that chunk size with some heap grooming. So in theory and many practical cases it is still possible. Would you prefer that I revise this part, or should I add more explanation to make it clearer?

  • I've pointed out many techniques - such as the largebin attack - that can be used to achieve a write-x-where primitive in the * VULNERABILITY * part.

@D4R30
Copy link
Contributor Author

D4R30 commented Nov 29, 2025

Sorry, I’m not sure whether you’re waiting for me to make changes. Based on my previous comment, please let me know if there’s anything you want me to update in the files.
@Kyle-Kyle

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants