Integer Overflow (Web Applications)
{{#include ../../banners/hacktricks-training.md}}
This page focuses on how integer overflows/truncations can be abused in web applications and browsers. For exploitation primitives inside native binaries you can continue reading the dedicated page:
{{#ref}}
../../binary-exploitation/integer-overflow-and-underflow.md
{{#endref}}
1. Why integer math still matters on the web
Even though most business-logic in modern stacks is written in memory-safe languages, the underlying runtime (or third-party libraries) is eventually implemented in C/C++. Whenever user-controlled numbers are used to allocate buffers, compute offsets, or perform length checks, a 32-bit or 64-bit wrap-around may transform an apparently harmless parameter into an out-of-bounds read/write, a logic bypass or a DoS.
Typical attack surface:
- Numeric request parameters β classic
id,offset, orcountfields. - Length / size headers β
Content-Length, WebSocket frame length, HTTP/2continuation_len, etc. - File-format metadata parsed server-side or client-side β image dimensions, chunk sizes, font tables.
- Language-level conversions β signedβunsigned casts in PHP/Go/Rust FFI, JS
Numberβint32truncations inside V8. - Authentication & business logic β coupon value, price, or balance calculations that silently overflow.
2. Recent real-world vulnerabilities (2023-2025)
| Year | Component | Root cause | Impact |
|---|---|---|---|
| 2023 | libwebp β CVE-2023-4863 | Malformed WebP lossless Huffman tables caused a heap overflow while building decoder lookup tables | A single malicious image was enough to get heap corruption / renderer RCE in Chromium-based browsers. |
| 2024 | Chrome Layout β CVE-2024-7025 | Integer overflow in the rendering/layout pipeline reachable from a crafted HTML page | Demonstrates that integer bugs are not limited to JS engines: HTML/CSS alone can be enough to reach heap corruption. |
| 2024 | Chrome Skia β CVE-2024-9123 | Integer overflow in the graphics stack while processing crafted HTML content | A page visit could trigger an out-of-bounds memory write in the renderer. |
3. Testing strategy
3.1 Boundary-value cheat-sheet
Send extreme signed/unsigned values wherever an integer is expected:
-1, 0, 1,
127, 128, 255, 256,
32767, 32768, 65535, 65536,
2147483647, 2147483648, 4294967295,
9223372036854775807, 9223372036854775808,
0x7fffffff, 0x80000000, 0xffffffff
Other useful formats:
* Hex (0x100), octal (0377), scientific (1e10), JSON big-int (9999999999999999999).
* Very long digit strings (>1kB) to hit custom parsers.
3.2 Burp Intruder template
Β§INTEGERΒ§
Payload type: Numbers
From: -10 To: 4294967300 Step: 1
Pad to length: 10, Enable hex prefix 0x
3.3 Fuzzing libraries & runtimes
- AFL++/Honggfuzz with
libFuzzerharness around the parser (e.g., WebP, PNG, protobuf). - Fuzzilli β grammar-aware fuzzing of JavaScript engines to hit V8/JSC integer truncations.
- boofuzz β network-protocol fuzzing (WebSocket, HTTP/2) focusing on length fields.
3.4 JavaScript and browser coercion cases worth forcing
Not every web integer bug is a native-style size_t wraparound. A lot of exploitable web logic starts with a representation mismatch:
- JavaScript numbers are IEEE-754 doubles, so integers above
Number.MAX_SAFE_INTEGER(2^53 - 1) lose precision. - Legacy code frequently uses bitwise operators such as
|0,~~x,x<<0, orx>>>0, which coerce values to 32-bit signed/unsigned integers. - Browser-facing code often parses a value once in JS and a second time in the backend, producing different range checks and different final values.
Useful probes:
// Precision loss above 2^53-1
JSON.parse('{"n":9007199254740993}').n
// Signed wrap to negative
(2147483648 | 0) // -2147483648
// Unsigned wrap to a huge positive
(-1 >>> 0) // 4294967295
// Common "fast truncation" gadget in legacy code
(4294967297 | 0) // 1
When a target mixes client-side validation with API-side validation, replay the same field as:
- JSON number vs JSON string
- decimal vs hex-like string (
4294967295vs0xffffffff) - plain integer vs scientific notation (
10000000000vs1e10) - positive vs negative boundary (
2147483647,2147483648,-1,4294967295)
Interesting symptoms:
- Pagination or
limitchecks pass, but the query executes with0,-1, or a huge unsigned value. - Frontend blocks a value while the backend accepts it after a second parse.
- A value displayed in the UI is not the value finally used by the API / renderer / WASM module.
4. Exploitation patterns
4.1 Logic bypass in server-side code (PHP example)
$price = (int)$_POST['price']; // expecting cents (0-10000)
$total = $price * 100; // β 32-bit overflow possible
if($total > 1000000){
die('Too expensive');
}
/* Sending price=21474850 β $total wraps to β2147483648 and check is bypassed */
4.2 Heap overflow via image decoder (libwebp 0-day)
The WebP lossless decoder bug behind CVE-2023-4863 was a good reminder that browser bugs still start with simple arithmetic mistakes around attacker-controlled metadata. In practice, a crafted image can make the decoder build invalid Huffman lookup tables and write past the heap before consistency checks finish. For web testing this means that image dimensions, chunk sizes, color-table counts and compression metadata are still first-class attack surface when the browser or the backend parses user-supplied files.
4.3 Browser-based XSS/RCE chain
- Integer overflow in V8 gives arbitrary read/write.
- Escape the sandbox with a second bug or call native APIs to drop a payload.
- The payload then injects a malicious script into the origin context β stored XSS.
4.4 Web logic bug β DOM XSS via integer truncation
This pattern is much more common in pentests than full renderer RCE:
const raw = JSON.parse(location.hash.slice(1)).len;
const len = raw | 0; // "fast" int cast to signed 32-bit
if (len <= 64) {
preview.innerHTML = userInput.slice(0, len);
}
If raw=4294967295, then len becomes -1. Depending on the surrounding code, this may:
- bypass a max-length check,
- make
slice(0, -1)drop the last character and preserve the rest of the payload, - or desynchronize validation and the eventual sink (
innerHTML, template renderer, markdown preview, etc.).
The offensive lesson is simple: whenever you see bitwise truncation in client-side code, test whether the sanitized/validated length is the same value that later reaches the DOM sink.
4.5 WASM note
If the target uses Emscripten/WASM, a single integer bug in linear-memory management can often be upgraded into DOM XSS by corrupting writable HTML templates instead of the sanitized source string:
{{#ref}}
wasm-linear-memory-template-overwrite-xss.md
{{#endref}}
5. Defensive guidelines
- Use wide types or checked math β e.g.,
size_t, Rustchecked_add, Gomath/bits.Add64. - Validate ranges early: reject any value outside business domain before arithmetic.
- Enable compiler sanitizers:
-fsanitize=integer, UBSan, Go race detector. - Adopt fuzzing in CI/CD β combine coverage feedback with boundary corpora.
- Stay patched β browser integer overflow bugs are frequently weaponised within weeks.
References
- Cloudflare: Uncovering the Hidden WebP vulnerability (CVE-2023-4863)
- NVD: CVE-2024-7025
{{#include ../../banners/hacktricks-training.md}}