Oj gem CVE-2026-54899
MEDIUMSeverity by source
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:L/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
Attacker-supplied JSON reaches the bug remotely (AV:N) but only when the app reuses a parser and toggles symbol_keys (AC:H); no auth or UI needed; UAF most reliably crashes the worker (A:H) with possible limited memory disclosure/corruption (C:L/I:L).
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:L/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
Disabling symbol_keys on a reused Oj::Parser instance triggers a heap use-after-free. When symbol_keys is toggled from true to false, opt_symbol_keys_set frees the internal key cache (cache_free) but does not clear the pointer. The next parse call reads from the freed cache via cache_intern, producing a use-after-free.
Version
- Software: oj gem
- Affected: all versions with
ext/oj/usual.c - Latest tested: 3.17.1 (confirmed present)
Details
ext/oj/usual.c, opt_symbol_keys_set:
// usual.c:1043-1051
if (symbol_keys) {
d->key_cache = cache_create(...); // allocate
} else {
cache_free(d->key_cache); // free - but d->key_cache pointer not NULLed
}On the next parse call, cache_key → cache_intern reads from d->key_cache which now points to freed memory.
ASAN report:
==145265==ERROR: AddressSanitizer: heap-use-after-free on address 0x50b00001a318
READ of size 8 at 0x50b00001a318 thread T0
#0 cache_intern /ext/oj/cache.c:328
#1 cache_key /ext/oj/usual.c:161
#2 close_object /ext/oj/usual.c:285
#3 parse /ext/oj/parser.c:693
#4 parser_parse /ext/oj/parser.c:1408
freed by thread T0 here:
#0 free
#1 cache_free /ext/oj/cache.c:277
#2 opt_symbol_keys_set /ext/oj/usual.c:1051
#3 option /ext/oj/usual.c:1111
#4 parser_missing /ext/oj/parser.c:1362
0x50b00001a318 is 40 bytes inside freed 112-byte region [fd]fd fd fd fd fd fd fdReproduce
require 'oj'
p = Oj::Parser.new(:usual, symbol_keys: true)
p.symbol_keys = false
# frees cache without nulling pointer
p.parse('{"attacker":1}')
# UAF: reads freed cacheAnalysisAI
Heap use-after-free in the Oj Ruby JSON parser (versions prior to 3.17.3) is triggered when an application toggles the symbol_keys option from true to false on a reused Oj::Parser instance. The opt_symbol_keys_set function frees the internal key cache via cache_free but fails to NULL the d->key_cache pointer, so the next parse call dereferences freed memory through cache_intern, potentially leading to memory disclosure, crashes, or controlled corruption. …
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 | Exploitation requires that the target application reuse a single Oj::Parser instance built with the :usual mode and symbol_keys: true, then toggle that instance's symbol_keys option to false at runtime (via the symbol_keys= setter or the equivalent option call) before invoking parse on attacker-influenced JSON. … Additional conditions and limiting factors are described in the full assessment. |
| Risk Assessment | No NVD CVSS vector is supplied, and EPSS, KEV, and SSVC data are absent from the input, so prioritization must rely on the technical facts. … Full risk analysis with EPSS, KEV, and SSVC signal comparison available after sign-in. |
| Exploit Scenario | An application instantiates a long-lived Oj::Parser(:usual, symbol_keys: true) - for example as a memoized parser in a web request pipeline - and at some point sets parser.symbol_keys = false based on a configuration flag or per-request hint before parsing user-supplied JSON; the next parse call dereferences the freed key cache, at minimum crashing the worker (DoS) and potentially leaking adjacent heap data into returned key values. No public PoC beyond the reporter's three-line reproducer has been identified, and weaponization into reliable code execution would additionally require heap-grooming primitives specific to the host Ruby/glibc allocator. |
| Remediation | Vendor-released patch: oj 3.17.3 - upgrade by pinning gem 'oj', '>= 3.17.3' in the Gemfile and running bundle update oj, then redeploy so the C extension is recompiled against the fixed source (see https://github.com/ohler55/oj/security/advisories/GHSA-2cw7-v8ff-p88r). … Detailed patch versions, workarounds, and compensating controls in full report. |
Recommended ActionAI
Within 24 hours: Inventory all Ruby applications using the Oj gem (search Gemfile and Gemfile.lock) and audit code for symbol_keys configuration toggles on reused Oj::Parser instances. …
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-2cw7-v8ff-p88r