CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



CVE-2017-1000375
9.838.41%

NetBSD Stack Clash: When the Stack Met the Linker

Alon Barad
Alon Barad
Software Engineer

Feb 25, 2026·7 min read·5 visits

PoC Available

Executive Summary (TL;DR)

NetBSD mapped the `ld.so` library immediately below the stack. Attackers can use large allocations to 'jump' over the stack guard page, land in `ld.so` memory, and overwrite function pointers to gain code execution.

In the golden age of memory corruption, 2017 was the year the 'Stack Clash' returned with a vengeance. CVE-2017-1000375 is a specific, devastating instance of this class affecting NetBSD. By mapping the dynamic linker (`ld.so`) directly adjacent to the stack region, the OS unwittingly created a playground for attackers. Despite protections like ASLR and stack guard pages, a clever manipulation of stack allocation allows an attacker to 'jump' the safety rails and overwrite critical linker structures, leading to total system compromise.

The Hook: Geometry is Destiny

In the world of exploit development, we often obsess over bad code—missing null checks, integer overflows, or use-after-free bugs. But sometimes, the vulnerability isn't in the code logic itself; it's in the geometry of the process memory. CVE-2017-1000375 is a masterclass in why memory layout matters just as much as code quality.

NetBSD, like a well-meaning architect, designed its process memory space with efficiency in mind. However, it made a fatal error in urban planning: it placed the user-space stack right next to the dynamic linker (ld.so). In a standard process, the stack grows downward (from high addresses to low addresses). If it grows too much, it hits a 'guard page'—a demilitarized zone meant to kill the process if breached.

But here's the kicker: NetBSD placed ld.so directly below that guard page. If an attacker could figure out how to step over the guard page rather than walking into it, they wouldn't just crash the app—they would land silently in the memory space of the most powerful utility in the process: the runtime linker. It’s like locking your front door (the guard page) but leaving the window to the master bedroom (ld.so) wide open.

The Flaw: Jumping the Tripwire

To understand this flaw, you have to understand the 'Stack Guard Page'. It is essentially a tripwire. The kernel maps a page of memory at the bottom of the stack with no read/write permissions. If the stack grows incrementally (pushing values one by one), the Stack Pointer (SP) eventually hits this page, triggers a segmentation fault, and the kernel kills the process. Safe, right?

Not quite. The flaw in NetBSD (and other systems vulnerable to Stack Clash) is the assumption that the stack always grows continuously. It doesn't. Functions like alloca() or variable-length arrays allow a program to subtract a massive value from the Stack Pointer in a single instruction.

> [!NOTE] > The Mechanics of the Jump > If the stack pointer is at address 0x1000 and the guard page is at 0x0000 to 0x0FFF, a subtraction of 0x2000 moves the pointer to 0xFFFFF000. The CPU never touches the guard page addresses. It simply teleports the pointer past them.

In NetBSD's case, because ld.so was mapped immediately below the stack, this 'teleportation' lands the Stack Pointer squarely inside the writable memory segments of the dynamic linker. The kernel doesn't notice because the destination address is valid, writable memory. The tripwire is intact, but the intruder is already inside.

The Code: The Smoking Gun

The vulnerability isn't a single line of C code you can point to and laugh at; it's a systemic failure in the memory allocator's policy. However, we can look at the Proof-of-Concept (PoC) provided by Qualys to see exactly how this geometry is weaponized. The code is deceptively simple.

/* NetBSD_CVE-2017-1000375.c - trimmed for clarity */
 
static void smash_no_jump(const size_t smash_size) {
    char buf[1024];
    memset(buf, 'A', sizeof(buf));
    // Recursion to grow the stack close to the edge
    if (smash_size > sizeof(buf))
        smash_no_jump(smash_size - sizeof(buf));
}
 
int main(const int argc, const char * const argv[]) {
    // ... setup code ...
    const size_t smash_size = strtoul(argv[1], NULL, 0);
    smash_no_jump(smash_size);
}

This PoC demonstrates the 'Clash' aspect. By recursively calling smash_no_jump, the attacker forces the stack to grow until it is right up against the ld.so mapping. In a real weaponized exploit, the attacker wouldn't just fill the stack; they would use a specific allocation pattern:

  1. Pad: Fill the stack until it's adjacent to the guard page.
  2. Jump: Trigger a large allocation (e.g., via a vulnerability in an image parser allocating a buffer based on image width).
  3. Smash: The application resumes execution, thinking it is writing to a local buffer, but it is actually overwriting ld.so structures.

