GitLab RCE · Found autonomously by depthfirst through the Open Defense Initiative
GitLab Oj Spill
Two memory corruption vulnerabilities in Oj, a fast native C JSON parser used by many Ruby applications, are chained to achieve remote code execution on a default GitLab installation. Reachable from normal authenticated users who can commit changes.
TL;DR
As part of the Open Defense Initiative, the depthfirst system analyzed Oj, a high-performance JSON parser with a substantial native C implementation, and produced a prioritized queue of potentially exploitable findings. The system surfaced 18 prioritized vulnerabilities, including 7 memory-safety bugs. Two of them, an out-of-bounds write and a heap-pointer disclosure, had survived in Oj for nearly five years. Oj is a low-level dependency used by GitLab, and we combined the two bugs to achieve remote code execution on a default GitLab installation.
Any authenticated user who can commit changes can trigger the vulnerability and execute commands. GitLab reaches the vulnerable code through its in-tree ipynbdiff gem, which renders diffs of Jupyter .ipynb notebooks. A crafted notebook, viewed on its commit diff page, feeds attacker-controlled bytes straight into Oj’s C parser inside a Puma worker.
Why it matters:
- Exploitable by any normal authenticated user who can commit changes.
- A crafted
.ipynbnotebook drives deterministic heap corruption in native code that can become remote code execution. - Commands execute as
git, exposing source code, Rails secrets, credentials, CI/CD data, token-signing material, and internal services reachable from the GitLab application.
What is affected
A GitLab installation is affected when its notebook transformer calls
Oj::Parser.usual.parse and it bundles Oj 3.13.0 through 3.17.1.
| Component | Affected versions | First fixed release |
|---|---|---|
| GitLab CE/EE | 15.2.0 through 18.10.7 | 18.10.8 |
| GitLab CE/EE | 18.11.0 through 18.11.4 | 18.11.5 |
| GitLab CE/EE | 19.0.0 through 19.0.1 | 19.0.2 |
| Oj gem | 3.13.0 through 3.17.1 | 3.17.3 |
All GitLab tiers are affected, including CE and EE across Free, Premium, and Ultimate. GitLab.com was patched by the June 10 release. Ruby itself is not affected; the vulnerabilities are in Oj’s native C extension.
Remediation
Upgrade self-managed GitLab to a current supported patch release. The fixes first shipped in GitLab 18.10.8, 18.11.5, and 19.0.2, all of which bundle Oj 3.17.3.
If you manage Oj directly, upgrade to Oj 3.17.3 or later. Version 3.17.3 was the first published release containing both fixes.
For Helm and Operator deployments, check the GitLab application version in the Webservice image rather than the chart or Operator version.
Use depthfirst Supply Chain to identify affected Oj versions across direct and transitive dependencies.
Technical details
Both bugs live in Oj’s native C parser, and the depthfirst system surfaced both autonomously during an automated review of Oj’s source. Human analysis then chained them into a working exploit.
The first is an out-of-bounds write. The parser tracks array and object nesting in a fixed 1,024-byte stack inside its parser struct and increments the depth with no bounds check. A deeply nested notebook sweeps one-byte selectors forward through the struct, and because each written byte reads back as a valid selector, the sweep sustains itself until it rewrites an interior heap pointer.
The second is a heap-pointer disclosure. An overlong JSON key narrows through a signed 16-bit length field, so the read returns inline bytes that overlap a live external key pointer. GitLab echoes that pointer back through the rendered notebook diff, which leaks a heap address and defeats ASLR.
Chained together, the leak locates libc and libruby, and the write redirects the parser’s start callback into system(), executing an attacker-controlled command as git.
In addition to the two bugs used in this chain, depthfirst’s broader Oj review produced nine additional CVE advisories: CVE-2026-54502, CVE-2026-54896, CVE-2026-54897, CVE-2026-54898, CVE-2026-54899, CVE-2026-54900, CVE-2026-54901, CVE-2026-54902, and CVE-2026-54903.
Credits
These vulnerabilities were discovered autonomously by the depthfirst platform as part of the Open Defense Initiative. We thank the Oj maintainer and the GitLab security team for their responsiveness throughout coordinated disclosure.
Disclosure timeline:
- May 21, 2026 — Bugs reported to Oj.
- May 27, 2026 — Oj merged the nesting-depth and overlong-key fixes.
- June 4, 2026 — Oj 3.17.3 published.
- June 5, 2026 — Reachable GitLab notebook-diff chain reported privately to GitLab.
- June 8, 2026 — GitLab confirmed the vulnerability.
- June 10, 2026 — GitLab released 19.0.2, 18.11.5, and 18.10.8 with Oj 3.17.3.
- July 17, 2026 — GitLab resolved the report.
References:
Open Defense Initiative
$5 million in platform credits to help maintainers of critical open source projects find and fix vulnerabilities before they’re exploited.
-
How do I know if I am affected?
Check your GitLab version against the table above and confirm the bundled Oj version is between 3.13.0 and 3.17.1. You are in scope if the GitLab tree contains the
Oj::Parser.usual.parsenotebook transformer call and bundles an affected Oj release. For Helm and Operator installs, judge the actual Webservice/Puma image’s GitLab version, not the chart number. -
Is exploitation in the wild?
depthfirst is not aware of in-the-wild exploitation. This was a coordinated private disclosure through the Open Defense Initiative, and GitLab independently reproduced the RCE.
-
Does this affect GitLab.com?
The vulnerable notebook-diff code path also existed on GitLab.com. GitLab had patched GitLab.com by the June 10 release, so GitLab.com users do not need to take action. Operators of affected self-managed installations must upgrade to a fixed release.
-
Is Ruby itself vulnerable?
No. The bugs are in Oj, a native C extension that GitLab depends on, not in Ruby core.
-
Where can I find the technical writeup?
Our full root-cause analysis, proof of concept, and patch walkthrough are in the technical research post.