/00 — boot sequence

Hello.

Article

GitLab RCE PoC Published: Authenticated Users Can Run Commands as Git via Jupyter Notebook Diff

July 27, 2026•6 min read
security gitlab rce vulnerability oj-gem ruby

Security researchers at depthfirst published working exploit code on July 24 for a GitLab remote code execution chain that GitLab patched six weeks earlier—on June 10—but did not classify as a security fix. Any authenticated user who can push to a project can achieve RCE as the git user on unpatched self-managed instances.

Vulnerability Details

The exploit chains two memory-corruption bugs in Oj, a high-performance JSON parser implemented as a native C extension for Ruby. Oj is a low-level dependency used by GitLab's ipynbdiff gem to render Jupyter Notebook diffs. The vulnerable parser, Oj::Parser.usual.parse, processes attacker-controlled .ipynb JSON content directly inside long-lived Puma worker processes.

Affected versions:

ComponentAffectedFirst Fixed
GitLab CE/EE15.2.0 – 18.10.718.10.8
GitLab CE/EE18.11.0 – 18.11.418.11.5
GitLab CE/EE19.0.0 – 19.0.119.0.2
Oj gem3.13.0 – 3.17.13.17.3

All tiers (Free through Ultimate) and both Community and Enterprise editions are affected. Ruby itself is not vulnerable.

Attack requirements:

  • Authenticated user with push access to any project
  • Self-managed GitLab instance (GitLab.com already patched)
  • No administrator rights, no CI/runner access, no victim interaction, no access to other projects needed

Impact: Commands execute as the git system user behind Puma. In reach: source code, Rails secrets, service credentials, CI/CD data, and internal services the application can reach.

Impact Assessment

The exploit is not a one-liner. It requires precise heap manipulation: a nesting-stack overflow that writes a controlled byte pattern, an allocator-assisted pointer forge via realloc, and a Ruby Array overlap that places a chosen callback address into the parser's p->start field. The infoleak primitive—a 65,565-byte object key that truncates to 29 in a signed 16-bit field—leaks a live heap pointer through the rendered diff HTML, defeating ASLR within the forked Puma worker tree.

Depthfirst's writeup demonstrates the chain against GitLab 18.11.3 on x86-64. Porting to another build on the same architecture generally needs only updated gadget offsets; the Ruby build, libc, jemalloc, and compilation environment matter more than the GitLab version. ARM64 would require a full rework due to different calling conventions and heap behavior.

No in-the-wild exploitation has been reported. GitLab reproduced the RCE independently. Depthfirst is not aware of active exploitation.

Affected Systems

Any self-managed GitLab installation running a version listed above that has not been upgraded to 18.10.8, 18.11.5, or 19.0.2 (or later). GitLab.com and GitLab Dedicated are already protected.

Critical gap: Versions 15.2 through 18.9 sit outside GitLab's security-maintained patch trains and receive no backport. Operators on those lines must upgrade to a supported release—there is no patch for older versions.

Helm and Operator users must check the GitLab version inside the Webservice image running Puma, not the chart or Operator version. The version mapping table is in the GitLab Charts documentation.

Mitigation & Patching

Upgrade immediately to one of the fixed releases:

  • 18.10.8 (for 18.10.x)
  • 18.11.5 (for 18.11.x)
  • 19.0.2 (for 19.0.x)
  • Any later version

No configuration-only workaround exists. Depthfirst researcher Yuhang Wu confirmed there is no documented GitLab option that fully disables the vulnerable notebook-diff path. Operators who cannot upgrade should contact GitLab for deployment-specific guidance.

The patch is a single-line dependency bump in GitLab's Gemfile.lock: oj (~> 3.17.3). The corresponding merge requests are:

Important: These MRs appear under "Bug fixes" in the June 10 patch release notes, not under "Security fixes." The security-fix table lists seven CVEs (CVE-2026-6552 through CVE-2026-1500). The Oj update is absent from that table. Operators who triaged the June 10 release against the security-fix table had no signal to treat it as urgent.

Detection

No CVE identifier exists for this chain. Depthfirst did not request one for the Oj bugs, and GitLab's HackerOne program assigned no separate identifier for the GitLab-specific chain. Detection must rely on version checks and behavioral indicators.

Version check:

bash

Behavioral detection (Suricata):

suricata

YARA rule for the exploit notebook structure:

yara

Sigma rule for audit logs:

yaml

Frequently Asked Questions

Q: Does this affect GitLab.com or GitLab Dedicated?
A: No. GitLab.com was patched on June 10. GitLab Dedicated customers do not need to take action.

Q: Why was no CVE assigned?
A: Depthfirst did not request CVEs for the Oj bugs. GitLab's HackerOne program assigned no separate identifier for the GitLab-specific chain. The absence reflects disclosure coordination choices, not severity.

Q: Is there a workaround without upgrading?
A: Depthfirst has not validated any configuration-only workaround. The notebook-diff path cannot be fully disabled via documented GitLab settings. Contact GitLab support for deployment-specific guidance.

Q: Does the exploit work against all GitLab versions listed?
A: The published PoC targets 18.11.3 on x86-64. Depthfirst states retargeting to another build on the same architecture generally needs only minor offset updates. ARM64 requires a full rework.

Q: How was this discovered?
A: Depthfirst's autonomous AI system (Open Defense Initiative) scanned Oj's native C code and surfaced 18 vulnerabilities including 7 memory-safety bugs. Researchers manually chained two of them.

Q: What is the CVSS score?
A: No official CVSS exists without a CVE. Given authenticated RCE as the git user with no interaction, a CVSS 3.1 base score would likely fall in the 8.8–9.8 range depending on scope and impact assumptions.

Key Takeaways

  1. Silent security fixes are dangerous. GitLab patched a critical RCE chain but labeled it a routine dependency update under "Bug fixes." Security teams triaging patch releases against the security-fix table missed it entirely.

  2. Native extensions break language safety assumptions. Ruby is memory-safe, but gems with C extensions (Oj, Nokogiri, Psych, etc.) reintroduce memory-corruption risk. The ipynbdiff gem is GitLab-maintained and in-tree, yet its call to Oj::Parser.usual.parse exposed native parser state to repository-controlled bytes.

  3. AI-driven vulnerability discovery is operational. Depthfirst's system autonomously found 18 vulnerabilities in Oj, including the two chained here. This class of tooling will accelerate native-extension auditing.

  4. Jupyter Notebook rendering is an overlooked attack surface. The .ipynb diff feature parses attacker-controlled JSON in the application process. Similar rich-diff renderers (Markdown, OpenAPI, etc.) warrant review.

  5. Patch-train boundaries create permanent exposure windows. GitLab 15.2–18.9 receive no backport. Organizations on those versions must upgrade to a supported release line—there is no patch coming.

Conclusion

The GitLab Oj RCE chain demonstrates how a dependency update buried in bug-fix release notes can hide a critical vulnerability. With a public PoC now available and no CVE to trigger vulnerability scanners, the onus is on operators to verify their GitLab version against the patched releases (18.10.8, 18.11.5, 19.0.2+) and upgrade immediately. The four-year exposure window (July 2022 – June 2026) and the absence of a configuration workaround make this a high-priority patch for every self-managed GitLab installation.


Sources:

Automated Transmission

This entry was synthesized and populated dynamically using native API integrations.

Resources & Links