Home/ Blog/ Security news/ Article
Blog · Security news

A GitLab flaw lets any user with push access run code on the server, and a public exploit is now out

GitLab quietly patched a self-managed code-execution flaw on June 10 with no CVE. A public exploit is now out and any push-access user can run code as git.

Isometric cutaway of stacked blocks with one fractured glowing block at the core

GitLab closed a code-execution hole in its self-managed servers on June 10. It did not call it one. The Oj JSON-parser bump that fixed the bug sits under bug fixes in the release notes, with no CVE, no severity score, and no mention of the notebook chain it closed. An administrator who triages updates by reading the security-fix table had no reason to rush it. On July 24 the researchers who reported the flaw, depthfirst, published a working exploit. The servers still on the old build are now the ones a public tool aims at.

This is the rare vulnerability story where the technical detail is not the hard part. The hard part is that the fix arrived unlabeled, so the exposure is as much about how teams schedule updates as about the bug itself. We wrote up GitLab's session-hijack patch earlier this year; this one is quieter and, for anyone who defers non-security releases, more dangerous.

What the flaw is, at a defender's level

Two memory-corruption bugs live in Oj, a fast Ruby JSON parser whose core is written in native C. A Jupyter notebook is, underneath, a JSON document. GitLab's own ipynbdiff gem feeds those notebook files straight from the repository into Oj::Parser.usual.parse when it draws a commit diff, and that call runs within a Puma worker process that stays alive across many requests. So bytes an attacker chose land in hand-managed C memory, inside the running application rather than off in some sandbox.

The first overruns a 1,024-byte buffer that caps how deep JSON nesting can go, and the overflow corrupts the parser's own bookkeeping. The second mishandles a very large object key in a signed 16-bit length field, and the miscount leaks an 8-byte memory address that ends up drawn into the diff on screen. Used together, they reveal enough of the process layout to find its loaded libraries, then bend one of the parser's callbacks toward running a command. depthfirst withheld the working payload; so will we, because a defender needs the mechanism, not the shellcode.

Who can pull the trigger, and what they reach

Read the precondition carefully, because it is the part most summaries blur. This is not an unauthenticated, internet-facing bug. It wants a logged-in project member who is allowed to push, who then opens the diff of a notebook they just committed. From there the command runs as the git account sitting behind those Puma workers. That account can touch the repositories themselves, GitLab's Rails secret keys, the credentials wired to connected services, pipeline data, and internal-only endpoints.

Here is why "authenticated" should not calm you down. On most self-managed GitLab instances, push access is granted broadly: every developer, a pile of contractors, service accounts, and anyone holding a personal access token that leaked into a log or a laptop. A single phished developer session is enough. In your risk model this behaves much closer to a remote takeover than to a narrow privilege bug, and it should be triaged that way.

GitLab release trainAffected versionsFirst fixed
Up to 18.1015.2.0 up to 18.10.718.10.8
18.11 series18.11.0, 18.11.1, up to 18.11.418.11.5
19.0 series19.0.0 and 19.0.119.0.2
GitLab Community and Enterprise Edition, all tiers. Source: GitLab patch release, June 10, 2026.

The gap between the patch and the exploit is the story

The vulnerable code was not new. depthfirst reports the flawed logic sat in Oj for roughly 1,753 days before anyone reported it, and GitLab began routing notebook diffs through the affected call in version 15.2.0 back in mid-2022. The recent sequence is fast, though: reported to Oj on May 21, fixed upstream and released as Oj 3.17.3 on June 4, disclosed to GitLab on June 5, confirmed June 8, patched June 10. The public exploit landed on July 24.

Oj bugs to public GitLab exploitMay 21: Bugs reported. Jun 4: Oj 3.17.3 out. Jun 10: GitLab patch. Jul 24: Public PoC.Oj bugs to public GitLab exploitMay 21Bugs reportedJun 4Oj 3.17.3 outJun 10GitLab patchJul 24Public PoC
Source: depthfirst disclosure and GitLab patch release notes, 2026.

Six weeks separated the fix from the exploit. depthfirst reports no sign of anyone using this in the wild yet, and the published exploit only works against an 18.11.3 server on x86-64, so pointing it at another build or a different chip takes real effort. Treat that as a short runway, not immunity. The memory offsets that make an exploit portable are exactly what a motivated attacker files down over a weekend.

Why an unlabeled fix is its own exposure

The Hacker News reviewed the June 10 release notes and found the Oj 3.17.3 upgrade listed under bug fixes rather than in the security-fix table, with no CVE assigned and no CVSS score. GitLab.com, the managed service, is patched centrally, so its users are already covered. The people carrying the risk are self-managed operators, and many of them run a patch cadence that fast-tracks security releases and lets feature and bug-fix updates wait for a maintenance window.

