Working PoC Published for GitLab RCE Chain
Security researcher Yuhang Wu at depthfirst (Open Defense Initiative) has published a fully working proof-of-concept (PoC) exploit for a remote code execution vulnerability chain in GitLab. The exploit targets two chained Ruby memory corruption bugs in the Oj (Optimized JSON) gem used by GitLab's Jupyter notebook diff renderer, giving an authenticated attacker code execution as the git user running GitLab's Puma web workers.
The vulnerability affected GitLab Community and Enterprise Edition across a wide range — CE/EE 15.2.0 through 18.11.4 — and was silently present in production for over 1,700 days before disclosure.
The Vulnerability Chain
The attack chains two memory-safety bugs in the Oj Ruby JSON parser (versions 3.13.0–3.17.1), discovered through GitLab's use of the ipynbdiff gem for rendering Jupyter notebook file comparisons.
CVEs Involved
| CVE | Description |
|---|---|
| CVE-2026-54502 | Stack buffer overflow in Oj.dump — write-beyond-bounds primitive |
| CVE-2026-54896 | Heap buffer overflow during exception serialization — read primitive |
| CVE-2026-54897 through CVE-2026-54903 | Additional use-after-free and integer overflow issues in Oj |
Stage 1: Write Primitive (CVE-2026-54502)
An unchecked nesting stack in Oj's parser allows writing beyond a 1,024-byte stack buffer, corrupting the parser's internal buf.head pointer. A subsequent realloc caches the forged address, which a Ruby Array reclaims to overwrite a callback pointer — establishing an arbitrary write primitive.
Stage 2: Read Primitive / ASLR Defeat (CVE-2026-54896)
An unsafe 16-bit narrowing of a key-length field causes Oj to read from an inline buffer rather than the heap, leaking 8 bytes of a heap allocation. This enables the attacker to defeat ASLR and compute the base addresses needed for the gadget chain.
Exploit Delivery
1. Attacker creates two lexically-ordered crafted .ipynb files (Jupyter Notebooks)
2. Commits both files to a repository on the target GitLab instance
3. Requests the commit diff view in GitLab's web UI
4. GitLab's ipynbdiff gem calls Oj::Parser.usual.parse on the files
5. The two notebooks maintain corrupted parser state across sequential parses
within a single Puma worker (maintaining the corrupt state between parses)
6. Gadget chain within libruby executes system() — max 39 bytes of command
7. Attacker achieves RCE as the `git` user on the Puma web workerAffected Versions and Patches
| GitLab Version Range | Fixed In |
|---|---|
| CE/EE 15.2.0 – 18.10.7 | 18.10.8 |
| CE/EE 18.11.0 – 18.11.4 | 18.11.5 |
| CE/EE 19.0.0 – 19.0.1 | 19.0.2 |
Why This Matters
The vulnerability chain is notable for several reasons:
- Long undetected window: The vulnerable parser code was merged into Oj on August 8, 2021. GitLab adopted
Oj::Parser.usual.parsefor notebook diffs on July 7, 2022. The bugs persisted undetected for over 1,753 days before disclosure in May 2026. - Authenticated-only barrier is low: Any authenticated GitLab user — including free accounts on self-managed instances — can trigger the exploit.
- Self-managed deployments specifically at risk: GitLab.com is managed by GitLab and patched immediately; self-managed operators must apply patches themselves.
- PoC is now public: With a working PoC published, exploitation risk for unpatched instances escalates rapidly.
Disclosure Timeline
| Date | Event |
|---|---|
| Aug 8, 2021 | Vulnerable parser code merged into Oj gem |
| Jul 7, 2022 | GitLab adopted Oj::Parser.usual.parse for notebook diffs |
| May 21, 2026 | Vulnerabilities reported to Oj maintainers |
| May 27, 2026 | Fixes merged into Oj gem |
| Jun 10, 2026 | GitLab releases patched versions (18.10.8, 18.11.5, 19.0.2) |
| Jul 25, 2026 | Yuhang Wu publishes full PoC and writeup |
Remediation
Immediate: Upgrade GitLab
Update your self-managed GitLab instance to the patched release for your branch:
# Check current GitLab version
gitlab-rake gitlab:env:info | grep "GitLab version"
# Ubuntu/Debian — upgrade GitLab package
sudo apt-get update && sudo apt-get install gitlab-ee
# RHEL/CentOS
sudo yum update gitlab-ee
# Verify version after upgrade
gitlab-rake gitlab:env:info | grep "GitLab version"If Immediate Upgrade Is Not Possible
Disable the Jupyter notebook diff rendering feature:
# In GitLab Rails console
Feature.disable(:jupyter_notebook_diff)Or restrict repository access to trusted users only until patching is complete.
Detection
Review GitLab application logs for exploitation attempts:
# Check for anomalous diff requests involving .ipynb files
grep -i "ipynb" /var/log/gitlab/gitlab-rails/production.log | grep "diff"
# Monitor for unexpected child process spawning from Puma workers
sudo grep "puma" /var/log/audit/audit.log | grep "execve"References
- depthfirst — Going Depthfirst: Achieving GitLab RCE via Two Ruby Memory Corruption Vulnerabilities
- The Hacker News — Researcher Publishes GitLab RCE PoC
- GitLab ipynbdiff gem