← Back to Feed CACHED · 2026-10-21 20:35:31 · CACHE_KEY CVE-2026-110264
CVE-2026-110264 · CWE-190 · Disclosed 2026-10-21

GitLab CE/EE Onigmo regex engine integer overflow via org-ruby Org-mode rendering

ASSESSED — NOISGATE
Vendor
—
—
—
Reassessed
—
—
—
Verdict: —
Do you agree?
01 · The Real Story

Somebody left `Regexp.new()` pointed at raw user input for twelve years, and now one Org-mode keyword topples the entire source-code vault

CVE-2026-110264 is an integer overflow in Onigmo, the Ruby regular-expression engine that ships inside every GitLab CE/EE self-managed package. The org-ruby gem — responsible for rendering Emacs Org-mode (.org) files in the GitLab web UI — passes raw #+TODO: keywords straight into Regexp.new(). An attacker who crafts a keyword containing more than 32,767 generic repeats causes Onigmo's signed 16-bit repeat-ID counter to wrap, producing an out-of-bounds heap write during regex compilation. That write can be steered into a system() call executing as the git user, giving the attacker a shell on the GitLab host. Every self-managed version from 7.0 (released June 2014) through 19.5.0 is affected — roughly 12 years of release branches. Fixes landed on 2026-10-21 in 19.5.1, 19.4.3, 19.3.6, 19.2.9, 19.1.9, 19.0.10, and 18.11.13. GitLab.com and GitLab Dedicated are already patched and not at risk.

GitLab's CNA-assigned CVSS 10.0 (AV:N/AC:L/PR:N/UI:N/S:C) is aggressive but largely defensible for the self-managed population. The PR:N component is realistic because most self-managed instances historically default to open registration (admin-approval gating was added later and is frequently not enforced), meaning an attacker can create an account, push a malicious .org file to a new public project, and trigger the rendering themselves — no victim interaction required. The only meaningful friction is that some hardened enterprises close registration entirely or restrict the instance to VPN-only access, turning this into an authenticated/internal-network bug for those deployments. That is not enough to pull a CI/CD platform below CRITICAL. The sibling CVE-2026-97229, patched just nine days earlier on Oct 12 via a nearly identical integer-overflow in the CI/CD regex path, proved the exploit class is viable — and the Oct 12 patches are *below* the new fixed versions, so instances that patched for -97229 are still exposed to -110264.

"Upload one .org file, own the GitLab server. Twelve years of releases are live targets."
02 · The Attack Path

5 steps from start to impact.

STEP 01

Identify exposed GitLab self-managed instance

The attacker uses Shodan, Censys, or FOFA to enumerate internet-reachable GitLab instances. Censys currently indexes ~86,000 hosts running GitLab with ~208,000 web properties. Version fingerprinting is trivial via the unauthenticated /help or /users/sign_in page, which embeds the GitLab version string in the HTML footer.
Conditions required:
  • Target runs GitLab CE/EE self-managed (not GitLab.com or Dedicated)
  • Instance is reachable over the network
Where this breaks in practice:
  • Enterprises behind VPN/ZTNA reduce the reachable population to internal users only
Detection/coverage: Shodan/Censys dorks for GitLab version strings; external attack-surface monitoring tools flag exposed instances
STEP 02

Obtain commit access via open registration

The attacker registers a new account on the target instance. GitLab's historical default is open registration; newer releases gate accounts behind admin approval, but many administrators auto-approve or never configure the gate. On instances where registration is closed, the attacker needs a compromised or insider account. Once logged in, the attacker creates a new public project (or forks an existing public project).
Conditions required:
  • Open registration enabled, OR existing/compromised credentials
Where this breaks in practice:
  • Locked-down instances with closed registration block this step entirely
  • Admin-approval gating adds a delay; attacker must wait for approval
  • Instances restricted to SAML/LDAP-only auth raise the bar significantly
Detection/coverage: GitLab audit log: user_created and project_created events; anomalous registration spikes
STEP 03

Commit malicious .org file with crafted #+TODO: keyword

The attacker pushes a single .org file containing a #+TODO: line with >32,767 generic regex repeats. The payload is small (a few KB) and looks like a benign text file to casual inspection. Naming the file README.org causes GitLab to auto-render it on the project landing page, but any .org file path works when navigated to directly. No CI/CD pipeline configuration is needed — this is purely a file-rendering exploit.
Conditions required:
  • Ability to git push to any branch of any project on the instance
