Oj gem CVE-2026-54902
MEDIUMSeverity by source
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
Remote attacker-controlled JSON reaches a SAJ handler over the network without auth (AV:N/PR:N/UI:N); exploitation needs a long key plus GC inside the callback, so AC:H; demonstrated impact is process crash (A:H), no confirmed C/I.
Primary rating from Vendor (https://github.com/ohler55/oj).
CVSS VectorVendor: https://github.com/ohler55/oj
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
Lifecycle Timeline
3DescriptionCVE.org
Summary
Oj::Parser in SAJ mode does not protect cached object keys (≥ 35 bytes) from garbage collection. A Ruby callback that triggers GC inside hash_end can cause the key string to be reclaimed while the C parser still holds a pointer to it. The subsequent access to the freed string VALUE results in a segfault, confirmed by an RIP pointing to address 0x4242 (a canary-style pattern suggesting control over the freed memory's content).
Version
- Software: oj gem
- Affected: all versions with
ext/oj/saj2.c/ext/oj/parser.c - Latest tested: 3.17.1 (confirmed present)
Details
Short keys (≤ 34 bytes) are stored inline on the C stack and are safe. Long keys (≥ 35 bytes) are stored as heap-allocated Ruby String objects passed to rb_funcall as the key argument. Between the key being resolved and the callback completing, a GC triggered inside the callback (e.g. GC.start) can collect the key String, leaving a dangling VALUE.
Crash output:
long_key_trigger
[BUG] Segmentation fault at 0x0000000000004242
close_object+0x260 /ext/oj/usual.c:405 (calls rb_funcall with freed key)
parse+0x11ff /ext/oj/parser.c:693
parser_parse+0x145 /ext/oj/parser.c:1408
RIP: 0x7fd1b46d68b7 RDI: 0x0000000000004242 (freed key VALUE)
R12: 0x0000000000004242The freed VALUE 0x4242 shows the attacker-controlled content of the key string was loaded as a pointer - a classic use-after-free indicator.
Reproduce
require 'oj'
class H < Oj::Saj
def add_value(value, key)
GC.start(full_mark: true, immediate_sweep: true) if key == 'x'
end
def hash_start(key); end
def hash_end(key); end
end
p = Oj::Parser.new(:saj)
p.handler = H.new
p.parse('{"' + 'A' * 35 + '":{"x":1}}')
# long outer key, GC fires on inner keyAnalysisAI
Use-after-free in the Oj Ruby JSON gem's SAJ parser allows an attacker who can influence parsed JSON content and the SAJ callback handler to crash the Ruby process and potentially corrupt memory. Oj::Parser fails to protect heap-allocated cached object keys of 35 bytes or more from garbage collection, so a GC cycle triggered from inside a hash_end callback frees the key while C code still holds a dangling VALUE pointer. …
Unlock full vulnerability intelligence
- Risk assessment & exploitation conditions
- Attack chain visualization
- Remediation with exact patch versions
- Threat intelligence from 22 sources
- Personal watchlist & email alerts
Free forever · No credit card required
Attack ChainAIDerived
Hypothetical attack flow derived from CVE metadata
Vulnerability AssessmentAI
| Exploitation | The target application must (1) use Oj::Parser.new(:saj) - not Oj.load DOM mode and not the legacy Oj::Saj API - with a custom handler, (2) parse attacker-controlled JSON, (3) include an object key of 35 bytes or more so the key is heap-allocated rather than stack-inlined, and (4) execute a SAJ callback (notably hash_end, or add_value as in the PoC) that triggers a Ruby garbage collection cycle, either explicitly via GC.start or implicitly via allocations during the callback. … Additional conditions and limiting factors are described in the full assessment. |
| Risk Assessment | No CVSS vector is supplied by NVD or the vendor, so all severity inference is qualitative. … Full risk analysis with EPSS, KEV, and SSVC signal comparison available after sign-in. |
| Exploit Scenario | An attacker submits a JSON document with an outer object whose key is at least 35 bytes (e.g. 35 'A' characters) and a nested value that causes the application's SAJ callback to allocate enough to trigger a GC cycle inside hash_end; the parser then dereferences the freed key VALUE, segfaulting the Ruby worker. … |
| Remediation | Vendor-released patch: upgrade the oj gem to 3.17.3 or later (bundle update oj and pin '~> 3.17.3' in the Gemfile), which is the fixed version recorded in the GHSA advisory at https://github.com/ohler55/oj/security/advisories/GHSA-m578-w5vf-rfcm. … Detailed patch versions, workarounds, and compensating controls in full report. |
Recommended ActionAI
24 hours: Identify all applications and dependencies using Oj gem; retrieve GHSA advisory and published reproducer for technical assessment; enable application monitoring for unexpected process exits. …
Sign in for detailed remediation steps and compensating controls.
Threat intelligence, references, and detailed analysis are available after sign-in.
Same weakness CWE-416 – Use After Free
View allSame technique Use After Free
View allShare
External POC / Exploit Code
Leaving vuln.today
GHSA-m578-w5vf-rfcm