Security researchers at depthfirst published working exploit code on July 24 for a GitLab flaw that GitLab patched six weeks earlier, on June 10. It runs commands as git on any self-managed 18.11.3 server that has not taken the update.
Any authenticated user who can push to a project can run it. The attacker commits a crafted Jupyter notebook and opens its commit diff, which leaks a heap pointer. Enough of those and an automated probe can locate the libraries in memory. Two more notebooks then fire the payload. No administrator rights, no CI or runner access, no victim interaction, no access to anyone else's project.
GitLab did not file the fix as a security fix. A review by The Hacker News found the Oj 3.17.3 bump listed under bug fixes in the June 10 patch release, not in the security-fix table. There is no CVE, no CVSS score, and no mention of the notebook-diff chain. Operators who triaged that release against the security table had no reason to treat it as urgent.
Two memory corruption bugs in Oj, a Ruby JSON parser implemented largely in native C, make the chain work. depthfirst says its system flagged them autonomously, and researchers chained them by hand.
GitLab's notebook renderer, an in-tree gem called ipynbdiff, passes repository-controlled .ipynb JSON to Oj::Parser.usual.parse inside a long-lived Puma worker, so attacker-controlled bytes reach Oj's manually managed C memory inside the application process.
One bug writes past a fixed 1,024-byte nesting stack until it controls the parser's start callback. The other truncates a 65,565-byte object key to 29 in a signed 16-bit field and returns a live heap pointer, which GitLab renders into the diff. The leak locates libc, and the write points the callback at system().
| Component | Affected | First fixed |
|---|---|---|
| GitLab CE/EE | 15.2.0 to 18.10.7 | 18.10.8 |
| GitLab CE/EE | 18.11.0 to 18.11.4 | 18.11.5 |
| GitLab CE/EE | 19.0.0 to 19.0.1 | 19.0.2 |
| Oj gem | 3.13.0 to 3.17.1 | 3.17.3 |
All tiers are affected, CE and EE, Free through Ultimate. Ruby itself is not. Oj 3.17.2 carried other fixes from the same review but not these two.
Upgrade to 18.10.8, 18.11.5, or 19.0.2. Neither GitLab nor depthfirst offers a workaround for anyone who cannot. depthfirst researcher Yuhang Wu told The Hacker News he is not aware of a documented GitLab option that fully disables the vulnerable notebook-diff path, and that depthfirst has not validated a configuration-only workaround it can recommend. Keeping untrusted users away from notebook-diff rendering would remove this entry point in principle, he said, but operators who cannot upgrade should ask GitLab for guidance specific to their deployment.
The trap is Helm and 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, because those lines sit outside GitLab's security-maintained patch trains, so those installs have to move to a supported release instead.
Commands run as git, the account behind Puma. How far that goes depends on how the install is isolated. In reach: source code, Rails secrets, service credentials, CI/CD data, and internal services the application can talk to.
The public exploit is built for GitLab 18.11.3 on x86-64. Gadget offsets, register state, and jemalloc behavior all came from that image, and a recovered library base holds only until the Puma master restarts, so this is not drop-in against an arbitrary target.
The Oj bugs are general, and the exploit travels further than that one image suggests. Wu said the exploitation path is not specific to 18.11.3, and that retargeting another GitLab build on the same architecture would generally need only minor adjustments, mostly updated gadget or symbol offsets. Those offsets often do not change at all across nearby releases, because the gadgets come from Ruby and system libraries rather than GitLab's own code. Architecture is the real barrier. Moving to ARM64 would mean different calling conventions, gadgets, register usage, and possibly different heap behavior, and the Ruby build, libc, allocator, and compilation environment matter more than the GitLab version does. depthfirst measured five to ten minutes for the memory search on a fresh two-worker install and projects one to two hours on longer-running ones. Its writeup has the full chain.
depthfirst reported the Oj bugs on May 21, the maintainer merged fixes on May 27, and Oj 3.17.3 shipped June 4. The GitLab chain went to GitLab on June 5, was confirmed on June 8, and was patched on June 10. Wu said depthfirst did not request CVE identifiers for either issue. The GitLab chain went through GitLab's HackerOne program, which assigned depthfirst no separate identifier for it, and the two Oj bugs went straight to the maintainer with no CVE requested and none assigned to depthfirst's knowledge. The absence, he said, reflects how the disclosures were coordinated rather than anything about severity.
depthfirst says it is not aware of in-the-wild exploitation, and that GitLab reproduced the RCE independently. Wu said that rests on the absence of reports and on depthfirst seeing nothing outside its own research environment, not on telemetry. The company has no access to GitLab's internal data or customer environments, he said, and GitLab is the right source for any conclusion drawn from those. Its wider Oj review produced nine more CVE advisories, none of them this chain.
GitLab has not responded to questions from The Hacker News about why the fix was not classified as a security issue and whether a CVE will be assigned. This story will be updated if it does.
Corrected on July 26, 2026: an earlier version said porting the exploit is real work. depthfirst says retargeting another GitLab build on the same architecture generally requires only minor offset updates; the substantial work is moving to a different architecture.