Where this breaks in practice:
  • Push rules blocking .org extensions would stop this, but this is not a standard configuration
  • Server-side Git hooks could theoretically inspect file content, but no detection signature exists pre-patch
Detection/coverage: Git push audit events; custom server-side hooks scanning for anomalous #+TODO: patterns (not standard)
STEP 04

Trigger Org-mode rendering → Onigmo integer overflow → OOB write

The attacker navigates to the committed file's URL in the GitLab web UI (or simply visits the project root if the file is named README.org). GitLab's backend calls the org-ruby gem to render the file. The gem passes the #+TODO: keyword into Regexp.new(), triggering Onigmo's regex compiler. The >32,767 repeats overflow the signed 16-bit repeat-ID, producing invalid regex bytecode that causes an out-of-bounds heap write when matched. The attacker controls both payload delivery and trigger — no victim interaction is required.
Conditions required:
  • GitLab web UI is reachable (standard HTTPS)
  • org-ruby gem is present (bundled in all GitLab CE/EE since 7.0)
Where this breaks in practice:
  • If a reverse proxy strips or blocks requests to .org file render endpoints, the trigger fails — but this is an unusual and fragile configuration
  • ASLR and stack canaries on the GitLab host may reduce exploit reliability on hardened Linux kernels
Detection/coverage: GitLab Sigma detection: Org mode render error burst (published Oct 20, 2026); Puma worker crash logs; unexpected Regexp compilation errors in production.log
STEP 05

Code execution as git user → full GitLab compromise

The OOB write is leveraged to redirect control flow into a system() call, granting the attacker a shell as the git user. On Omnibus installations, git has read access to the entire GitLab data directory: repository contents, CI/CD secrets in the database, runner registration tokens, SSH keys, and Rails secret key material. From here the attacker can dump every repository, inject backdoors into source code, extract CI/CD variables (cloud credentials, deploy keys), forge session cookies via the Rails secret, and pivot to connected infrastructure including CI runners, container registries, and deployment targets.
Conditions required:
  • OOB write is successfully weaponized into controlled code execution
Where this breaks in practice:
  • Memory corruption reliability is architecture-dependent; binary hardening (PIE, RELRO, stack canaries) complicates exploitation but does not prevent it for skilled attackers
  • SELinux/AppArmor in enforcing mode on the GitLab host may limit post-exploitation lateral movement
Detection/coverage: GitLab Sigma detection: Onigmo native worker crash; EDR/HIDS alerts for unexpected child processes spawned by Puma/Unicorn workers; anomalous git user process trees; unexpected outbound connections from the GitLab host
03 · Compensating Control

