Back to blog
CVEGitLabsupply chainDevOps

GitLab RCE via Oj: a memory-corruption chain hidden in a non-security patch

Published on 2026-07-257 min readCleanIssue

> In short: A GitLab RCE chain built on two memory-corruption bugs in Oj (a Ruby JSON parser) lets any authenticated user who can push to a project run commands as git on self-managed GitLab. Fixed in 18.10.8, 18.11.5, and 19.0.2 — but the June 10 fix was filed under bug fixes, not security, with no CVE. depthfirst published working PoC code on July 24.

Why this matters to you

GitLab is where your source code, CI/CD secrets, and deploy keys live. A self-managed GitLab compromise is a supply-chain incident: the attacker gets your code and your pipeline in one move. If you self-host GitLab for sovereignty or cost reasons (many French SaaS vendors do), this chain lands directly on your infrastructure.

The most uncomfortable detail is not the bug itself — it's that the fix shipped six weeks before the PoC, and most operators never knew it was security-relevant. GitLab listed the Oj 3.17.3 bump under bug fixes. There is no CVE, no CVSS, no advisory. Teams that triage releases against the security table had no signal to upgrade urgently.

The chain in plain terms

GitLab's notebook renderer (ipynbdiff) passes repository-controlled .ipynb JSON to Oj::Parser.usual.parse inside a long-lived Puma worker. Two Oj bugs make the chain work:

  • A write past a fixed 1,024-byte nesting stack that ends up controlling the parser's start callback.
  • A truncation of a 65,565-byte object key into a 29-byte signed 16-bit field that returns a live heap pointer — which GitLab renders into the commit diff.
  • The attacker commits a crafted Jupyter notebook and opens its commit diff. That leaks a heap pointer. Repeat enough times to locate libc in memory, then two more notebooks fire the payload pointing the callback at system(). No admin rights, no CI/runner access, no victim interaction, no access to anyone else's project.

    The affected versions and the Helm trap

    All tiers are affected, CE and EE, Free through Ultimate:

  • GitLab 15.2.0 to 18.10.7 → fix in 18.10.8
  • GitLab 18.11.0 to 18.11.4 → fix in 18.11.5
  • GitLab 19.0.0 to 19.0.1 → fix in 19.0.2
  • Oj gem 3.13.0 to 3.17.1 → fix in 3.17.3
  • The trap: if you deploy GitLab via Helm or the Operator, check the GitLab version inside the Webservice image running Puma, not the chart or Operator version. Anything on 15.2 through 18.9 gets no backport — those lines sit outside GitLab's security-maintained patch trains, so those installs must move to a supported release.

    What to do

  • Upgrade to 18.10.8, 18.11.5, or 19.0.2 depending on your train. There is no documented configuration-only workaround GitLab or depthfirst will endorse.
  • Audit who has push access. Until you upgrade, the chain only needs an authenticated user with push rights on any project. Tighten that surface, especially external contributors.
  • Review access logs for notebook-diff views and unusual commits predating the upgrade — the memory-search phase takes 5–10 minutes on a fresh two-worker install, so probing leaves traces.
  • Rotate secrets that lived in the GitLab instance after any suspected exposure: CI/CD tokens, deploy keys, runner registration tokens, anything in CI variables. Commands run as git reach source code, Rails secrets, service credentials, and CI/CD data.
  • The wider lesson: security fixes that don't look like security fixes

    The most important takeaway for an ops team is procedural. Not every security fix is published as one. depthfirst's wider Oj review produced nine more CVE advisories, none of them this chain. The GitLab fix went through HackerOne but was filed as a bug fix in the release notes. Your patching process cannot rely on the "security fixes" table alone — it has to triage dependency bumps on security-sensitive parsers (Oj, libxml2, Jackson, Fastjson) as if they were CVEs even when no CVE is attached. That's how you would have caught this on June 10 instead of July 24.

    Building HR, payroll, or recruiting software? CleanIssue performs security audits for HR SaaS in real-world conditions, no source code access needed. For a first read of your exposure, start with an external review of your application.

    Need an external review of your HR SaaS?

    Share your product, stack, and client context. We will come back with the right review scope.

    Discuss your audit