# imgproxy has integer overflow in imgproxy ICO loader that causes denial of service and may permit remote code execution

imgproxy ICO loader wraps a 32-bit data_size+14 addition, allocating 3 bytes then writing 64 attacker bytes past the heap buffer, enabling DoS or RCE.

- Payout: Research
- Severity: critical
- Program: imgproxy
- Found by: mateofumis, JasonPuglisi, JacobBrackett
- Disclosed: 2026-10-30
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-hff9-5pqw-6w9f
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-hff9-5pqw-6w9f
- Tags: area:oss domain:oss impact:dos impact:rce kind:research platform:ghsa sev:critical tech:go technique:source-review vuln:dos vuln:memory-corruption

**Target:** ICO image loader in imgproxy (vips/icoload.c)
**Root cause:** Attacker-controlled 32-bit data_size is added to BMP_FILE_HEADER_LEN without overflow check; for data_size > UINT32_MAX-14 the sum wraps to a tiny allocation while the original size is still used for the read.
**Steps:**
1. Craft ICO file starting with magic `00 00 01 00` and a directory entry with `data_size = 0xfffffff5` and `data_offset = 22`.
2. imgproxy detects ICO and dispatches to `vips_icoload_source_go`.
3. `vips_foreign_load_ico_header` reads data_size and computes `data_size + BMP_FILE_HEADER_LEN` = 3, so `VIPS_MALLOC` allocates 3 bytes.
4. `actual_data = data + 14` points past the allocation; `vips_foreign_load_read_full` is called with the original data_size.
5. The Go source callback copies available source bytes (64 in the fixture) to the invalid pointer, corrupting the heap.
**How found:** Code review of the ICO loader's allocation arithmetic spotted the unchecked 32-bit addition of attacker-controlled data_size to a constant, then a crafted fixture confirmed the wrap and out-of-bounds write.
**Exploitation:** A network client submits a crafted ICO image to imgproxy; the wrapped allocation causes the source callback to write attacker bytes past the 3-byte heap buffer, crashing the process or corrupting heap metadata for potential RCE.
**Insight:** In image/parser loaders, audit size arithmetic where a 32-bit field is added to a header constant before malloc but the original field is reused for the read; the wrap window is the top 14 values of uint32.

## Proof of concept (copied exactly from the original report)

**Crafted directory entry values causing wraparound**

```
data_size       = 0xfffffff5 (4294967285)
data_offset     = 22
BMP header size = 14
32-bit result   = (0xfffffff5 + 14) mod 2^32 = 3
```