1
CRITICAL 9.6→IGNORE 0.0
SEVERITY REDUCED
Patch to a fixed GitLab version immediately — Upgrade to 18.11.13, 19.0.10, 19.1.9, 19.2.9, 19.3.6, 19.4.3, or 19.5.1. This is the only complete fix. Per the noisgate mitigation SLA for CRITICAL, deploy within 3 days. Per the noisgate remediation SLA, complete fleet-wide patching within 90 days. Given no workaround exists, mitigation and remediation are the same action here. Test in staging, then push to production. If you run a branch older than 18.11 with no backport available, you must upgrade to 18.11.13+ or a supported 19.x branch — there is no in-place fix for legacy branches.
2
CRITICAL 9.6→HIGH 7.5
SEVERITY REDUCED
Disable open user registration and enforce SAML/LDAP-only authentication — In Admin Area → Settings → General → Sign-up restrictions, uncheck 'Sign-up enabled' and require external authentication. This eliminates the anonymous account-creation step, forcing the attacker to already possess valid credentials. On instances already using SAML/LDAP, verify that local password authentication is disabled. Deploy within 3 days (noisgate mitigation SLA). This does NOT protect against insiders or compromised accounts, but it removes the lowest-friction attack path.
3
CRITICAL 9.6→HIGH 7.0
SEVERITY REDUCED
Restrict GitLab network exposure to VPN/ZTNA only — If GitLab does not need to be internet-facing, place it behind a VPN or zero-trust network access gateway immediately. This shrinks the attacker pool from the entire internet to internal users and VPN-authenticated parties. Deploy within 3 days. Note: this is a compensating control, not a fix — any authenticated internal user can still exploit the vulnerability.
4
CRITICAL 9.6→CRITICAL 9.0
Deploy GitLab's Sigma detections for exploitation indicators — Import the two Sigma rules published Oct 20, 2026: Org mode render error burst and Onigmo native worker crash into your SIEM (Splunk, Elastic, Sentinel, QRadar). These detect post-exploitation indicators — abnormal Puma worker crashes and Org-mode rendering failures. Detection does not prevent exploitation but enables rapid incident response. Also monitor for unexpected child processes spawned by the git user and anomalous outbound connections from the GitLab host.
5
CRITICAL 9.6→HIGH 6.5
SEVERITY REDUCED
Block .org file rendering at the reverse proxy layer — If you front GitLab with nginx, HAProxy, or a CDN, add a rule that returns HTTP 403 for requests matching repository file-view paths ending in .org (e.g., ~* /.*/-/blob/.*\.org$). This is fragile — it may break legitimate Org-mode file viewing and may not cover all rendering paths (raw endpoints, API, merge request diffs) — but it raises the bar for the specific documented attack vector while you schedule patching. Test thoroughly before deploying.
What doesn't work
  • WAF rules on HTTP parameters: The payload is delivered via git push (Git pack protocol over HTTPS or SSH), not as an HTTP form/query parameter. Web application firewalls cannot inspect the contents of Git pack data for malicious Org-mode keywords.
  • The Oct 12 patch for CVE-2026-97229: That patch (19.4.2, 19.3.5, 19.2.8) fixes the CI/CD regex path only. Those version numbers are *below* the -110264 fixed versions. If you patched on Oct 12 and stopped, you are still vulnerable to this CVE.
  • Disabling CI/CD pipelines or restricting Runner access: CVE-2026-110264 exploits the Org-mode *file rendering* path, not CI/CD pipeline execution. Disabling pipelines has no effect on this vulnerability.
  • IP allowlisting on the GitLab API alone: The attack uses git push for payload delivery and standard web UI navigation for triggering. API restrictions do not cover these paths.
04 · Intelligence Metadata

The supporting signals.