That cadence is reasonable, and it is exactly what this flaw slips through. When a remote code execution fix ships without the labels your triage keys on, the label is the vulnerability. It is the same lesson as when a trusted dependency shipped the problem with a valid signature: the control everyone relies on to sort urgent from routine quietly passed the dangerous thing through. Note too that GitLab's security-maintained patch trains do not backport this to 15.2 through 18.9, so older self-managed installs get no fix at all short of a real upgrade.

Detection while you are still on the old build

Patching closes the hole. It does not tell you whether someone already probed it. Because the trigger is a notebook diff render inside a Puma worker, the useful signals sit on the host, not at the perimeter:

  • Puma workers crashing or segfaulting around notebook-diff rendering. A failed exploit attempt is far noisier than a clean one, and repeated worker restarts tied to diff views are worth a look.

  • Unexpected child processes under the git account. That account runs application code, so a shell, a network client, or an interpreter spawning beneath it is a strong lead.

  • Bursts of .ipynb commits and diff views from a new, low-privilege, or rarely-active account. The exploit needs the attacker to push notebooks and open their diffs, which is an odd pattern for most real users.

None of these is a signature you can buy off the shelf, which is the point of watching host behavior rather than waiting for a rule. It is also worth pulling the list of who actually holds push access right now; the answer is usually longer than anyone expects.

Patch GitLab, then audit your own Ruby

The first action is unambiguous: upgrade self-managed GitLab to 18.10.8, 18.11.5, or 19.0.2, whichever train you are on. There is no workaround, and the fixed builds carry the corrected Oj.

The wider action is the one the wire coverage skips. The root bugs are not GitLab's code; they are in Oj, a general-purpose gem that thousands of Ruby applications pull in, often without knowing it sits under a higher-level JSON call. Any Ruby service that parses untrusted JSON through Oj 3.13.0 to 3.17.1 is worth the same scrutiny, and the fix is Oj 3.17.3 or later. Audit your dependency trees for native C extensions handling attacker-reachable input; that is where memory safety leaves the language and becomes your problem.

There is a pattern behind the discovery worth sitting with. depthfirst found these with an automated system that ranked 18 candidate flaws in Oj, seven of them memory-safety bugs, before a human chained two into an exploit. It is the same shape as when an AI system flags bugs in old, trusted code, when researchers reopened a filesystem audited years earlier, and when a vendor's own AI found the flaws in its appliances. The economics of finding old memory-corruption bugs in widely-used native code just changed. The decade-old dependency you never think about is now cheap to fuzz at scale, and the update that fixes it may not arrive wearing a label that tells you to hurry.

Topics

Frequently asked questions

Is there a CVE for this GitLab vulnerability?

No CVE was assigned. According to The Hacker News, GitLab shipped the fix in its June 10 release as an unlabeled bug fix, listing the Oj 3.17.3 upgrade under bug fixes rather than the security-fix table, with no CVSS score. That absence is a triage problem, not a sign the flaw is minor.

Which GitLab versions are affected and fixed?

Community and Enterprise Edition are affected across every tier, from 15.2.0 up to the builds just before each fix; the version table lists the exact ranges. The first fixed releases are 18.10.8, 18.11.5, and 19.0.2. Anything on 15.2 through 18.9 gets no backport and needs a real upgrade.

Can an unauthenticated attacker exploit it?

No. The attacker needs an authenticated account with push access to a project, then views a diff of a notebook they committed. In practice push access is granted broadly, so a single developer account or a leaked access token is enough to reach it.

Is this being exploited in the wild?

No in-the-wild exploitation has been reported. depthfirst published a working proof of concept on July 24 that targets GitLab 18.11.3 on x86-64 only. Retargeting another build or architecture takes real effort, so there is a short window, not immunity.

What can the code reach if it runs?

Code runs as the git account behind GitLab's Puma workers. That account can reach the repositories, Rails secret keys, credentials for connected services, pipeline data, and internal endpoints. The breadth of that access is why an authenticated push-access flaw here should be treated like a near-full takeover.

Do GitLab.com (SaaS) users need to act?

No. GitLab.com is the managed service and was patched centrally. The risk falls on self-managed operators running their own GitLab servers, who must upgrade to 18.10.8, 18.11.5, or 19.0.2 and, ideally, audit any Ruby services that use the Oj gem.

Ready to meet the Guardians?

Deploys fast - agentless for monitoring and cloud, a lightweight agent for deep endpoint security. Just Suriq, standing watch.