Heap Spraying Technique
Memory exploitation technique filling heap with shellcode to increase reliability of program execution hijacking.
Continue your mission
Memory exploitation technique filling heap with shellcode to increase reliability of program execution hijacking.
# Heap Spraying Technique
Heap spraying is an attacker technique that turns probabilistic memory corruption into deterministic code execution. Without it, exploiting a corrupted pointer requires knowing the exact memory address where shellcode lives, a nearly impossible task when address layouts shift between runs. By flooding the heap with many copies of a payload, an attacker removes that requirement: any address within the sprayed region becomes a valid landing zone. The technique does not create a vulnerability; it makes existing vulnerabilities reliably weaponizable. Security engineers must understand it not as a curiosity but as a foundational concept explaining why memory safety mitigations like ASLR, DEP, and CFI exist in their current forms, and why those mitigations must be deployed together rather than individually.
---
Heap spraying is an exploitation aid, not a vulnerability class on its own. It refers to the deliberate, high-volume allocation of heap memory blocks filled with attacker-controlled content, typically a NOP sled followed by shellcode or a ROP chain, with the goal of saturating enough of the process address space that a hijacked control-flow transfer lands inside the payload regardless of exact pointer value.
The technique is distinct from heap overflow, heap feng shui, and heap grooming, though all four involve heap manipulation. A heap overflow corrupts adjacent allocations through a buffer boundary violation. Heap feng shui (also called heap massaging) arranges free and allocated blocks in a specific geometric pattern to position a target object at a known offset. Heap grooming is a broader term for any manipulation that coerces the allocator into placing objects in a controlled relationship. Heap spraying is none of these: it is purely about coverage and probability, not about precise layout.
Subtypes include:
Classic spray: Large JavaScript string allocations in browser contexts, repeated hundreds of times, each string containing a NOP sled and shellcode. This was the dominant form from roughly 2004 through 2010.
Typed-array spray: Uses ArrayBuffer or Float64Array objects in JavaScript to spray with byte-exact control, bypassing string encoding constraints that could corrupt shellcode bytes.
Precision spray: Targets a specific predictable heap address (often 0x0c0c0c0c on 32-bit Windows) by computing allocation sizes so that the desired address falls inside a sprayed block. This reduces spray volume and detection surface.
Use-after-free spray: Combines heap grooming with spraying to reclaim freed memory at a known offset, then read or execute from it.
What heap spraying is NOT: it is not a privilege escalation technique, not a network-level attack, and not a standalone exploit. It is an amplifier applied on top of a memory corruption primitive.
---
Memory corruption vulnerabilities such as use-after-free, type confusion, and out-of-bounds write can overwrite a function pointer, vtable entry, or return address with an attacker-supplied value. The attacker must then direct execution to their shellcode. On a 32-bit system without ASLR, the heap starts at a known base address, and an attacker could hardcode the shellcode address. On any modern system, address layout is randomized, and the shellcode location changes with every process instantiation. Heap spraying solves this by making the shellcode occupy such a large fraction of the address space that the attacker does not need to know the precise address; they only need to aim at any address inside the sprayed region.
Step 1: Payload construction. The attacker prepares a unit block. This block begins with a NOP sled, a sequence of single-byte no-operation instructions (0x90 on x86) that perform no computation and advance the instruction pointer forward. The sled is followed by shellcode. A typical block for a 32-bit browser spray might be 1 MB: approximately 1,044,480 bytes of NOP followed by 1,024 bytes of shellcode, with the whole unit repeated to fill the allocation.
Step 2: Mass allocation. Using JavaScript (in a browser context), the attacker allocates hundreds of such blocks. A common configuration is 200 allocations of 1 MB each, consuming 200 MB of heap. On a 32-bit process with a 2 GB user address space, 200 MB represents a 10 percent saturation rate; meaning that a random pointer in the lower heap region has roughly a 1-in-10 chance of landing inside the spray on a single shot, and near-certainty after the attacker also applies precision targeting.
Step 3: Trigger the vulnerability. The attacker triggers the memory corruption bug. This might be a malformed HTML element that causes the browser's DOM engine to free an object and then access it again (use-after-free), or a crafted JavaScript value that causes a type confusion allowing a write to an arbitrary address.
Step 4: Control-flow hijack. The corrupted pointer now contains an address inside the sprayed region. Execution jumps there, slides down the NOP sled, and enters the shellcode.
The "Operation Aurora" attack against Google and other companies exploited a use-after-free vulnerability in Internet Explorer's handling of the HTML object model. After freeing a specific COM object, the exploit reclaimed the freed memory using a JavaScript heap spray, filling it with a payload that began with the bytes 0x0c0c0c0c. The exploit then overwrote the freed object's vtable pointer with 0x0c0c0c0c. When IE called a virtual method on the object, execution jumped to that address, which resided inside the sprayed region, and the shellcode executed. The spray provided reliability: without it, the freed memory might or might not contain attacker data at the moment the vtable call occurred.
Browser security teams have catalogued specific allocation patterns that defeat naive sprays. Garbage collection can reclaim sprayed blocks if the JavaScript engine detects they are unreferenced. To counter this, attackers keep live references to all allocated arrays. Some JavaScript engines also zero-initialize allocations, which destroys NOP sleds built from non-zero bytes; attackers respond by using zero-tolerant shellcode or encoding schemes.
On 64-bit systems, the address space is 128 TB in user space, making coverage sprays impractical. Attackers on 64-bit targets combine precision spraying with information-disclosure vulnerabilities that leak an actual heap address, collapsing the uncertainty to a small region rather than the full space.
Native code contexts (PDF readers, media parsers, Office applications) allow heap spraying using the application's own scripting or data-formatting features, such as ActionScript in Adobe Reader or macro code in Office documents.
---
A memory corruption bug that is difficult to exploit reliably may be rated lower severity during triage. Heap spraying fundamentally changes that calculus. A bug that without spraying produces a crash 95 percent of the time and code execution 5 percent of the time becomes a bug that produces code execution 95 percent of the time with a spray. This gap matters enormously in patch prioritization, bug bounty payouts, and incident response decision-making. Security teams that assess exploitability without accounting for spray-based amplification routinely underestimate risk.
Organizations that deploy ASLR alone, without heap randomization and DEP, provide weaker protection than they believe. Classic ASLR randomizes module base addresses but may leave heap layout partially predictable. Heap spraying exploits this residual predictability. Vendors learned this lesson in the mid-2000s when ASLR was deployed on Windows Vista and attackers immediately demonstrated that browser heap sprays remained effective because the heap base was only weakly randomized. The response was high-entropy ASLR, mandatory DEP, and later Control Flow Guard on Windows.
Every Pwn2Own contest from 2007 through the present has featured exploits that combine a memory corruption bug with heap-based memory manipulation. In Pwn2Own 2011, all three major browsers (Internet Explorer, Firefox, Safari) fell within two days, with every winning exploit relying on some form of heap manipulation to achieve reliable code execution. These controlled demonstrations established that heap spraying was not theoretical; it was the operational backbone of every serious browser exploit at that time.
A persistent misconception is that 64-bit systems are immune to heap spraying. They are not. They require a more sophisticated approach, typically pairing a small information leak with a targeted spray, but the technique remains viable. Another misconception is that JavaScript engine hardening (JIT spraying defenses, pointer compression) has eliminated the threat. In practice, attackers have shifted from pure NOP-sled sprays to ROP-chain sprays and type-confusion-assisted sprays that do not rely on classic NOP sleds at all.
---
CDA approaches heap spraying through the Planetary Defense Model's VSD (Vulnerability Surface Domain), applying the Continuous Surface Reduction methodology. The principle is direct: every surface you expose is a surface we eliminate.
Heap spraying is effective because three conditions are simultaneously true: executable heap memory exists, the process exposes a scripting or allocation interface, and pointer values are partially predictable. CDA's CSR methodology targets all three conditions rather than any single one.
Executable memory elimination. CDA configurations enforce DEP (Data Execution Prevention) and W^X (write XOR execute) policies across all managed systems. This does not prevent the spray itself but ensures that even a successful landing on a sprayed address cannot execute shellcode unless the attacker also chains a ROP bypass, significantly raising the attack cost.
Allocation interface restriction. For applications that do not require scripting, CDA's hardening baselines disable or constrain the scripting engine. For applications that do (browsers, PDF readers, Office), CDA enforces process isolation so that even a successful spray-based exploit executes in a sandboxed process with minimal token privileges.
Address space entropy maximization. CDA deployment standards require high-entropy ASLR, heap randomization, and guard pages between large allocations. These controls force an attacker to pair the spray with an information-disclosure vulnerability, adding a required exploit primitive and reducing the pool of viable exploits.
Detection. At the endpoint level, CDA's monitoring profiles flag processes that allocate unusually large contiguous heap regions in short time windows, a behavioral indicator of spray attempts. This does not block every spray but generates telemetry that feeds incident response workflows.
What CDA does differently from a generic hardening checklist is sequencing: mitigations are applied in dependency order, confirmed through automated testing that verifies the mitigation is actually active rather than merely configured. A DEP policy that is bypassed by a legacy compatibility exception provides no protection; CDA audits for those exceptions and closes them.
---
---
---
CDA Theater missions that address topics covered in this article.
Cryptographic technique that encrypts data while preserving its original format and length, enabling protection without breaking legacy system compatibility.
Guide to HTTP/2 security covering binary framing, HPACK compression attacks, rapid reset vulnerability, stream multiplexing risks, and mitigation strategies.
Explanation of Certificate Transparency framework, covering log servers, Signed Certificate Timestamps, monitoring capabilities, and detection of fraudulent certificates.
Written by CDA Editorial
Found an issue? Help improve this article.