Listen to this Post
How CVE-2026-54898 Works
The vulnerability resides in the `Oj::Parserparse` method of the `oj` Ruby gem, specifically within the C extension’s SAJ (Streaming API for JSON) and SAJ2 callback handling.
When `Oj::Parserparse` is invoked, the C function `parser_parse` obtains a raw `const byte ` pointer to the Ruby string’s internal buffer via StringValuePtr(json). This pointer is then passed to the internal `parse` function and used throughout the parsing loop to read characters sequentially.
The critical flaw occurs because the C parser retains this raw pointer while allowing Ruby callbacks—such as hash_start, array_start, or add_value—to execute during parsing. If a callback mutates the input JSON string—for example, by calling `Stringreplace` with a longer value, or using `<<` or gsub!—Ruby may reallocate the string’s internal buffer to accommodate the new content.
Ruby’s string implementation will free the old buffer and allocate a new one if the new content exceeds the embedded capacity. However, the C parser’s `const byte ` pointer still points to the now-freed memory region, creating a dangling pointer.
The use-after-free is triggered when the parser continues its loop and attempts to read the next character from the freed buffer at parser.c:607. This can lead to reading arbitrary freed memory, potentially causing crashes, data corruption, or information disclosure.
The AddressSanitizer report confirms the issue:
==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: [bash]fd fd fd fd fd ... (entire region freed)
All versions of the `oj` gem containing `ext/oj/parser.c` are affected; version 3.17.1 has been confirmed vulnerable.
DailyCVE Form
Platform: ……. Ruby (oj gem)
Version: …….. 3.17.1 and earlier
Vulnerability :…… Heap Use-After-Free (CWE-416)
Severity: ……. High
date: ………. 2026-06-16 (published)
Prediction: here you should include expected Patch date. 2026-06-30 (estimated)
Analytics under heading What Undercode Say
The following bash commands and Ruby scripts can be used to reproduce and analyze the vulnerability:
Reproduction Script (`reproduce.rb`):
require 'oj'
class Mutator
def initialize(json)
@json = json
@done = false
end
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)
Run with AddressSanitizer:
Compile Ruby with ASAN support (if not already) Then run the script with ASAN enabled RUBYOPT="-r" ASAN_OPTIONS=detect_leaks=0 ruby reproduce.rb
Check for vulnerable versions:
gem list oj or bundle list | grep oj
Monitor for crashes:
Run with ASAN and log output ASAN_OPTIONS=log_path=asan.log ruby reproduce.rb cat asan.log.
Exploit
An attacker can exploit this vulnerability by crafting a JSON payload and a malicious SAJ callback handler that mutates the input string during parsing. The key steps are:
1. Provide a JSON string that triggers the SAJ callback (e.g., a hash with a key that invokes hash_start).
2. Implement a callback (e.g., hash_start) that replaces the input string with a larger value, forcing Ruby to reallocate the buffer.
3. Trigger the use-after-free by allowing the C parser to continue reading from the freed memory.
The proof-of-concept above demonstrates this. The `hash_start` callback replaces the JSON string with a 1,000,000-character string, causing the original buffer to be freed. The parser then reads from the freed memory, leading to undefined behavior.
This vulnerability could be leveraged to cause denial of service (crashes), read sensitive memory contents, or potentially achieve arbitrary code execution depending on memory layout and exploitation techniques.
Protection
- Upgrade the `oj` gem to a patched version once available. Monitor the official repository for security fixes.
- Avoid mutating the input JSON string inside SAJ/SAJ2 callbacks. If mutation is necessary, consider copying the string first or using a different parsing approach.
- Use `Oj.default_options = { mode: :strict }` as a temporary mitigation to reduce attack surface, though this does not directly fix the UAF.
- Run with AddressSanitizer in development and testing environments to detect similar issues early.
- Consider using an alternative JSON parser (e.g., `json` gem) until a patch is available, especially in security-sensitive applications.
- Review custom SAJ handlers to ensure they do not modify the input string during parsing.
Impact
- Confidentiality: An attacker may read freed memory contents, potentially exposing sensitive data such as API keys, tokens, or other secrets stored in the same memory region.
- Integrity: Memory corruption could lead to data corruption or unexpected behavior in the application.
- Availability: The vulnerability can cause segmentation faults or crashes, leading to denial of service.
- Scope: All applications using the `oj` gem with `Oj::Parser` in SAJ or SAJ2 mode are affected. The vulnerability is present in all versions containing
ext/oj/parser.c. - Exploitability: The attack requires the ability to control the JSON input and the callback behavior, which may be possible in many web applications that parse user-supplied JSON with custom handlers.
🎯Let’s Practice Exploiting & Learn Patching For Free:
🎓 Live Courses & Certifications:
Join Undercode Academy for Verified Certifications
🚀 Request a Custom Project:
Secure, high-velocity infrastructure and disruptive technological engineering. Contact our engineering team for high-tier development and proprietary systems:
[email protected]
💎 Smart Architecture | 🛡️ Secure by Design | ⭐ Trusted by Thousands
Sources:
Reported By: github.com
Extra Source Hub:
Undercode

