# Buffer overflow in mrb_time_asctime

Out-of-range Time values in mruby's mrb_time_asctime pass unvalidated tm_mon/tm_wday into snprintf, causing out-of-bounds reads and segfaults in Shopify's sandbox.

- Payout: $10,000
- Severity: high
- Program: shopify-scripts
- Credited: haquaman
- Disclosed: 2016-12-18
- Write-up quality: 7/10
- Original: https://hackerone.com/reports/188326
- Library page: https://bugbountylibrary.com/r/h1%3A188326
- Tags: domain:oss impact:dos kind:bounty platform:hackerone sev:high tech:ruby technique:source-review vuln:dos vuln:memory-corruption

**Target:** mruby-time gem (mrb_time_asctime) in mruby / mruby-engine sandbox
**Root cause:** time_alloc calls mrb_time_update_datetime without checking its return value; when the time is out of range, tm fields (tm_mon, tm_wday) are left uninitialized/garbage and later used as array indices into wday_names/mon_names and passed to snprintf.
**Steps:**
1. Create crash.rb containing `Time.new-0XD00000000000000&0` (or `Time.at(sec,usec)` with extreme values)
2. Trigger to_s / mrb_time_asctime, e.g. via a NoMethodError message that stringifies the Time
3. Run `./dev/bin/mruby crash.rb` under ASAN or lldb
4. Observe strlen crash in __vfprintf via mrb_time_asctime with garbage tm_mon=6484120, tm_wday=0
**How found:** Researcher noticed Time.new with malformed numeric literal produced inconsistent/corrupted date strings across runs, then used lldb to inspect the mrb_time struct and found tm_mon held garbage (6484120) while tm_wday indexed wday_names, tracing to unchecked mrb_time_update_datetime return.
**Exploitation:** Attacker supplies an out-of-range Time value to a sandboxed mruby script; when the value is stringified (e.g. in an exception message), mrb_time_asctime indexes name arrays with garbage tm_mon and formats via snprintf, reading out of bounds and crashing the host process (DoS).
**Insight:** When a C date/time formatter indexes name arrays by tm_wday/tm_mon, feed out-of-range epoch values and check whether the update function's failure return is ignored, leaving struct fields uninitialized.
