‹ 首页

exploiting-integer-overflow-vulnerabilities

@xalgord · 收录于 昨天 · 上游提交 昨天

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.

适合你,如果你在授权测试中需要发现并利用整数溢出漏洞。

/ 通过 npx 安装 校验哈希
npx oh-my-skill add xalgord/xalgorix/exploiting-integer-overflow-vulnerabilities
/ 通过 bash 安装
curl -fsSL https://oh-my-skill.com/install.sh | bash -s -- xalgord/xalgorix/exploiting-integer-overflow-vulnerabilities
/ 已经装过?验证本机副本,不用重装
npx oh-my-skill verify xalgord/xalgorix/exploiting-integer-overflow-vulnerabilities
安装目标可用 --agent / --scope 或 --to 明确指定;省略时只会在唯一已存在的 agent 目录上自动选择,零命中或多命中会停止并提示。content_hash 缺失或不一致均拒装。
807GitHub stars
~2K上下文体积 · 单文件
索引托管

怎么用

技能原文 SKILL.md作者撰写 · MIT · eeaf26b

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
# 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
// 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
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.
按 MIT 许可原样转载,未经改动 · 在 GitHub 查看 →

评论

登录即可评论;带「已验证安装」的,是发布者名下有本店的安装或持有记录。