Can anyone explain how use after free results in a practical exploit?

I ask because I’ve been dabbling in rooting various closed devices at home. I’m finding AI fantastic at finding vulnerabilities statically (from firmware upgrade binaries), but when you then ask it to use it to get root or code execution it just goes in circles.

In the end it usually ends up there’s no exploit it can find, because it can’t actually influence the affected memory in a useful way. This is especially so if that memory is in the heap. On the stack it’s a lot more predictable (especially with OOB writes).

You need to do some https://en.wikipedia.org/wiki/Heap_feng_shui. If you can reasonably reliably trigger some other operation that allocated the freed memory for another purpose, you have something like a type confusion vulnerability.

One example is that you might be able to read what was written there. Here that may mean you could read another process’s packet(s). Imagine they were sending unencrypted passwords, perhaps because it’s in a trusted LAN connection.

Out of interest - what models are you using for this? GLM?

From experience OpenAI and Anthropic have pretty heavy safeguards in recent (public) models.

Mixture of GLM 5.3 cloud, and local Qwen 3.8 FN on Strix Halo.

If it’s freed it can be reallocated, so then you have pointers to the same area of memory used for completely different data structures, and this can go very wrong

I get the principle - what I struggle to find is concrete examples of how it can practically be used.

How do you know which pointer (as I say, especially if it’s in the heap)? And I know it can go wrong - but how do you do more than just crash the kernel?

In OOB writes in a stack variable it’s completely clear to me. For other situations I’d love to find real and clearly explained exploits of past UAF or OOB vulnerabilities.

Getting the right thing allocated in the right place to exploit a UAF is usually very very tricky. For a defender we just need to know it's probably possible and therefore we patch the bug, but the attacker still has a difficult job to do even already knowing a UAF bug.

The simplest thing you could try to do is allocate several more of the same type of object and hope one of them lands at the same memory address. For example if you can UAF a web app's user session and keep it open for a long time period (not true in the TCP bug), you might try to wait until an admin happens to log in with their session at the same address, then use their admin session.

Or you can allocate a lot of something else objects and hope a certain field overlaps, like a function pointer if you can write to it. Like suppose you can get some kind of authentication callback pointer to overlap where your packet sequence number was, maybe you can send a certain number of packets to make it point to the return instruction and skip the authentication.

One technique is to just spam the heap full of a certain object you're hoping will overlap it. E.g. if you want to affect a connection object, you might try to open one million simultaneous connections, then touch the UAF object, then check each connection to see if it got messed up. This is called heap spraying and it's often seen in JavaScript vulnerabilities, a bit less in network vulnerabilities since it's easier to allocate a million Uint8Arrays than to open a million TCP connections.

A web search for "exploit chain uaf" found me a few real-world and a few tutorial examples, though I don't know which are good.

At a high level, though: once you've gotten a new object allocated where the old one was, you now have two different views of the same bit of memory. You use valid operations on one view to put the other one into a corrupted state. For example: in a unixlike kernel, maybe one view has a field containing a function pointer (for method dispatch, essentially). You read that field using the other view, use it to deduce something about the target's memory layout, write a new value to the pointer, then use the field via the first view to cause execution to jump to a location you control.

Maybe check https://github.com/sarabpal-dev/IonStack-S22U It has a full chain reproduction of the use-after-free vulnerability that grants you root on Samsung phones with a locked bootloader.

I don't think a UAF on its own allows control hijacking in an exploit. You will need further logic bugs or the ability to legitimately or illegitimately control memory, either via further requests / function calls, OOB writes and such. And it will always be extremely specific to the application vulnerable.

For example, I remember an exploit workshop around reuse of passwd entries. The vulnerable would validate a passwd entry for a non-root user, use it a bit, and later on free and zero it out for security purposes... except it then looked at the passwd entry again in some conditions. Suddenly, the uid was non-zero at first, and zero later on, so later decisions assumed the user was root.

In a more concurrent situation, a possible vulnerability was a shared cache of these passwd entries. It wouldn't zero them out anymore, but you could create a race condition of starting requests as user A, and then hammering requests as user B, so you'd end up mixing up the permission contexts of these users. Because the passwd entry that had been released back to the pool was overwritten, and then re-read with the value from another context.

The necessary line of thinking tends to be very different from thinking about program code regularly.

It goes around in circles because a UAF alone isn't enough in a lot of cases, sometimes you also need a chain (an info leak, read/write primitive etc), a way to defeat ASLR and so on depending on what you are working on and target mitigations. Not all UAFs can be used for RCEs, some can be used only for DoS or for nothing really.

been doing exactly this lately with camera firmware and yeah the circles thing is real, but im pretty sure its a feedback loop problem, not a model problem

the model spins because it cant test its own guesses. what fixed it for me was emulating the actual device. qemu with the vendor rootfs, real binaries running, and then every claim becomes checkable: did the daemon actually parse my input, which uid is it under, what does its own log say. once it can check things it stops guessing, getting qemu to actually boot the thing was its own little hell though, took more tries than i want to admit.

the other thing i learned the hard way: most static findings die on reachability, not on exploit primitives. so i run them in the emulator first and kill the unreachable ones cheap, before spending any time on heap work

on heap vs stack, i think that intuition is desktop shaped. device userland usually has weak or no mitigations, heap uafs are way more tractable there than youd expect

If you really learned anything you should post your own thoughts, not whatever this lowercased Claudish is.

Oh yes definitely seen the reachability problem!

Good ideas on the QEMU. I guess you’d have to patch the binary if it’s expecting particular hardware etc?

Not sure what you mean by “desktop shaped”?

Can you give a concrete example of UAF on heap? I’m learning a bit on how this is done, and eventually recognised the LLM is doing something called heap spray. But that seemed to rely on hoping some object you wrote just happened to get interpreted by some process. And that was the “no, wait, pointer f isn’t y” loops.

[flagged]