shellphish / how2heap

A repository for learning various heap exploitation techniques.
MIT License
7.1k stars 1.13k forks source link

New little trick in ptmalloc #173

Closed M-ouse closed 6 months ago

M-ouse commented 6 months ago

This is a simple trick to make ptmalloc's free check futile. We all know ptmalloc added tcache for efficiency, but the cons are lacking some security checks than non-tcache free procedure.

When the chunk to be freed has the size within tcache's domain, and corresponding tcache entry has room with redundancy, ptmalloc will put the chunk into tcache without many checks, which actually reduces security integrity. In this case, I first do 3 mallocs: p1 = malloc(sz) p2 = malloc(sz) p3 = malloc(sz). (sz < tcache.size_threshold). If we can use an overflow from chunk p1 to wirte p2.size with sz2 (sz < sz2 <= tcache.size_threshold), then free(p2), chunk p2 will not receive strict check, but be put into tcache directly. Free p1, p3, we got tcache_sz [p3 -> p1], tcache_sz2 [ p2 ]. Then we do p4 = malloc(sz3) (usable_size(sz3) = sz2), we will get p4 == p2, p4 and p3 has an overlapping. Next we can modify p3->next to arbitrary address (tcache poisoning), which is taken as an example, of course we can do further attack since we already have a chunk overlapping.

Demo code is at https://github.com/M-ouse/ptmalloc-exp-test/blob/main/house%20test.c. tested on ptmalloc-2.35, Linux 6.5.0-17-generic #17~22.04.1-Ubuntu

Please let me know your opinions.

Milo-D commented 6 months ago

Okay, as a quick recap:

p1 = malloc(s1);
p2 = malloc(s2);
p3 = malloc(s3);

where s1, s2, s3 <= tcache.size_threshold

Now we assume an OOB write from p1 to p2. We use it to increase p2.size, effectively creating an overlapping chunk.

Though I guess we would have to be careful with the new size if debug is enabled (check_inuse_chunk). I think in that case, the easiest way to handle it is to avoid partial overlaps i.e. set

p2.size = s2 + s3 (<= tcache.size_threshold)

The next

free(p2);

will put p2 into (the wrong) tcache[s2 + s3]. Now we free p3 in order to have some valuable metadata to overlap with when we allocate p2 back from the tcache.

free(p3);
p4 = malloc(s2 + s3); // p2

And from there we can tamper with p3's metadata.

====

You could maybe remove the step where you

free(p3);

in order to make it more generic? One would maybe like to tamper with p3's userdata instead of its metadata. Free'ing p3 would then be optional and case dependent. So maybe something like

p1 = malloc(s1);
p2 = malloc(s2);
p3 = malloc(s3);

// OOB write
p2.size = s2 + s3;

free(p2);
p4 = malloc(s2 + s3);

(option #1: free p3 and tamper with its metadata)
(option #2: keep p3 allocated and mess with its userdata)
M-ouse commented 6 months ago

You are right, what this trick does is chunk overlapping primitive. It could be one of the other exploitations' intermediate process to achieve another attack, and I just use the tcache poisoning as an example. I wonder if it could be treated as a new exploit trick or house.

Milo-D commented 6 months ago

It is at least a primitive on the same level as the other overlap primitives

https://github.com/shellphish/how2heap/blob/master/glibc_2.27/overlapping_chunks.c https://github.com/shellphish/how2heap/blob/master/glibc_2.23/overlapping_chunks_2.c https://github.com/shellphish/how2heap/blob/master/glibc_2.35/mmap_overlapping_chunks.c

So what you got would be the tcache_overlapping_chunks technique.

Not sure if it qualifies for a house though. At least to me, a house combines several primitives to achieve something bigger. But then again, there is no strict definition of what a house actually is I guess.

M-ouse commented 6 months ago

I agree with what you said, a house should have more primitives than just one. It was an existing technique.

Kyle-Kyle commented 6 months ago

I agree with the assessment of @Milo-D , that this technique is a downstream application of chunk overlapping and tcache poisoning. Usually, what we have in this repo is something novel, in the sense that:

  1. a new generic primitive (like chunk overlapping, tcache poisoning, fastbin_reverse_into_tache, notice that some house-series techniques are in this category as well, e.g. house-of-spirit)
  2. a clever (and likely non-trivial) combination of existing primitives to achieve something unexpected in a generic way (house-of-roman etc)
  3. a novel way of abusing pt-malloc that may not be as powerful at the moment but may inspire future research (house-of-gods)

About this PR, it is a combination of chunk overlapping and tcache poisoning. Correct me if I'm wrong, but I think that is a clear way forward given chunk overlapping primitive and an info leak (to bypass safe-linking). So, not as exciting as other techniques. But if you can use some clever downstream application to remove some requirements, that'd be amazing (e.g. house-of-roman requires no leak)

About "house", I don't think there is a clear definition of them.