# controlled buffer under-read in pack_unpack_internal()

Signedness bug in Ruby's pack_unpack_internal() lets a negative '@' offset under-read memory before the string buffer, leaking heap contents via attacker-controlled unpack format.

- Payout: hidden
- Severity: medium
- Program: Ruby
- Found by: aerodudrizzt
- Disclosed: 2018-03-30
- CVE: CVE-2018-8778
- Write-up quality: 7/10
- Original: https://hackerone.com/reports/298246
- Library page: https://bugbountylibrary.com/r/h1%3A298246
- Tags: area:oss domain:oss impact:data-leak kind:bounty platform:hackerone sev:medium tech:ruby technique:source-review vuln:info-disclosure vuln:memory-corruption

**Target:** Ruby core pack.c unpack() format parser
**Root cause:** len is a signed long but parsed with STRTOUL, so a decimal value like 4294967196 wraps to a negative long; the '@' case only checks len > RSTRING_LEN(str), letting negative len move the pointer before the buffer start.
**Steps:**
1. Call `"0123456789".unpack("@4294967196C110")` on 32-bit Ruby (adjust value for 64-bit).
2. STRTOUL parses 4294967196 into signed long len, wrapping negative.
3. '@' check `len > RSTRING_LEN(str)` passes since negative; `s = RSTRING_PTR(str) + len` points before buffer.
4. Subsequent 'C110' reads 110 bytes of memory preceding the string, returning leaked heap data.
**How found:** Noticed len is declared signed long but populated via STRTOUL (unsigned parse), hypothesized integer wrap to negative, then traced the '@' bounds check which only rejects len greater than string length, not negatives.
**Exploitation:** Attacker who controls the unpack format string supplies a large decimal '@' offset that wraps to negative, then reads arbitrary counts of preceding heap memory, disclosing pointers and heap contents in the returned array.
**Insight:** When a length is parsed with an unsigned function into a signed variable, audit every downstream bounds check for one-sided comparisons (len > max) that miss negative values; '@'-style seek/offset operators in format parsers are prime spots for pointer rewinds.

