Address-space randomisation is on: what exactly does one leaked pointer buy an attacker?
answer
- the base moves, the offsets do not
- one pointer collapses a whole mapping
- bases are drawn per mapping
- one bug became two bugs
- low bits within a page never move
basics
~20 sRandomisation's whole protection is a secret: where things are. One leaked address recovers it, because everything inside a mapping moves by the same offset, turning a probabilistic guess into a deterministic exploit against that process.
solid answer
~50 sRandomisation picks a fresh base for each mapping at process start - the stack, the heap, the region shared libraries land in, and the executable itself when it is built position independent. What it does not randomise is the *internal* layout: the distance from a library's base to any function or instruction fragment inside it is fixed when that library is built. So one disclosed pointer subtracts to the base, and the base plus a fixed offset gives every address the attacker wants. The same reasoning is per mapping - a leaked stack pointer says nothing about the library base, because those bases were drawn separately. In practice the mitigation converts a one-bug exploit into a two-bug exploit: the attacker now needs a disclosure primitive as well as the corruption. That is the price, and an uninitialised buffer echoed back in a reply, an out-of-bounds read or an error string quoting a pointer all pay it.
go deeper
Know that randomisation hides where code and data sit rather than changing the code itself, and that the base is chosen once when the process starts.
Explain why offsets inside a mapping are fixed at build time, so one leaked pointer yields the mapping base and every address in it, while other mappings stay unknown.
Demonstrate that you rate a memory-disclosure defect and a corruption defect in the same reachable surface as one exploit, and name the disclosure sources a network daemon actually leaks through.
Be able to say what randomisation genuinely buys - the end of the copy-pasted exploit - and refuse to let that be recorded as the class being handled.
## What randomisation randomises At process start the loader places each region at a base chosen at random: the stack, the heap, the region that shared libraries are mapped into, and - if the executable was built position independent - the program image too. The point is that the attacker's chain needs *addresses*: the address of a resident function to return into, the address of an instruction fragment, the address of a controlled buffer. If those move on every start, an exploit written against one machine does not run on the next. ## What it does not randomise The distance from a mapping's base to anything inside it. A library's internal layout is fixed at build time, so if the attacker learns the runtime address of any one thing inside it, subtracting the known build-time offset yields the base, and the base plus any other build-time offset yields any other address inside that mapping. One pointer collapses the whole mapping. Two consequences follow immediately. - The leak has to be *of the right mapping*. A stack address does not give up the library base and vice versa, because those bases were drawn independently. An attacker who needs both usually needs a disclosure that reaches both, or two leaks. - Randomisation is page-granular, so the low bits that index within a page are not randomised at all. That is why partially overwriting only the low bytes of a stored pointer is a real technique: it needs far less unknown information than forging a full address. ## Why this makes it a price rather than a fix Before randomisation, one memory-corruption defect was an exploit. After it, one defect is usually a crash, and an exploit needs a *second* capability: something that discloses memory. The catalogue of disclosure sources in a memory-unsafe network daemon is unglamorous and long: - a reply built from a buffer that was never fully initialised, so the tail carries whatever the last request left there, including pointers; - an out-of-bounds read where a length or index taken from the request is trusted past the data actually written; - a format-string defect that prints stack contents; - an error or diagnostic string that quotes a pointer value back to the caller; - a data structure serialised out with an embedded pointer field that nobody thought was reachable. None of those are exotic, and this is the honest core of the subject: the second bug is a cost, not an impossibility. Add a month of somebody's time to the estimate and the cost is met. ## Guessing instead of leaking Where the entropy in the base is small, the attacker can simply guess. On 32-bit systems the number of possible bases was small enough that brute force over a service that survives failed attempts was practical. Moving to 64-bit addresses raised that number enormously, which is precisely why the leak - not the guess - became the standard route. Note the shape of that history: the mitigation was not broken, it was *repriced*, and the attacker moved to the cheaper of two options. ## Directionality worth stating precisely A leaked address proves where one object is *in that process, at that moment*. It does not prove the same layout holds in another process; a freshly started process gets a fresh draw. This matters when you reason about whether a disclosure is fatal: a leak in a short-lived process spawned per request is worth much less than the same leak in a daemon whose workers all share one draw, because in the second case the attacker can leak in one interaction and corrupt in another. ## An out-of-bounds read is not a lesser bug Engineers routinely triage a read-only out-of-bounds defect as low severity because it does not corrupt anything. Against a hardened target it is the *enabling* half of the pair. The exploit needs both halves, and the market price of the corruption defect goes up or down depending on whether a disclosure is available in the same reachable surface. When you rate two defects in the same daemon, rate them as a set. ## What this means for the argument you will actually have Someone will say the layout is random, therefore the class is handled. The precise reply is that randomisation stores a secret and the exploit's job is now to learn that secret; secrets in a process that talks to untrusted input have many ways out. What randomisation reliably buys is that a copy-pasted public exploit no longer works unmodified - which is genuinely worth having, and is a statement about which adversaries are priced out, not about the defect.
- Does a leaked stack address help an attacker locate a library function?No. Each mapping gets its own base draw, so a stack pointer fixes the stack region and nothing else. The attacker either needs a disclosure that reaches the library mapping too, or a fragment reachable via a pointer already stored on the stack, such as a saved return address into library code.
- Why do experienced people refuse to triage an out-of-bounds read as low severity?Because against randomisation it is the half that supplies the address secret. A corruption defect and a disclosure defect in the same reachable surface are one exploit, not two minor issues, so they should be rated as a set rather than individually.
- What does randomisation still buy if leaks are this common?It ends the copy-pasted exploit. A public proof of concept written against one build no longer runs unmodified, so the population that can use the defect shrinks to those who can find or buy a disclosure and rebuild the chain. That is a real reduction in who can pay, not a claim of safety.
saying these in an interview costs you the question
- Thinks the layout is re-randomised during the life of a process
- Believes one leaked pointer reveals all mappings at once
- Treats an out-of-bounds read as harmless because it writes nothing
- Assumes 64-bit entropy makes the mitigation absolute
- Says randomisation hides the code rather than its address