The fix implemented in NetBSD 7.1.1 didn't change the alloca logic; instead, it changed the map layout. The kernel was patched to enforce a much larger gap (essentially a 'Death Valley') between the stack and any other mapping, making the jump impossible without hitting unmapped memory.

The Exploit: Clash, Jump, Smash

Let's break down the attack chain. Qualys dubbed this the "Clash, Run, Jump, Smash" method. It sounds like a dance move, but it ends with a root shell.

Phase 1: The Clash

The attacker identifies a target SUID binary or a remote service. They need to control the stack depth. They might do this by passing deep recursive arguments or large environment variables. The goal is to bring the Stack Pointer (RSP) as close to the guard page as possible without touching it.

Phase 2: The Jump

This is the critical moment. The attacker triggers a code path that performs a large stack allocation that is larger than the guard page size (typically 4KB at the time).

Phase 3: The Smash

Now that RSP points into ld.so, the application continues. It eventually decides to write data to this 'buffer'. This write operation corrupts the linker's internal state. A classic target is the Global Offset Table (GOT) or function pointers used by the linker during symbol resolution.

When the application later calls exit() or resolves a dynamic symbol, ld.so wakes up, reads the corrupted pointer, and unwittingly jumps to the attacker's shellcode (which was likely sprayed on the stack earlier). Game over.

The Impact: From Local to Global

Why is this CVSS 9.8? Usually, stack clashes are treated as Local Privilege Escalation (LPE) bugs. You run it on a shell you already have to get root. However, the vector here is listed as Network (AV:N). This implies that if you can trigger this allocation behavior in a network daemon (like an HTTP server or mail handler running on NetBSD), you can achieve Remote Code Execution (RCE).

If the vulnerability is triggered in a critical system process or a setuid binary, the attacker instantly gains root privileges. The proximity of ld.so is particularly damning because ld.so is loaded into every dynamically linked executable. This isn't a vulnerability in one program; it's a vulnerability in the environment that hosts all programs.

Successful exploitation allows:

  • Code Execution: Running arbitrary shellcode.
  • Privilege Escalation: Bypassing user restrictions.
  • Defense Evasion: Since the guard page is skipped, no segmentation fault is generated, often leaving no logs behind.

The Fix: Mind the Gap

Fixing Stack Clash required a multi-layered approach. You can't just tell developers "don't allocate large buffers," because they won't listen.

  1. Kernel-Level Layout Randomization: NetBSD 7.1.1 introduced a much larger guard region. Instead of a single 4KB page, the kernel now reserves 1MB (or more) of unmapped space below the stack. Jumping 1MB with a single alloca is significantly harder and rarer than jumping 4KB.

  2. Stack Probing (-fstack-check): This is the compiler-side fix. When this flag is enabled, the compiler inserts code for every allocation larger than a page size. This code 'probes' (touches) every page between the old SP and the new SP.

// Conceptual logic of -fstack-check
sub rsp, 4096
test [rsp], rax  // Touch the page
sub rsp, 4096
test [rsp], rax  // Touch the next page

This ensures that you cannot step over the guard page. You are forced to walk through it, triggering the trap and crashing the program safely before you can do any damage.

Official Patches

NetBSDNetBSD Security Advisory 2017-005

Technical Appendix

CVSS Score
9.8/ 10
CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
EPSS Probability
38.41%
Top 3% most exploited

Affected Systems

NetBSD 7.1NetBSD 7.0NetBSD 6.x

Affected Versions Detail

Product
Affected Versions
Fixed Version
NetBSD
NetBSD
<= 7.17.1.1
AttributeDetail
Attack VectorNetwork / Local
CVSS v3.09.8 (Critical)
EPSS Score38.41%
Exploit StatusPoC Available
WeaknessStack Clash
Target Componentld.so / Kernel Memory Manager

MITRE ATT&CK Mapping

T1190Exploit Public-Facing Application
Initial Access
T1068Exploitation for Privilege Escalation
Privilege Escalation
CWE-119
Improper Restriction of Operations within the Bounds of a Memory Buffer

Known Exploits & Detection

Exploit-DBQualys Security Advisory and PoC for NetBSD Stack Clash

Vulnerability Timeline

Vulnerability Disclosed by Qualys
2017-06-19
CVE Assigned
2017-06-19
NetBSD Patches Released
2017-06-20
PoC Published on Exploit-DB
2017-06-28

References & Sources

  • [1]Qualys Stack Clash Advisory
  • [2]NVD CVE-2017-1000375
Related Vulnerabilities
CVE-2017-1000364CVE-2017-1000366

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.