daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

integer-overflow.md (13922B)


      1 ---
      2 title: "Integer Overflow (Web Applications)"
      3 section: "Web Pentesting"
      4 sectionSlug: "pentesting-web"
      5 sourcePath: "src/pentesting-web/xss-cross-site-scripting/integer-overflow.md"
      6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/pentesting-web/xss-cross-site-scripting/integer-overflow.md"
      7 sha: "188de82beb54e70956b2952367a0af91d26758b8"
      8 isIndex: false
      9 modified: true
     10 license: "CC-BY-NC-4.0"
     11 ---
     12 
     13 # Integer Overflow (Web Applications)
     14 
     15 > 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:
     16 >
     17 > 
     18 {{#ref}}
     19 > ../../binary-exploitation/integer-overflow-and-underflow.md
     20 > {{#endref}}
     21 
     22 ---
     23 
     24 ## 1. Why integer math still matters on the web
     25 
     26 Even though much business logic in modern stacks is written in *memory-safe* languages, runtimes, native extensions, parsers, databases, and FFI boundaries still impose fixed-width integer representations. Whenever user-controlled numbers allocate buffers, compute offsets, or participate in length checks, **wraparound, truncation, or precision loss can transform an apparently harmless parameter into an out-of-bounds access, logic bypass, or denial of service**.
     27 
     28 Typical attack surface:
     29 
     30 1. **Numeric request parameters** – classic `id`, `offset`, or `count` fields.
     31 2. **Length / size headers** – `Content-Length`, WebSocket frame length, HTTP/2 `continuation_len`, etc.
     32 3. **File-format metadata parsed server-side or client-side** – image dimensions, chunk sizes, font tables.
     33 4. **Language-level conversions** – signed↔unsigned casts in PHP/Go/Rust FFI, JS `Number` → `int32` truncations inside V8.
     34 5. **Authentication & business logic** – coupon value, price, or balance calculations that silently overflow.
     35 
     36 ---
     37 
     38 ## 2. Recent real-world vulnerabilities (2023-2025)
     39 
     40 | Year | Component | Root cause | Impact |
     41 |------|-----------|-----------|--------|
     42 | 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.<sup>[[1]](#references)</sup> |
     43 | 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.<sup>[[2]](#references)</sup> |
     44 | 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.<sup>[[3]](#references)</sup> |
     45 
     46 Project Zero's 2025 **BLASTPASS** write-up is also useful operationally even though it revisits `CVE-2023-4863`: the vulnerable WebP decoder was reached through a **`.pkpass` ZIP wrapper / preview pipeline**, not just through a raw standalone image. When you review a web application, don't stop at `image/webp` uploads — enumerate **thumbnailers, preview handlers, archive-based import flows, OCR/media workers, mobile share targets, and message/notification renderers** that eventually invoke the same parser.<sup>[[4]](#references)</sup>
     47 
     48 ---
     49 
     50 ## 3. Testing strategy
     51 
     52 ### 3.1 Boundary-value cheat-sheet
     53 
     54 Send **extreme signed/unsigned values** wherever an integer is expected:
     55 
     56 ```text
     57 -1, 0, 1,
     58 127, 128, 255, 256,
     59 32767, 32768, 65535, 65536,
     60 2147483647, 2147483648, 4294967295,
     61 9223372036854775807, 9223372036854775808,
     62 0x7fffffff, 0x80000000, 0xffffffff
     63 ```
     64 
     65 Other useful formats:
     66 * Hex (`0x100`), octal (`0377`), scientific (`1e10`), JSON big-int (`9999999999999999999`).
     67 * Very long digit strings (>1kB) to hit custom parsers.
     68 
     69 ### 3.2 Burp Intruder template
     70 
     71 ```text
     72 §INTEGER§
     73 Payload type: Numbers
     74 From: -10 To: 4294967300 Step: 1
     75 Pad to length: 10, Enable hex prefix 0x
     76 ```
     77 
     78 ### 3.3 Fuzzing libraries & runtimes
     79 
     80 * **AFL++/Honggfuzz** with `libFuzzer` harness around the parser (e.g., WebP, PNG, protobuf).
     81 * **Fuzzilli** – grammar-aware fuzzing of JavaScript engines to hit V8/JSC integer truncations.
     82 * **boofuzz** – network-protocol fuzzing (WebSocket, HTTP/2) focusing on length fields.
     83 
     84 ### 3.4 JavaScript and browser coercion cases worth forcing
     85 
     86 Not every web integer bug is a native-style `size_t` wraparound. A lot of exploitable web logic starts with a **representation mismatch**:
     87 
     88 * JavaScript numbers are IEEE-754 doubles, so integers above `Number.MAX_SAFE_INTEGER` (`2^53 - 1`) lose precision.<sup>[[5]](#references)</sup>
     89 * Legacy code frequently uses bitwise operators such as `|0`, `~~x`, `x<<0`, or `x>>>0`, which **coerce values to 32-bit signed/unsigned integers**.
     90 * 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.
     91 
     92 Useful probes:
     93 
     94 ```javascript
     95 // Precision loss above 2^53-1
     96 JSON.parse('{"n":9007199254740993}').n
     97 
     98 // Signed wrap to negative
     99 (2147483648 | 0)        // -2147483648
    100 
    101 // Unsigned wrap to a huge positive
    102 (-1 >>> 0)              // 4294967295
    103 
    104 // Common "fast truncation" gadget in legacy code
    105 (4294967297 | 0)        // 1
    106 ```
    107 
    108 When a target mixes client-side validation with API-side validation, replay the same field as:
    109 
    110 * JSON number vs JSON string
    111 * decimal vs hex-like string (`4294967295` vs `0xffffffff`)
    112 * plain integer vs scientific notation (`10000000000` vs `1e10`)
    113 * positive vs negative boundary (`2147483647`, `2147483648`, `-1`, `4294967295`)
    114 
    115 Interesting symptoms:
    116 
    117 * Pagination or `limit` checks pass, but the query executes with `0`, `-1`, or a huge unsigned value.
    118 * Frontend blocks a value while the backend accepts it after a second parse.
    119 * A value displayed in the UI is not the value finally used by the API / renderer / WASM module.
    120 
    121 ### 3.5 Source-review patterns for JS/TS and hybrid apps
    122 
    123 When the target ships a large client bundle, do a quick **source-grep pass** before fuzzing random parameters:
    124 
    125 ```bash
    126 rg -n '(\|\s*0|>>>\s*0|~~[A-Za-z_(]|parseInt\(|Number\(|BigInt\(|Math\.imul\(|Buffer\.alloc\(|ArrayBuffer\(|DataView\(|Uint(8|16|32)Array\(|WebAssembly)' src/ public/ dist/
    127 ```
    128 
    129 Pay special attention to places where the code:
    130 
    131 * casts with `|0`, `~~x`, `x<<0`, or `x>>>0` **before** a bounds check,
    132 * multiplies attacker-controlled values before allocation/copy (`count * elemSize`, `width * height * bpp`, `offset + len`),
    133 * validates a number in JS and later forwards it to **WASM / native bindings** expecting `int32_t`, `uint32_t`, or `size_t`,
    134 * or parses the same field twice (`JSON.parse`, `Number`, `parseInt`, backend deserializer), creating a parser differential.
    135 
    136 ---
    137 
    138 ## 4. Exploitation patterns
    139 
    140 ### 4.1 Logic bypass in fixed-width server-side code
    141 
    142 An unchecked 32-bit signed multiplication in Java, C#, a native extension, or a database function can wrap and bypass a later comparison:
    143 
    144 ```java
    145 int price = Integer.parseInt(request.getParameter("price")); // cents
    146 int total = price * 100;                                     // unchecked wrap
    147 if (total > 1_000_000) throw new IllegalArgumentException("Too expensive");
    148 // price=21474850 produces -2147482296 after 32-bit wrap and passes the check
    149 ```
    150 
    151 Do not copy this model directly to modern PHP: PHP integers use the platform word size, and arithmetic that exceeds `PHP_INT_MAX` is converted to a float rather than silently wrapping as a signed integer. PHP applications can still become vulnerable when values lose precision as floats or are later truncated by a database, binary packing operation, native extension, or FFI boundary.<sup>[[6]](#references)</sup>
    152 
    153 The safe form checks the multiplication itself (for example, Java `Math.multiplyExact`) or validates `price <= intdiv(MAX_TOTAL, 100)` before multiplying.
    154 
    155 <!-- Historical unsafe PHP-style pseudocode retained for recognizing this anti-pattern in old write-ups:
    156 ```php
    157 $price = (int)$_POST['price'];          // expecting cents (0-10000)
    158 $total = $price * 100;                  // ← 32-bit overflow possible
    159 if($total > 1000000){
    160     die('Too expensive');
    161 }
    162 /* This does not silently wrap in current PHP; verify the actual runtime/FFI boundary. */
    163 ```
    164 -->
    165 
    166 ### 4.2 Heap overflow via image decoder (libwebp 0-day)
    167 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.
    168 
    169 ### 4.3 Browser renderer exploitation chain
    170 
    171 1. An **integer overflow** in a browser renderer or JavaScript engine produces memory corruption.
    172 2. A successful exploit turns that corruption into renderer-process code execution or another powerful primitive.
    173 3. A separate sandbox escape is normally required for operating-system compromise.
    174 
    175 This native RCE path is distinct from XSS. Integer truncation can also lead directly to DOM XSS when it bypasses application validation, as in the next section, but native renderer compromise does not need to “become stored XSS.”
    176 
    177 ### 4.4 Web logic bug → DOM XSS via integer truncation
    178 
    179 This pattern is much more common in pentests than full renderer RCE:
    180 
    181 ```javascript
    182 const raw = JSON.parse(location.hash.slice(1)).len;
    183 const len = raw | 0;                 // "fast" int cast to signed 32-bit
    184 
    185 if (len <= 64) {
    186   preview.innerHTML = userInput.slice(0, len);
    187 }
    188 ```
    189 
    190 If `raw=4294967295`, then `len` becomes `-1`. Depending on the surrounding code, this may:
    191 
    192 * bypass a max-length check,
    193 * make `slice(0, -1)` drop the last character and preserve the rest of the payload,
    194 * or desynchronize validation and the eventual sink (`innerHTML`, template renderer, markdown preview, etc.).
    195 
    196 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.
    197 
    198 ### 4.5 JS ↔ WASM / native boundary truncation
    199 
    200 Hybrid frontends often "normalize" an attacker-controlled number in JS and then pass it to native code:
    201 
    202 ```javascript
    203 const raw = Number(new URL(location).searchParams.get("n"));
    204 const len = raw >>> 0;                   // forced to uint32 in JS
    205 
    206 if (len <= 0x1000) {
    207   Module._copy(dstPtr, srcPtr, len);     // native side may later cast again
    208 }
    209 ```
    210 
    211 If the callee later treats the same value as signed, truncates it again, or multiplies it (`len * elem_size`) before allocation, inputs such as `-1`, `4294967295`, `4294967297`, or `9007199254740993` can desynchronize the **checked value**, **allocated size**, and **copied length**. In real targets this shows up in PDF viewers, image editors, collaborative whiteboards, and chat widgets compiled with Emscripten or backed by native browser components.
    212 
    213 ### 4.6 WASM note
    214 
    215 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:
    216 
    217 {{#ref}}
    218 wasm-linear-memory-template-overwrite-xss.md
    219 {{#endref}}
    220 
    221 ### 4.7 Wrapper-aware parser reachability
    222 
    223 The 2025 BLASTPASS analysis showed the vulnerable WebP path was reachable through a **PassKit `.pkpass` archive** containing a mislabeled WebP, not only through a direct image open.<sup>[[4]](#references)</sup> The offensive lesson generalizes well: once you find an integer bug in a decoder, test every wrapper that can silently reach the same parser:
    224 
    225 * archive/bundle imports (`.zip`, office docs, pass files, theme packs),
    226 * server-side thumbnail / resize / OCR pipelines,
    227 * browser features that decode images indirectly (`<picture>`, CSS images, favicons, notification icons, clipboard / drag-and-drop previews),
    228 * and mobile or desktop preview handlers that unpack attachments before rendering.
    229 
    230 ---
    231 
    232 ## 5. Defensive guidelines
    233 
    234 1. **Use checked arithmetic**, not merely wider types – for example Rust `checked_add`/`checked_mul`, Java `Math.multiplyExact`, compiler overflow builtins, or explicit precondition checks. In Go, `sum, carry := bits.Add64(x, y, 0)` exposes unsigned overflow through a nonzero `carry`, which the caller must reject rather than ignoring.<sup>[[7]](#references)</sup>
    235 2. **Validate ranges early**: reject any value outside business domain before arithmetic.
    236 3. **Enable relevant compiler sanitizers**: Clang's `-fsanitize=integer` or targeted signed/unsigned-overflow checks and UBSan during testing. Go's race detector finds data races, not integer overflow.
    237 4. **Adopt fuzzing in CI/CD** – combine coverage feedback with boundary corpora.
    238 5. **Stay patched** – browser integer overflow bugs are frequently weaponised within weeks.
    239 
    240 ---
    241 
    242 
    243 ## References
    244 
    245 - [1] [Cloudflare: Uncovering the Hidden WebP vulnerability (CVE-2023-4863)](https://blog.cloudflare.com/uncovering-the-hidden-webp-vulnerability-cve-2023-4863/)
    246 - [2] [NVD: CVE-2024-7025](https://nvd.nist.gov/vuln/detail/CVE-2024-7025)
    247 - [3] [NVD: CVE-2024-9123](https://nvd.nist.gov/vuln/detail/CVE-2024-9123)
    248 - [4] [Project Zero: Blasting Past WebP](https://projectzero.google/2025/03/blasting-past-webp.html)
    249 - [5] [HackerOne: Safely Handling Large Integers in JSON: Best Practices and Pitfalls](https://www.hackerone.com/blog/safely-handling-large-integers-json-best-practices-and-pitfalls)
    250 - [6] [PHP manual - Integer overflow and conversion to float](https://www.php.net/manual/en/language.types.integer.php#language.types.integer.overflow)
    251 - [7] [Go standard library - `math/bits.Add64`](https://pkg.go.dev/math/bits#Add64)