In-the-wild exploitationNone observed. GitLab confirms no known exploitation as of Oct 21, 2026. Not listed in CISA KEV. However, the sibling CVE-2026-97229 (same integer-overflow class, CI/CD regex path) saw PoC publication and active probing within days of its Oct 12 disclosure, establishing a clear pattern for this bug family.
PoC availabilityNo public PoC as of Oct 22. pocindex.io returns no results. No GitHub repos named after CVE-2026-110264 found. The sibling CVE-2026-97229 has a PoC on vulners.com demonstrating the integer-overflow pattern. Given that GitLab's advisory and the hol.org writeup disclose the precise trigger (#+TODO: keyword, >32,767 repeats, Regexp.new()), a working PoC is likely days away from public release.
EPSSNot yet scored — CVE was published Oct 21, 2026; FIRST EPSS data typically requires 24–48 hours. Given the CVSS 10.0 network vector and GitLab's exploitation history (multiple prior CVSS 10 bugs exploited within a week), expect a high EPSS probability once scored.
KEV statusNot listed in CISA Known Exploited Vulnerabilities catalog as of Oct 22, 2026.
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H — 10.0 Critical per GitLab CNA advisory. Network-reachable, low complexity, no privileges, no user interaction, scope change (compromise of the GitLab host impacts all hosted projects and connected infrastructure), full confidentiality/integrity/availability impact.
Affected versionsGitLab CE/EE self-managed 7.0 through 19.5.0 — every release from June 2014 to Oct 2026. The underlying flaw exists in Onigmo (Ruby's regex engine) as bundled by GitLab.
Fixed versions18.11.13, 19.0.10, 19.1.9, 19.2.9, 19.3.6, 19.4.3, 19.5.1 — all released Oct 21, 2026. Branches older than 18.11 have no backport; those instances must upgrade to 18.11.13+ or a supported 19.x branch. Note: the Oct 12 patches for CVE-2026-97229 (19.4.2, 19.3.5, 19.2.8) are *below* these versions and do NOT protect against -110264.
Internet exposureCensys indexes ~86,000 GitLab hosts with ~208,000 web properties globally. Top countries: China (19,435), US (13,242), Germany (12,249), Russia (8,639), France (5,062). Prior GitLab critical CVEs showed 13,700+ instances still unpatched weeks after disclosure. Expect similar lag here.
Disclosure timelineOct 12, 2026: sibling CVE-2026-97229 patched (19.4.2, 19.3.5, 19.2.8). Oct 20: GitLab publishes Sigma detections. Oct 21: CVE-2026-110264 patches released. Oct 22: 1 day post-patch; vast majority of self-managed instances are still unpatched.
Researcher / reporterjoaxcar via GitLab's HackerOne bug bounty program. No public writeup from the researcher yet.

Sources.

  1. GitLab Critical Patch Release 19.5.1 (vendor advisory)
  2. The Hacker News — Critical GitLab Flaw (CVSS 10.0)
  3. hol.org — CVE-2026-110264 campaign analysis
  4. Rapid7 — GitLab CVSS 10.0 authentication catch (sibling CVE-2026-97229)
  5. vulners.com — PoC for GitLab Onigmo integer overflow (CVE-2026-97229)
  6. Cybernews — Thousands of vulnerable GitLab servers exposed
  7. netmanageit — CVE-2026-110264 coverage
  8. GitLab open registration default issue
05 · The Call

Final Verdict
= UNCHANGED to CRITICAL (9.6/10)

Why this verdict

  • Unauthenticated RCE with attacker-controlled trigger: The attacker delivers the payload (malicious .org file) and triggers exploitation (navigating to the file URL) without requiring any victim interaction. On instances with open registration — still the historical default — the full chain is achievable by an anonymous internet attacker. This satisfies CVSS PR:N/UI:N for the majority of the installed base.
  • Twelve-year affected window, massive exposed population: Every GitLab CE/EE release from 7.0 (2014) through 19.5.0 is vulnerable. Censys shows ~86,000 exposed instances. The patch is 1 day old; expect >90% of the internet-facing population to remain unpatched for weeks, consistent with prior GitLab critical CVE patching velocity.
  • Sibling CVE proves the exploit class is weaponizable: CVE-2026-97229 — same integer-overflow root cause in the same regex engine, patched just 9 days earlier — already has a public PoC and saw active probing. This dramatically compresses the timeline to weaponization for -110264. The technical trigger (#+TODO: keyword, >32,767 repeats) is fully documented in public sources.
  • No vendor workaround available: GitLab's advisory provides no interim mitigation. There is no supported method to disable Org-mode rendering. Administrators cannot reduce their exposure without patching or applying non-standard compensating controls.
  • Role multiplier — GitLab IS the CI/CD and SCM platform: This is not software that occasionally occupies a high-value role; GitLab *is* the supply-chain infrastructure by definition. ≥95% of GitLab installations serve as the primary source code repository and CI/CD engine for their organization. Compromise of the GitLab host yields: (1) supply-chain-scale impact — the attacker can inject backdoors into any repository's source code and CI/CD pipelines, propagating malicious code to all downstream builds and deployments; (2) credential-scale impact — CI/CD variables, deploy keys, runner tokens, Rails secret keys, and SSH keys are all accessible to the git user; (3) identity-scale impact — forging sessions via Rails secrets grants admin access to every GitLab account. This blast radius is categorically domain/fleet/supply-chain tier. The verdict floor for a canonical CI/CD platform with a viable unauthenticated RCE chain is CRITICAL.
  • 0.4-point reduction from the theoretical 10.0: (a) Achieving reliable heap-corruption-to-code-execution across 12 years of diverse binary builds requires non-trivial exploit engineering — this is not a simple command injection; (b) the subset of instances with closed registration and VPN-only access genuinely reduces the reachable population for those deployments; (c) no confirmed in-the-wild exploitation yet. These frictions prevent a full 10.0 but do not pull below CRITICAL given the role multiplier and blast radius.

Why not higher?

A 10.0 would require truly zero-interaction, universally reliable exploitation — like a wormable network service bug. CVE-2026-110264 requires the attacker to commit a file and trigger rendering (self-serviceable but not zero-click), and the memory corruption step introduces exploit reliability variance across architectures and binary builds. The 9.6 reflects these minor frictions while keeping the verdict at the CRITICAL floor dictated by the supply-chain blast radius.

Why not lower?

Dropping below CRITICAL would require evidence that (a) the vast majority of self-managed GitLab instances have closed registration AND are not network-reachable, or (b) the memory corruption is impractical to weaponize. Neither is supported by the evidence. Censys shows 86,000+ exposed instances, many with open or loosely-gated registration. The sibling CVE-2026-97229 proves the Onigmo integer-overflow class is weaponizable with a public PoC. GitLab is canonically a CI/CD and supply-chain component — by the deployment-role blast-radius rule, a viable RCE in this class of software cannot drop below CRITICAL regardless of friction, because the worst plausible outcome (supply-chain compromise) is fleet-scale.

06 · Verification

Crowdsourced verification payload.

Run on the GitLab host itself (no arguments) or from any workstation with curl pointed at the instance URL. Local: ./check_cve_2026_110264.sh (needs read access to /opt/gitlab/). Remote: ./check_cve_2026_110264.sh https://gitlab.corp (the /api/v4/version endpoint may require a PRIVATE-TOKEN header — export it first if needed). No root required.

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/usr/bin/env bash
# CVE-2026-110264 — GitLab CE/EE Onigmo integer overflow (unauth RCE)
# Exit: 0=PATCHED  1=VULNERABLE  2=UNKNOWN
set -euo pipefail

version_gte() {
  [ "$(printf '%s\n%s' "$1" "$2" | sort -V | head -n1)" = "$2" ]
}

TARGET="${1:-}"
VERSION=""

if [ -n "$TARGET" ]; then
  URL="${TARGET%/}/api/v4/version"
  RAW=$(curl -skL --max-time 10 \
    ${PRIVATE_TOKEN:+-H "PRIVATE-TOKEN: $PRIVATE_TOKEN"} \
    "$URL" 2>/dev/null || true)
  VERSION=$(echo "$RAW" | grep -oP '"version"\s*:\s*"\K[^"]+' 2>/dev/null || true)
  if [ -z "$VERSION" ]; then
    echo "UNKNOWN — could not retrieve version from $URL"
    echo "  Hint: export PRIVATE_TOKEN=glpat-... and retry"
    exit 2
  fi
else
  for F in /opt/gitlab/embedded/service/gitlab-rails/VERSION \
           /opt/gitlab/version-manifest.txt; do
    if [ -f "$F" ]; then
      if [[ "$F" == *version-manifest* ]]; then
        VERSION=$(grep -E '^gitlab-(ce|ee) ' "$F" | awk '{print $2}' | head -1)
      else
        VERSION=$(cat "$F" | tr -d '[:space:]')
      fi
      [ -n "$VERSION" ] && break
    fi
  done
  if [ -z "$VERSION" ]; then
    echo "UNKNOWN — no local GitLab found; pass URL as \$1"
    exit 2
  fi
fi

echo "[*] GitLab version: ${VERSION}"

MAJOR=$(echo "$VERSION" | cut -d. -f1)
MINOR=$(echo "$VERSION" | cut -d. -f2)

# Post-affected branches
if [ "$MAJOR" -gt 19 ] || { [ "$MAJOR" -eq 19 ] && [ "$MINOR" -gt 5 ]; }; then
  echo "PATCHED — beyond all affected branches"
  exit 0
fi

# Pre-affected
if [ "$MAJOR" -lt 7 ]; then
  echo "PATCHED — predates affected range (< 7.0)"
  exit 0
fi

# Map branch → fixed version
case "${MAJOR}.${MINOR}" in
  18.11) FIXED="18.11.13" ;;
  19.0)  FIXED="19.0.10"  ;;
  19.1)  FIXED="19.1.9"   ;;
  19.2)  FIXED="19.2.9"   ;;
  19.3)  FIXED="19.3.6"   ;;
  19.4)  FIXED="19.4.3"   ;;
  19.5)  FIXED="19.5.1"   ;;
  *)
    # 7.0–18.10.x: no backport exists
    echo "VULNERABLE — ${VERSION} (no backport; upgrade to >=18.11.13)"
    exit 1
    ;;
esac

if version_gte "$VERSION" "$FIXED"; then
  echo "PATCHED — ${VERSION} >= ${FIXED}"
  exit 0
else
  echo "VULNERABLE — ${VERSION} < ${FIXED} (upgrade to ${FIXED})"
  exit 1
fi