# Memory disclosure in mruby String#lines method

mruby String#lines caches a raw pointer to the string buffer; a block that mutates the string leaves a dangling pointer, leaking heap contents including mrb_value pointers.

- Payout: hidden
- Severity: high
- Program: Shopify
- Found by: isra17
- Disclosed: 2016-12-16
- Write-up quality: 8/10
- Original: https://hackerone.com/reports/181319
- Library page: https://bugbountylibrary.com/r/h1%3A181319
- Tags: area:oss domain:oss impact:data-leak kind:bounty platform:hackerone sev:high tech:ruby technique:chained technique:source-review vuln:info-disclosure vuln:memory-corruption

**Target:** mruby interpreter String#lines (mruby-string-ext)
**Root cause:** String#lines caches `p = RSTRING_PTR(self)` and `e = p + RSTRING_LEN(self)` before iterating, but yields to a caller block each line; if the block clears/reallocates the string, `p` becomes dangling and subsequent iterations read freed/reused heap memory.
**Steps:**
1. Allocate a string with many newline-separated lines, e.g. `$a = ("a"*0xf + "\n") * 1000`.
2. Call `$a.lines do |l| ... end` and inside the block call `$a.clear` then allocate objects like `foo = "UUUUUUUU" * 1000`.
3. Collect yielded lines into `@a`; the later lines contain the newly allocated data, including `mrb_value` pointers.
**How found:** Reviewing mruby source for vulnerabilities; noticed the cached `p`/`e` pointers in String#lines while the loop yields to an attacker-controlled block that can mutate `self`.
**Exploitation:** Attacker with arbitrary Ruby execution in mruby (e.g. Shopify's mruby-engine) triggers the dangling pointer to reliably disclose heap contents and object addresses, then chains with a second bug to build a full RCE.
**Escalation:** Memory disclosure used to locate heap object addresses, combined with another reported vulnerability to achieve RCE.
**Insight:** When a C-level iterator caches a raw buffer pointer but yields to a callback per element, check whether the callback can free/reallocate the underlying buffer — that turns the cached pointer into a dangling read.

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

**Ruby PoC leaking freed string via String#lines**

```
@a = []
$a = ("a"*0xf + "\n") * 1000
$a.lines do |l|
  $a.clear
  foo = "UUUUUUUU" * 1000
  @a << l
end
Look at `@a` to get the "UUUU..." `mrb_value` object and strings.
```

