Oj gem CVE-2026-54900
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
Reachable over the network through any JSON endpoint using the affected parser config, no auth or interaction, trivial trigger; confirmed impact is process crash (A:H), C/I impact not demonstrated.
Primary rating from Vendor (https://github.com/ohler55/oj).
CVSS VectorVendor: https://github.com/ohler55/oj
Lifecycle Timeline
3DescriptionCVE.org
Summary
Oj::Parser#parse in usual mode with create_id enabled is vulnerable to heap corruption via a negative-size memcpy. When a JSON object key is exactly 65,535 bytes long, an integer truncation in form_attr (usual.c:63) converts the length to -1 before passing it to memcpy. This causes memcpy to copy SIZE_MAX bytes (interpreted as a huge size_t), corrupting heap memory and crashing the process.
Version
- Software: oj gem
- Affected: all versions with
ext/oj/usual.c - Latest tested: 3.17.1 (confirmed present)
Details
ext/oj/usual.c, form_attr:
// usual.c:55-64
static ID form_attr(const char *str, size_t slen) {
char buf[4096];
// ...
int blen = (int)slen + 1; // ← truncates: 65535 + 1 = 65536 → wraps to 0
// or: 65535 cast to int = 65535 (fits),
// but blen = 65536 → INT overflow on +1 if slen=INT_MAX
// ...
memcpy(buf, "@", 1);
memcpy(buf + 1, str, (size_t)blen); // ← size_t(-1) = SIZE_MAX
}The cache (cache_intern) uses a fixed 65,536-byte slab. When slen = 65535, the arithmetic wraps and memcpy is called with (size_t)-1.
ASAN report:
==80452==ERROR: AddressSanitizer: negative-size-param: (size=-1)
#0 memcpy
#1 form_attr /ext/oj/usual.c:63
#2 cache_intern /ext/oj/cache.c:326
#3 get_attr_id /ext/oj/usual.c:186
#4 close_object_create /ext/oj/usual.c:374
#5 parse /ext/oj/parser.c:693
#6 parser_parse /ext/oj/parser.c:1408
0x531000528800 is located 0 bytes inside of 65536-byte region [0x531000528800, 0x531000538800)Reproduce
Generate the payload:
key = 'A' * 65535
with open('poc.json', 'w') as f:
f.write('{"json_class":"Oj::Bag","' + key + '":1}')Trigger:
require 'oj'
Oj::Parser.new(:usual, create_id: 'json_class').parse(STDIN.read)AnalysisAI
Heap corruption in the Oj Ruby JSON parser allows remote attackers to crash or potentially corrupt memory in applications that parse untrusted JSON with Oj::Parser in :usual mode when the create_id option is enabled. A 65,535-byte object key triggers an integer truncation in form_attr (ext/oj/usual.c:63) that turns the buffer length into (size_t)-1, causing memcpy to write SIZE_MAX bytes onto a fixed 65,536-byte cache slab. No public exploit identified at time of analysis beyond the maintainer-supplied reproduction script in GHSA-9cv6-qcjw-4grx.
Technical ContextAI
Oj is a widely used high-performance C-extension JSON parser for Ruby that, in :usual mode, supports a create_id directive (commonly json_class) to instantiate Ruby objects from JSON. The vulnerable function form_attr prefixes an @ to a key to build a Ruby instance-variable identifier, but computes the buffer length as int blen = (int)slen + 1. When slen equals 65,535 the +1 overflows the signed int to a negative value, which is then implicitly cast to size_t, producing SIZE_MAX. The cache (cache_intern at cache.c:326) operates on 65,536-byte slabs, so the over-long memcpy runs straight off the slab into adjacent heap, classic CWE-680 / CWE-190 territory (the CVE entry tags it CWE-416 Use-After-Free, which appears to be a mis-classification - the ASAN trace is negative-size-param, not UAF).
RemediationAI
Upgrade to oj 3.17.3 or later (vendor-released patch: 3.17.3) - update your Gemfile to gem 'oj', '>= 3.17.3' and run bundle update oj, then redeploy. Until you can upgrade, the most reliable workaround is to stop using Oj::Parser in :usual mode with create_id and instead use Oj.load or Oj.safe_load (side effect: applications relying on json_class-based object reconstitution will need to handle hashes manually). A second compensating control is to enforce a strict maximum JSON-key length at an upstream layer (reverse proxy, Rack middleware, schema validator) - anything well under 65,535 bytes blocks the trigger, with the trade-off that legitimate large keys are rejected. See the GitHub advisory at https://github.com/ohler55/oj/security/advisories/GHSA-9cv6-qcjw-4grx for the upstream fix commit.
Wazuh SIEM platform versions 4.4.0 through 4.9.0 contain an unsafe deserialization vulnerability in the DistributedAPI t
BentoML version 1.4.2 and earlier contains an unauthenticated remote code execution vulnerability through insecure deser
pgAdmin 4 contains critical remote code execution vulnerabilities in the Query Tool download and Cloud Deployment endpoi
The renderLocalView function in render/views.py in graphite-web in Graphite 0.9.5 through 0.9.10 uses the pickle Python
BentoML is a Python library for building online serving systems optimized for AI apps and model inference. Rated critica
OpenSSL before 0.9.8za, 1.0.0 before 1.0.0m, and 1.0.1 before 1.0.1h does not properly restrict processing of ChangeCiph
pyLoad download manager version prior to 0.5.0b3.dev77 exposes the Flask SECRET_KEY through an unauthenticated endpoint.
Langflow (a visual LLM pipeline builder) contains a critical unauthenticated code execution vulnerability (CVE-2026-3301
In Mercurial before 4.1.3, "hg serve --stdio" allows remote authenticated users to launch the Python debugger, and conse
Unauthenticated remote code execution affects Kestra OSS (the open-source event-driven orchestration platform) prior to
Unauthenticated remote code execution in Marimo ≤0.20.4 allows attackers to execute arbitrary system commands via the `/
pyLoad is the free and open-source Download Manager written in pure Python. Rated medium severity (CVSS 5.3), this vulne
Same weakness CWE-416 – Use After Free
View allSame technique Denial Of Service
View allVendor StatusVendor
Share
External POC / Exploit Code
Leaving vuln.today
GHSA-9cv6-qcjw-4grx