Like leaving a master key inside a vending machine that only premium subscribers can use
CVE-2026-87719 is an insecure deserialization flaw (CWE-502) in GitLab EE's GraphQL subscription serializer. An authenticated user who has Duo Chat access can craft a malicious GraphQL subscription argument that bypasses serialization controls and triggers unauthorized server-side object lookup. The result: the attacker obtains Advanced Search (Elasticsearch/OpenSearch) instance configurations and sensitive credentials — connection strings, passwords, API keys — for the search backend. Affected versions span GitLab EE 18.3 through 19.1.7, 19.2.0–19.2.5, and 19.3.0–19.3.1. Fixed in 19.1.8, 19.2.6, and 19.3.2 (released September 10, 2026). Reported by kyyblin via HackerOne.
GitLab scored this CVSS 9.9 CRITICAL with Scope Changed and all-high CIA impact. That vector overstates the real-world risk for most deployments. The 9.9 implies near-RCE with scope escape, but the documented impact is credential disclosure — specifically Advanced Search backend credentials. That's serious, but it's not arbitrary code execution on the GitLab host. More importantly, the attack requires *two* gates: (1) a valid authenticated session, and (2) Duo Chat access, which demands GitLab EE with a Premium/Ultimate license plus a Duo add-on (Core, Pro, or Enterprise). Many EE installations have not purchased or enabled Duo, and CE installations are entirely unaffected. The vendor severity is therefore inflated by roughly one full tier for the majority of self-managed deployments.
5 steps from start to impact.
Obtain authenticated GitLab session
- Valid GitLab EE user account
- Network access to the GitLab instance (internet or internal)
- Many enterprises disable public self-registration on self-managed GitLab
- Internal-only instances require pre-existing network access
Confirm Duo Chat entitlement
duo_chat_requires_licensed_seat feature flag restricts it further. The attacker can verify access by attempting to invoke the Duo Chat interface.- GitLab EE instance has Duo add-on enabled
- User belongs to a Premium/Ultimate group with Duo access
- Duo is a paid add-on — not all EE instances purchase it
- Organizations can restrict Duo Chat to licensed seats only via feature flag
- GitLab CE installations (a large share of self-managed) are entirely unaffected
Craft malicious GraphQL subscription
- Knowledge of GraphQL subscription schema
- Duo Chat websocket/subscription endpoint accessible
- No public PoC available yet (day-zero of disclosure)
- WAF rules inspecting GraphQL payloads may catch anomalous subscription arguments
Exfiltrate Advanced Search credentials
- Target instance has Advanced Search configured with Elasticsearch or OpenSearch
- Advanced Search is a Premium/Ultimate feature that requires explicit setup — not all EE instances use it
- If Elasticsearch uses network-level isolation, stolen creds may not be reachable from the attacker's position
Pivot to Elasticsearch backend
- Network path from attacker to Elasticsearch endpoint
- Elasticsearch credentials are valid and not rotated
- Well-architected deployments isolate Elasticsearch behind private subnets unreachable from the internet
- Credential rotation after detection breaks the chain
The supporting signals.
| In-the-wild exploitation | Not observed. No reports of active exploitation for CVE-2026-87719 as of 2026-09-12. The sibling CVE-2026-85706 (path traversal, CVSS 10.0) is seeing active probes since September 11 per watchTowr, but this deserialization bug has not drawn the same attention. |
|---|---|
| Proof-of-concept | None public. No PoC repos identified on GitHub or exploit-db. Reported by kyyblin via HackerOne bug bounty — details likely embargoed per GitLab's responsible disclosure policy. |
| EPSS score | Not yet scored — CVE disclosed 2026-09-12 (today). Expect initial EPSS within 24–48 hours. |
| KEV status | Not listed on CISA KEV catalog as of 2026-09-12. |
| CVSS vector | CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H — Network-accessible, low complexity, low privilege required, no user interaction, scope changed. The S:C and I:H/A:H components are aggressive given the described impact is credential disclosure rather than RCE. |
| Affected versions | GitLab EE only: 18.3 ≤ v < 19.1.8, 19.2.0 ≤ v < 19.2.6, 19.3.0 ≤ v < 19.3.2. GitLab CE is not affected. |
| Fixed versions | 19.1.8, 19.2.6, 19.3.2 — released September 10, 2026 |
| Exposure data | CyCognito identified >20,000 self-managed GitLab instances reachable on standard web ports via Shodan/Censys. Subset running EE with Duo enabled is significantly smaller. |
| Disclosure date | 2026-09-12 (public advisory); patch released 2026-09-10 |
| Reporter | kyyblin via HackerOne bug bounty program |
noisgate verdict.
The single most decisive factor is the Duo Chat access gate: Duo is a paid add-on that requires a separate license on top of GitLab EE Premium/Ultimate, meaning the reachable attack population is a fraction of the already-limited EE self-managed install base. Despite GitLab's role as a CI/CD and source-code platform warranting a HIGH floor, the compounding prerequisites (authentication + Duo entitlement + Advanced Search configured) and the credential-disclosure-not-RCE impact class do not sustain the vendor's CRITICAL 9.9 rating.
Why this verdict
- Duo Chat gate narrows population significantly. Duo requires a separate paid add-on license (Core/Pro/Enterprise) on top of EE Premium/Ultimate. Many EE deployments have not purchased Duo, and all CE deployments are entirely out of scope. This alone reduces the vulnerable population by an estimated 60–80%.
- Authentication required. The CVSS PR:L means the attacker needs a valid account. Self-managed instances that disable public registration (enterprise default) require credential theft or insider access — a prior compromise stage that compounds downward pressure.
- Impact is credential disclosure, not RCE. The documented outcome is theft of Advanced Search (Elasticsearch) configs and credentials. While serious, this is an information-disclosure primitive that requires a second-stage pivot to Elasticsearch to achieve meaningful data access — not arbitrary code execution on the GitLab server itself. The vendor's I:H and A:H are overweighted.
- Role multiplier: GitLab EE is canonically a CI/CD and source-code management platform — a high-value supply-chain component. If the chain succeeds, stolen Elasticsearch credentials give read access to *all indexed repository content, merge requests, and issues* — potentially including secrets, API keys, and proprietary source code. Blast radius is tenant-to-organization-scale (all projects on the instance). This sets the verdict floor at HIGH and prevents further downgrade despite the friction.
- No exploitation evidence. No KEV listing, no in-the-wild activity, no public PoC. The sibling CVE-2026-85706 is drawing active probes, but this bug is not — reducing urgency relative to CRITICAL.
Why not higher?
The vendor's CRITICAL (9.9) assumes the deserialization leads to full scope-changed compromise with high integrity and availability impact. In practice, the documented impact is credential disclosure for a specific backend service (Elasticsearch), not arbitrary RCE. The Duo Chat prerequisite dramatically narrows the eligible attacker population — this is not a feature available to every authenticated user, but only to those with a paid add-on entitlement. No PoC or in-the-wild activity exists to demonstrate a broader impact than documented.
Why not lower?
GitLab is a canonical CI/CD and supply-chain platform. Even credential disclosure from a GitLab instance carries outsized risk because the Elasticsearch backend indexes all source code, merge requests, and issues — including secrets that developers inevitably commit. A successful chain ends in organization-scale data exposure. Additionally, ~20,000 self-managed instances are internet-facing, the attack complexity is Low, and deserialization bugs historically evolve from info-disclosure to RCE as researchers probe deeper. The HIGH floor is warranted.
What to do — in priority order.
- Restrict Duo Chat to licensed seats immediately — Enable the
duo_chat_requires_licensed_seat(orduo_chat_requires_licensed_seat_smfor self-managed) feature flag to limit Duo Chat to explicitly assigned seats. This shrinks the attack surface to only users with paid Duo seats. Deploy within 30 days per noisgate mitigation SLA for HIGH. - Rotate Advanced Search / Elasticsearch credentials — If you suspect any exposure, rotate Elasticsearch passwords, API keys, and connection strings immediately. Update GitLab's Advanced Search configuration with the new credentials. This breaks any exfiltrated credential chain.
- Network-isolate Elasticsearch backends — Ensure Elasticsearch/OpenSearch clusters are in private subnets unreachable from user-facing networks or the internet. Use firewall rules to allow connections only from the GitLab application servers. This prevents pivoting even if credentials are stolen.
- Audit GraphQL subscription logs — Review GitLab application logs for unusual GraphQL subscription patterns, particularly targeting Duo Chat endpoints. Set up alerts for anomalous subscription argument lengths or unexpected object references.
- Disable public self-registration — If not already done, disable public sign-up on self-managed instances to raise the authentication barrier. This forces attackers to steal or social-engineer credentials before reaching the vulnerability.
- IP-based rate limiting on the GitLab web UI — the exploit requires only a single crafted GraphQL request, not brute-force attempts, so rate limiting provides no meaningful protection.
- GitLab's built-in 2FA/MFA — while 2FA raises the bar for initial authentication, once a valid session exists (including via stolen session tokens or OAuth flows), the deserialization payload works normally within that session.
- Disabling Advanced Search after exploitation — if credentials were already exfiltrated, disabling the feature in GitLab does not invalidate the stolen Elasticsearch credentials. You must rotate them.
Crowdsourced verification payload.
Run this script on the GitLab server (or any host with curl and access to the GitLab API) as any user with shell access. Example: bash check_cve_2026_87719.sh https://gitlab.example.com. No special privileges required — it checks the GitLab version via the public API endpoint.
#!/usr/bin/env bash
# check_cve_2026_87719.sh — Detect GitLab EE instances vulnerable to CVE-2026-87719
# Usage: bash check_cve_2026_87719.sh <GITLAB_URL>
# Exit codes: 0 = PATCHED, 1 = VULNERABLE, 2 = UNKNOWN
set -euo pipefail
if [[ $# -lt 1 ]]; then
echo "Usage: $0 <GITLAB_URL>"
echo "Example: $0 https://gitlab.example.com"
exit 2
fi
GITLAB_URL="${1%/}"
# Fetch version from public API
VERSION_JSON=$(curl -sf "${GITLAB_URL}/api/v4/version" 2>/dev/null || curl -sf "${GITLAB_URL}/api/v4/metadata" 2>/dev/null || true)
if [[ -z "$VERSION_JSON" ]]; then
echo "UNKNOWN — Could not retrieve GitLab version. API may require authentication."
echo "Try: curl -s --header 'PRIVATE-TOKEN: <token>' ${GITLAB_URL}/api/v4/version"
exit 2
fi
VERSION=$(echo "$VERSION_JSON" | grep -oP '"version"\s*:\s*"\K[^"]+' || true)
EDITION=$(echo "$VERSION_JSON" | grep -oP '"edition"\s*:\s*"\K[^"]+' 2>/dev/null || echo "unknown")
if [[ -z "$VERSION" ]]; then
echo "UNKNOWN — Could not parse version from API response."
exit 2
fi
echo "Detected GitLab version: $VERSION (edition: $EDITION)"
# CE is not affected
if echo "$EDITION" | grep -qi 'community\|CE'; then
echo "PATCHED — GitLab Community Edition is not affected by CVE-2026-87719."
exit 0
fi
# Parse major.minor.patch
IFS='.' read -r MAJOR MINOR PATCH <<< "$(echo "$VERSION" | grep -oP '^[0-9]+\.[0-9]+\.[0-9]+')"
if [[ -z "$MAJOR" || -z "$MINOR" || -z "$PATCH" ]]; then
echo "UNKNOWN — Could not parse version components from: $VERSION"
exit 2
fi
# Affected: EE 18.3 <= v < 19.1.8, 19.2.0 <= v < 19.2.6, 19.3.0 <= v < 19.3.2
# Not affected: < 18.3, >= 19.1.8 (on 19.1.x), >= 19.2.6 (on 19.2.x), >= 19.3.2 (on 19.3.x)
VULNERABLE=false
if (( MAJOR < 18 )); then
VULNERABLE=false
elif (( MAJOR == 18 && MINOR < 3 )); then
VULNERABLE=false
elif (( MAJOR == 18 && MINOR >= 3 )); then
VULNERABLE=true
elif (( MAJOR == 19 && MINOR == 0 )); then
VULNERABLE=true
elif (( MAJOR == 19 && MINOR == 1 )); then
if (( PATCH < 8 )); then
VULNERABLE=true
fi
elif (( MAJOR == 19 && MINOR == 2 )); then
if (( PATCH < 6 )); then
VULNERABLE=true
fi
elif (( MAJOR == 19 && MINOR == 3 )); then
if (( PATCH < 2 )); then
VULNERABLE=true
fi
fi
if $VULNERABLE; then
echo "VULNERABLE — GitLab $VERSION is affected by CVE-2026-87719."
echo "Update to 19.1.8, 19.2.6, or 19.3.2 immediately."
exit 1
else
echo "PATCHED — GitLab $VERSION is not in the affected range for CVE-2026-87719."
exit 0
fiIf you remember one thing.
duo_chat_requires_licensed_seat feature flag immediately to restrict the attack surface; (3) rotate your Elasticsearch/OpenSearch credentials as a precaution; (4) schedule the patch deployment for this sprint. Do not wait for the 30-day window if your GitLab instance is internet-facing.Sources
- GitLab Critical Patch Release 19.3.2, 19.2.6, 19.1.8
- CybersecurityNews — GitLab Patches Critical Flaws
- The Hacker News — GitLab CVSS 10 File-Read Flaw Draws In-the-Wild Probes
- BleepingComputer — GitLab urges users to patch max severity path traversal flaw
- SecurityWeek — GitLab Vulnerability Exploited One Day After Disclosure
- GitLab Docs — Duo Chat
- GitLab Docs — Elasticsearch / Advanced Search
- GitLab Forum — Critical Patch Release Announcement
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.