GitLab GraphQL Code Injection — CVE-2026-19478

An unauthenticated attacker abuses a GraphQL directive to rewrite, delete, and forge integrity records on public GitLab CE/EE projects. CVSS 9.4, CWE-94 code injection, exploited in the wild within days of disclosure.
Threat
CVE-2026-19478 · GitLab CE/EE GraphQL
Severity
EXPLOITED ITW
Type
CVE · self-hosted web app · supply-chain adjacent
Access
Unauthenticated, network — POST /api/graphql
Version
v0.1 · 2026-08-25
Author
HuntPack
Confidence
High (GitLab + NVD primary, corroborated by 3 press outlets)
01

Executive Summary

CVE-2026-19478 (CVSS 3.1: 9.4 Critical, CWE-94 — Improper Control of Generation of Code) is an unauthenticated code-injection flaw in the GraphQL layer of self-managed GitLab Community and Enterprise Edition. GitLab shipped an out-of-band critical patch release on 2026-08-17 (versions 19.2.4, 19.1.6, 19.0.8, 18.11.11) after a HackerOne researcher (hiimguardian) reported that a GraphQL directive could be abused, without any credentials, user interaction, or unusual configuration, to remotely modify or delete public projects and user data.

Secondary reporting (WatchTowr, corroborated across multiple press outlets) names the abused mechanism as the @gl_introduced GraphQL directive and describes an attacker's practical reach as broader than the vendor advisory's own wording suggests: deleting entire repositories, forging merge records to make it look as though a fix landed when it did not, and banning or removing project maintainers — all via a single unauthenticated HTTP request. WatchTowr researcher Jake Knott stated the firm reproduced the flaw "within minutes" of disclosure using only the advisory and the patch diff, and its honeypot network caught real-world exploitation attempts within days.

The CVSS 3.1 vector — AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:H/A:H — is the load-bearing technical fact for scoping this pack: Confidentiality is rated Low, Integrity and Availability are rated High. This is a data-integrity and data-destruction vulnerability at the application/repository layer, not a documented remote-code-execution or webshell primitive. No source reviewed for this pack claims OS-level RCE as part of CVE-2026-19478 itself. Any process/network/persistence hunt content in this pack (Section 8, Q2–Q6) targets a secondary, hypothetical follow-on chain — e.g. an attacker who tampers with a public project's CI/CD configuration or merges malicious code that later executes on a build runner — and is explicitly labelled as inferred, not vendor-asserted.

Defender priority: The exploitation string @gl_introduced in an inbound GraphQL request is the single most direct, lowest-false-positive signal for this CVE, but it lives in application/web logs, not default Falcon EDR sensor telemetry — the endpoint sensor does not capture inbound HTTP request bodies to a self-hosted web application. If you have not ingested GitLab, reverse-proxy, or WAF logs into Falcon Next-Gen SIEM (or another log platform), your first control is patching to 19.2.4 / 19.1.6 / 19.0.8 / 18.11.11 and restricting unauthenticated access to /api/graphql at the network edge — not a detection query.

02

Source Review & Web Hunter Notes

Eight sources were fetched and snapshotted verbatim to GitLab-CVE-2026-19478-Hunt-sources/. Every fact and indicator shipped in this pack traces to one of those snapshots.

TierSourceKey findingCarry forward
1 · GovNVD — CVE-2026-19478 detail (fetched)CVSS 3.1 vector/score, CWE-94, affected version ranges, publish/modify datesYes — primary basis for severity and scope
1 · VendorGitLab Docs — official patch release notes (fetched)Release date, exact CVE description, companion CVE-2026-19650, reporter creditYes — primary basis for patch/version facts
2 · PressThe Hacker News — CVE-2026-19478 exploitation report (fetched)WatchTowr attribution, @gl_introduced directive name, hunt guidance (web logs, /api/graphql)Yes — primary basis for the detection angle
2 · PressCSO Online — GitLab flaw article (fetched)Jake Knott (WatchTowr) direct quotes, honeypot exploitation timeline, mitigation checklistYes — corroboration + quotes
2 · PresseSecurity Planet — CVE-2026-19478 article (fetched)Corroborates WatchTowr quotes, supply-chain-risk framing, mitigation checklistYes — corroboration + hardening detail
3 · ResearcherHorizon3.ai — attack research page (fetched)CWE-94 framing, confirms the vendor advisory itself does not name the directive publiclyPartial — corroboration only
3 · ResearcherCyCognito — emerging threat blog (fetched)Impact detail (source, docs, CI/CD config, IaC files), mitigation checklistPartial — corroboration only
4 · AggregateSearch aggregation — CISA KEV status check (fetched/logged)CISA KEV listing and remediation-due-date could not be confirmed by direct fetch (cisa.gov returned HTTP 403 to automated fetch twice)No — gap, not carried forward as fact

Research notes and deliberate exclusions

  • The @gl_introduced directive name is a T2/WatchTowr-attributed technical detail, not verbatim text in the T1 GitLab advisory as fetched. GitLab's own patch-release notes describe "a GraphQL directive" without naming it. The name is corroborated consistently across three independent T2 outlets reporting WatchTowr's findings, so it is carried forward at high confidence, but the distinction is preserved rather than presented as vendor-confirmed language.
  • No file hashes, C2 domains, or IPv4 indicators are shipped in this pack. This CVE is a logic/data-integrity flaw exploited via crafted HTTP requests against application logic, not malware with attacker-controlled infrastructure. Section 4 and Section 10 reflect that honestly rather than manufacturing atomic IOCs to fill a table.
  • CISA KEV status is an explicit gap, not a claim. Multiple secondary aggregators reference KEV activity in the same week, and one search-engine summary conflated this CVE with an unrelated "Ray" framework KEV entry — that conflation was discarded, not carried into this brief. Two direct fetch attempts against cisa.gov (the alert page and the KEV catalog page) both returned HTTP 403. Any KEV-specific claim in this pack is marked as unverified; validate directly against cisa.gov/known-exploited-vulnerabilities-catalog before citing KEV status in a customer-facing document.
  • No RCE is asserted for CVE-2026-19478 itself. The CVSS vector (C:L/I:H/A:H) and every fetched source describe data modification, deletion, and account-access removal — not code execution on the underlying host. Section 8's process/network/persistence queries (Q2–Q6) are explicitly framed as hunting a hypothetical secondary chain, per the task brief for this pack.
  • No exploit code was fetched, reproduced, or included. WatchTowr's reproduction and public PoC availability are referenced descriptively only.
03

Hunt Brief & Attack Chain

Hunt hypotheses (ordered by fidelity)

#HypothesisMITRETelemetryConf
H1Unauthenticated requests to /api/graphql containing the @gl_introduced directive are probing or exploiting CVE-2026-19478T1190Ingested GitLab / reverse-proxy / WAF HTTP logsHIGH
H2A GitLab application-server process (Puma/Sidekiq/Ruby) spawns an unexpected interpreter or shell child following exploitation (secondary/hypothetical — not documented by the CVE itself)T1059.004 (inferred)ProcessRollup2MED
H3A new executable or script is written into a GitLab install/data path by an application-server process (webshell drop) (inferred)T1505.003 (inferred)NewExecutableWritten, ELFFileWrittenMED
H4A GitLab application-server host makes unusual outbound connections following tampering (inferred)T1041 / T1071 (inferred)NetworkConnectIP4MED
H5A GitLab application-server host resolves rare or first-seen domains (inferred)T1071.004 (inferred)DnsRequestLOW
H6An attacker who tampered with a public project's CI/CD configuration establishes persistence on a CI runner host (speculative)T1053.005 / T1543 (inferred)ScheduledTaskRegistered, CreateServiceLOW

Attack chain

StepBehaviorTelemetryHunt angle
1 · ReconAttacker identifies an internet-facing self-managed GitLab instance running an unpatched version (18.2–18.11.10, 19.0–19.0.7, 19.1–19.1.5, or 19.2–19.2.3) with at least one public projectExternal attack-surface / version dataPlaybook 1 (version check)
2 · Initial access / exploitationUnauthenticated POST to /api/graphql containing the @gl_introduced directive triggers CWE-94 code injection in GitLab's GraphQL layerGitLab / reverse-proxy / WAF HTTP logsQ1
3 · Impact — data manipulationAttacker rewrites public project data or forges merge records to fake a fix landingGitLab audit events, Git reflog / object historyNative hunt (Section 7)
4 · Impact — data destructionAttacker deletes public projects or entire repositoriesGitLab audit events, backup diffsNative hunt (Section 7)
5 · Impact — account access removalAttacker bans or removes project maintainersGitLab audit events (membership changes)Native hunt (Section 7)
6 · Secondary (hypothetical) — local executionIf tampering extends to CI/CD config or a merged malicious commit runs on a build runner, an interpreter/shell or dropped script appears on the app or runner hostProcessRollup2, NewExecutableWritten / ELFFileWrittenQ2, Q3
7 · Secondary (hypothetical) — C2 / exfilAnomalous outbound network or DNS activity from the app-server hostNetworkConnectIP4, DnsRequestQ4, Q5
8 · Secondary (hypothetical) — persistenceScheduled task or service creation on a CI/CD runner hostScheduledTaskRegistered, CreateServiceQ6
04

Consolidated IOC Table

CVE-2026-19478 is a logic/data-integrity vulnerability, not malware with attacker-controlled infrastructure. No file hashes, C2 domains, or IPv4 addresses have been published by any source reviewed for this pack. The table below reflects that honestly — see Section 10 for the corresponding Behavioral Signatures and Named-Vulnerability quick-copy blocks.

TypeValueConfActionContext
String / GraphQL directive@gl_introducedHIGHhuntExploitation indicator per WatchTowr — hunt web/proxy/WAF logs for this literal string in requests to /api/graphql
Software version (vulnerable)GitLab CE/EE 18.2–18.11.10, 19.0–19.0.7, 19.1–19.1.5, 19.2–19.2.3HIGHenrichVersion-audit target for self-managed instances (Playbook 1)
Software version (patched)GitLab CE/EE 19.2.4, 19.1.6, 19.0.8, 18.11.11HIGHenrichTarget state for the patch — confirm and verify
CVE identifierCVE-2026-19478 (companion: CVE-2026-19650)HIGHenrichReference / vulnerability-management tracking
File hash(none published)No malware payload is associated with this CVE
Domain / C2(none published)No attacker-controlled infrastructure has been published
IPv4 / IPv6(none published)No attacker-controlled infrastructure has been published
05

Affected Surface & Telemetry Matrix

SurfaceRequired telemetryPriorityGap risk
Self-managed GitLab CE/EE app servers exposing /api/graphql unauthenticated, with public projects enabledGitLab / reverse-proxy / WAF HTTP access logs (ingested to a SIEM)CRITICALHIGH — this is the actual exploitation surface and it is NOT default Falcon EDR telemetry
GitLab.com and GitLab Dedicated tenantsNone — vendor-patched automaticallyLOWLow — customer action not required
GitLab application-server host (Puma/Sidekiq/Ruby processes)ProcessRollup2, SyntheticProcessRollup2, NewExecutableWritten, ELFFileWrittenHIGHMedium — only detects a hypothetical secondary local-execution chain, not the CVE itself
GitLab application-server network egressNetworkConnectIP4, DnsRequestMEDIUMMedium — high baseline noise from legitimate integrations
CI/CD runners executing pipelines from public/tampered projectsScheduledTaskRegistered, CreateService, ProcessRollup2MEDIUMHigh — speculative secondary exposure, not directly tied to the CVE by any source
GitLab audit / application logs (project deletion, membership changes, merge-request state)GitLab Audit Events (native, non-Falcon)CRITICALMedium — audit event streaming may require a paid GitLab tier; see Section 7
06

ATT&CK Mapping

TacticTechniqueObserved behaviorQuery / control
Initial AccessT1190 — Exploit Public-Facing ApplicationUnauthenticated GraphQL directive injection against /api/graphqlQ1 · patch + edge restriction
ImpactT1565.001 — Data Manipulation: Stored Data ManipulationRewritten project data; forged merge records faking a fix landingNative hunt (Section 7) · GitLab audit events
ImpactT1485 — Data DestructionDeletion of public projects / entire repositoriesNative hunt (Section 7) · backup verification
ImpactT1531 — Account Access RemovalProject maintainers banned or removedNative hunt (Section 7) · GitLab audit events
Execution (inferred)T1059.004 — Unix ShellInterpreter/shell spawned by a GitLab app-server process post-tamperQ2
Persistence (inferred)T1505.003 — Web ShellNew executable/script written into GitLab install/data pathQ3
Command and Control (inferred)T1071 / T1041 — C2 & ExfiltrationUnusual outbound connection from the app-server hostQ4
Command and Control (inferred)T1071.004 — DNSRare/first-seen domain resolution from the app-server hostQ5
Persistence (inferred)T1053.005 / T1543 — Scheduled Task / Create or Modify System ProcessTask or service creation on a CI/CD runner hostQ6
Impact (inferred, downstream)T1195.002 — Compromise Software Supply ChainTampered CI/CD config or forged merge history reaching a build/deploy pipelineHardening: protected branches, required approvals (Section 12)
07

Native Audit-Log Hunts

The actual exploitation and its highest-value evidence live in GitLab's own logs, not in EDR telemetry. Run these in parallel with, and ahead of, the CQL hunts in Section 8.

  • GitLab Rails/API access log (/var/log/gitlab/gitlab-rails/api_json.log on an omnibus install, or your ingested equivalent) — grep or SIEM-search for @gl_introduced in requests to /api/graphql, especially from source IPs with no prior authenticated session.
  • GitLab Audit Events (Admin Area > Monitoring > Audit Events, or Audit Event Streaming on Premium/Ultimate) — filter for project deletion, project visibility/data changes, merge-request state changes, and member-removal events around and after the disclosure window (2026-08-17 onward), especially any attributed to an unexpected or anonymous actor.
  • Reverse-proxy / nginx access logs — POST requests to /api/graphql with no corresponding authenticated session cookie or bearer token.
  • WAF / CDN logs — request-body inspection for @gl_introduced, if body logging is enabled.
  • Backup integrity checks — confirm your most recent known-good backup predates any suspected tampering window before relying on it for recovery.
  • Git reflog / object store review — orphaned commits, unexpected force-pushes, or merge commits inconsistent with your normal review workflow are consistent with forged merge records.
  • Software/version inventory — reconcile every self-managed GitLab instance against the patched version list (19.2.4 / 19.1.6 / 19.0.8 / 18.11.11); see Playbook 1 for a scripted check.
08

CrowdStrike LogScale CQL Hunt Queries

Pick your tenant's cloud first — every "Open in Falcon" button below uses this selection.
Q1 · GraphQL directive abuse pattern (@gl_introduced) against /api/graphql
CONF HIGHFP LOWCOST MED

Looks for: the literal @gl_introduced GraphQL directive string in inbound HTTP requests — the most direct signal for this CVE per WatchTowr's technical reporting. Requires non-default telemetry: GitLab / reverse-proxy / WAF logs ingested into Falcon Next-Gen SIEM as a custom log source. The endpoint sensor does not capture inbound HTTP request bodies to a self-hosted web application, so this query returns nothing on a tenant relying on EDR telemetry alone. FP: your own post-patch validation traffic or an authorized vulnerability scan will also match.

// HUNT: GraphQL directive abuse pattern (@gl_introduced) against /api/graphql
// MITRE: T1190
// CONF: high | FP: low | COST: medium
// REQUIRES: GitLab / reverse-proxy / WAF HTTP logs ingested into Falcon NG-SIEM as a custom log source -- NOT default Falcon EDR sensor telemetry
// FALSE POSITIVES: your own patch-validation test traffic; an authorized vulnerability scanner probing the same string
// TUNING: exclude known scanner source IPs and your own post-patch validation test requests once identified
// FIELD NOTE: field names below (host, src_ip, http_user_agent, url, method, status, raw) are generic NG-SIEM / third-party log conventions -- validate the exact field names against YOUR tenant's log-source parser before relying on this query
"@gl_introduced"
| table([@timestamp, host, src_ip, http_user_agent, url, method, status, raw])
Q2 · Interpreter/shell spawned by a GitLab app-server process (secondary/hypothetical)
CONF MEDFP MEDCOST LOW

Looks for: a shell or scripting interpreter spawned by a GitLab application-server process. This hunts a hypothetical secondary chain, not CVE-2026-19478 itself — the CVE is a data-integrity flaw, not documented RCE. FP: GitLab's own Sidekiq/Puma workers legitimately shell out for git hooks, housekeeping tasks, and background jobs.

// HUNT: Interpreter or shell spawned by a GitLab application-server process
// MITRE: T1190, T1059.004 (inferred secondary chain -- not asserted by CVE-2026-19478 itself)
// CONF: medium | FP: medium | COST: low
// REQUIRES: ProcessRollup2, SyntheticProcessRollup2 on the GitLab application-server host(s)
// FALSE POSITIVES: GitLab's own Sidekiq/Puma workers legitimately shell out for git hooks, housekeeping, and background jobs
// TUNING: baseline your own git/gitlab-shell/gitlab-workhorse child processes first and exclude them by CommandLine before promoting
#event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/
| ParentBaseFileName=/^(ruby|puma|unicorn|sidekiq|bundle|gitlab-rails|gitlab-workhorse)(\.exe)?$/i
| FileName=/^(bash|dash|zsh|python[0-9.]*|perl|nc|ncat|socat)(\.exe)?$/i
| table([@timestamp, ComputerName, UserName, ParentBaseFileName, FileName, CommandLine])
Q3 · New executable/script written into GitLab paths (webshell drop pattern)
CONF MEDFP MEDCOST LOW

Looks for: a new executable or script written under GitLab's install/data paths by a GitLab application-server process. Inferred secondary chain — not documented by any source reviewed for this pack. FP: gem/asset compilation and GitLab's own upgrade process legitimately write executables under /opt/gitlab.

// HUNT: New executable or script written into GitLab install/data paths by an app-server process
// MITRE: T1505.003 (inferred secondary chain -- not asserted by CVE-2026-19478 itself)
// CONF: medium | FP: medium | COST: low
// REQUIRES: NewExecutableWritten, ELFFileWritten on the GitLab application-server host(s)
// FALSE POSITIVES: gem/asset compilation and GitLab's own upgrade process legitimately write executables under /opt/gitlab
// TUNING: exclude writes occurring inside a known maintenance/upgrade window (correlate against your change calendar)
#event_simpleName=/^(NewExecutableWritten|ELFFileWritten)$/
| ContextBaseFileName=/^(ruby|puma|unicorn|sidekiq|bundle|gitlab-rails|gitlab-workhorse|nginx)(\.exe)?$/i
| TargetFileName=/(\/opt\/gitlab\/|\/var\/opt\/gitlab\/|\/home\/git\/)/i
| table([@timestamp, ComputerName, ContextBaseFileName, TargetFileName])
Q4 · Unusual outbound connection from a GitLab app-server process
CONF MEDFP HIGHCOST MED

Looks for: outbound connections from GitLab app-server processes to non-RFC1918 addresses, grouped for triage. Inferred secondary chain. FP: license checks, container-registry pulls, webhook/integration calls, git remote mirroring, and outbound email all legitimately leave the app server — expect a noisy baseline until tuned.

// HUNT: Unusual outbound connection from a GitLab application-server process
// MITRE: T1041, T1071 (inferred secondary chain)
// CONF: medium | FP: high | COST: medium
// REQUIRES: NetworkConnectIP4 on the GitLab application-server host(s)
// FALSE POSITIVES: license checks, container-registry pulls, webhook/integration calls, git remote mirroring, outbound email
// TUNING: exclude your known SaaS integrations (webhooks, registry mirrors, license server) by RemoteAddressIP4/RemotePort before alerting
#event_simpleName=/^NetworkConnectIP4$/
| ContextBaseFileName=/^(ruby|puma|unicorn|sidekiq|bundle|gitlab-rails|gitlab-workhorse)(\.exe)?$/i
| !cidr(RemoteAddressIP4, subnet=["10.0.0.0/8","172.16.0.0/12","192.168.0.0/16"])
| groupBy([ComputerName, ContextBaseFileName, RemoteAddressIP4, RemotePort], function=count())
| sort(_count, order=desc)
Q5 · Rare/first-seen DNS resolution from a GitLab app-server process
CONF LOWFP HIGHCOST MED

Looks for: low-frequency domain resolutions from GitLab app-server processes — a weak beaconing signal, not a confirmatory one. Inferred secondary chain. FP: package-manager mirrors, container-registry pulls, and one-off admin troubleshooting all produce low-count domains.

// HUNT: Rare or first-seen DNS resolution from a GitLab application-server process
// MITRE: T1071.004 (inferred secondary chain)
// CONF: low | FP: high | COST: medium
// REQUIRES: DnsRequest on the GitLab application-server host(s)
// FALSE POSITIVES: package-manager mirrors, container-registry pulls, one-off admin troubleshooting
// TUNING: raise the _count threshold and add an allowlist for your package/registry mirrors before treating hits as actionable
#event_simpleName=/^DnsRequest$/
| ContextBaseFileName=/^(ruby|puma|unicorn|sidekiq|bundle|gitlab-rails|gitlab-workhorse)(\.exe)?$/i
| groupBy([ComputerName, DomainName], function=count())
| test(_count <= 3)
| sort(_count, order=asc)
Q6 · Scheduled task / service creation on a CI/CD runner host (speculative)
CONF LOWFP HIGHCOST LOW

Looks for: new scheduled tasks or services registered on hosts named like CI/CD runners — a highly speculative persistence hunt for an attacker who tampered with a public project's pipeline config. Not documented as part of CVE-2026-19478 by any source reviewed. FP: legitimate CI/CD tooling and configuration-management agents register tasks/services constantly on runner hosts.

// HUNT: Scheduled task or service creation on a CI/CD runner host (possible persistence via a forged/tampered pipeline)
// MITRE: T1053.005, T1543 (inferred, speculative -- not documented as part of CVE-2026-19478)
// CONF: low | FP: high | COST: low
// REQUIRES: ScheduledTaskRegistered, CreateService
// FALSE POSITIVES: legitimate CI/CD tooling and configuration-management agents register tasks/services constantly on runner hosts
// TUNING: scope ComputerName to your actual runner fleet naming convention and exclude your CM tool's service account before hunting further
#event_simpleName=/^(ScheduledTaskRegistered|CreateService)$/
| ComputerName=/(runner|ci|build)/i
| table([@timestamp, ComputerName, UserName, TaskName, ServiceName, ServiceImagePath])
09

CrowdStrike Custom IOA Recommendations

No query in this pack is a clean Block-mode Custom IOA candidate. Q1 is the highest-confidence detection but it is a log query (Falcon Next-Gen SIEM Scheduled Search), not sensor-based behavior — Custom IOAs are endpoint-sensor rules and do not apply to ingested HTTP logs. Q2 and Q3 have workable confidence but medium FP from legitimate GitLab internals; Q4–Q6 carry high FP or low confidence and are investigate-only.

QueryPromotion pathActionNotes
Q1Falcon Next-Gen SIEM Scheduled Search (not a Custom IOA)Alert (detect)Primary alerting mechanism for this pack — requires GitLab/proxy/WAF log ingestion first
Q2Custom IOA — Monitor mode onlyMonitorBaseline for 1–2 weeks against your own git-internal child-process patterns before considering Alert mode
Q3Custom IOA — Monitor mode onlyMonitorCorrelate against your GitLab upgrade/maintenance calendar before promoting
Q4, Q5, Q6Investigate-onlyHuntHigh FP / low confidence — not promotion candidates at this time

Alert email package — Q1 (primary detection)

Scheduled Search name: DETECT — GitLab GraphQL @gl_introduced Directive Abuse (CVE-2026-19478)

Description: Fires when the literal GraphQL directive string @gl_introduced appears in ingested GitLab, reverse-proxy, or WAF HTTP logs — the primary indicator of a probe or active exploitation attempt against the unauthenticated CVE-2026-19478 code-injection flaw.

Alert email subject: [CRITICAL] Detection: GitLab CVE-2026-19478 — GraphQL directive abuse (@gl_introduced) observed

Alert email body:
------------------------------------------------------------
A request containing the GraphQL directive string "@gl_introduced" was
observed against /api/graphql on {{ host }} from source {{ src_ip }} at
{{ @timestamp }}.

This string is the primary exploitation indicator for CVE-2026-19478, a
CVSS 9.4 unauthenticated code-injection flaw in GitLab's GraphQL layer
(CWE-94). Successful exploitation can modify or delete public projects,
forge merge records, and remove project maintainers WITHOUT any
credentials or user interaction.

IMMEDIATE ACTIONS:
  1. Confirm the target GitLab instance's patch level. If it is NOT on
     19.2.4 / 19.1.6 / 19.0.8 / 18.11.11 (or later), treat this as an
     active incident, not a hunt lead.
  2. Restrict unauthenticated access to /api/graphql at the reverse
     proxy / WAF immediately (Playbook 2) if patching cannot happen
     within the hour.
  3. Pull GitLab Audit Events for the affected instance covering the
     alert window +/- 24h; look for project deletion, data-visibility
     changes, merge-request state changes, and maintainer removal.
  4. Do NOT assume "no visible damage" means no compromise -- integrity
     tampering (forged merge records) is designed to look clean.

ESCALATION: Platform/Infra on-call -> Security IR lead if step 1 confirms
an unpatched, internet-facing instance with a real hit.
------------------------------------------------------------

Triage checklist

  1. Confirm the alerting request actually targeted /api/graphql and not an unrelated field that happens to contain the string.
  2. Check the target GitLab instance's version against the patched list (Playbook 1).
  3. Identify the source IP — is it a known vulnerability scanner or your own patch-validation tooling? If so, close as benign and add to the Q1 TUNING exclusion.
  4. If the source is unknown/external, pull GitLab Audit Events for the same window and instance.
  5. Check for project deletions, visibility changes, merge-request state changes, or maintainer removals in that window.
  6. If any of the above are found, escalate to Security IR — this is a confirmed exploitation attempt with observable impact, not a probe.
  7. Preserve the raw log lines and any GitLab audit-event export before further remediation (evidence for Section 14, Phase 1).
  8. Regardless of confirmed impact, patch and restrict /api/graphql immediately if not already done.

Pivot queries

Host-scoped: re-run Q2 and Q3 scoped to the same ComputerName as the alerting GitLab instance, over the 24 hours following the Q1 hit, to check for the hypothetical secondary local-execution chain.

Environment-wide: run Q1 across all ingested GitLab instances for the last 30 days (extend the console time picker) to check for earlier, previously undetected probing that predates this alert.

IOC blocklist entries: not applicable — this CVE has no published atomic indicators to block (see Section 4).

Hunt package — Q2 (investigate-only, lighter)

Q2's medium FP means it should not alert unattended. Run it manually against any host that also produced a Q1 hit, or periodically as a hygiene sweep. Pivot: correlate any hit against Q3 (same host/window) — a webshell-style file write alongside an unexpected shell child materially raises confidence over either signal alone.

10

Machine-Readable IOC Appendix

Falcon IOC Management CSVno atomic IOCs for this CVE class
# CVE-2026-19478 is a logic/data-integrity flaw in GitLab's GraphQL layer, not
# malware with attacker-controlled infrastructure. No file hashes, C2 domains, or
# IPv4 addresses have been published by any source reviewed for this pack --
# shipping fabricated atomic IOCs here would fail this pack's own provenance gate.
#
# Track the vulnerability instead by:
#   CVE ID:            CVE-2026-19478 (companion: CVE-2026-19650)
#   CVSS 3.1:           9.4 (AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:H/A:H)
#   CWE:                CWE-94 Improper Control of Generation of Code (Code Injection)
#   Exploitation string: @gl_introduced (GraphQL directive -- see Behavioral Signatures block)
#
# If your own incident response later recovers attacker infrastructure (source
# IPs, follow-on payload hashes, etc.) from a CONFIRMED compromise, add rows here
# in standard type,value,action,severity,expiration,description,tags form and
# re-run check_ioc_provenance.py against a newly saved source snapshot.
Behavioral Signaturesnot importable — hunt these
# CVE-2026-19478 behavioral signatures (no atomic IOC form)
# Exploitation indicator
GraphQL directive string in request body/URI: @gl_introduced
Target endpoint: POST /api/graphql
Requires: NO authentication, NO user interaction (per GitLab advisory + NVD)
# Impact behaviors to hunt in GitLab Audit Events (Section 7)
Public project data modified or deleted without a corresponding authenticated
  admin/maintainer session
Merge request marked merged/closed with no matching reviewer action
  ("forged merge record" pattern per WatchTowr)
Project maintainer removed or banned with no corresponding admin action
# Secondary/hypothetical chain (inferred, not vendor-asserted)
GitLab app-server process (ruby/puma/unicorn/sidekiq/gitlab-rails/gitlab-workhorse)
  spawning bash/dash/zsh/python/perl/nc/ncat/socat
New executable or script written under /opt/gitlab/, /var/opt/gitlab/, or /home/git/
  by a GitLab app-server process
Named Vulnerability & Toolingtriage vocabulary
Primary CVE:     CVE-2026-19478 -- CVSS 3.1 9.4 Critical -- CWE-94
Companion CVE:   CVE-2026-19650 -- CVSS 3.1 7.1 High -- CSRF in GraphQL multiplex
                 query handler (requires user interaction)
Vulnerable:      GitLab CE/EE 18.2 < 18.11.11, 19.0 < 19.0.8,
                 19.1 < 19.1.6, 19.2 < 19.2.4
Patched:         GitLab CE/EE 19.2.4, 19.1.6, 19.0.8, 18.11.11
                 (released 2026-08-17, out-of-band critical patch release)
Not affected:    GitLab.com, GitLab Dedicated (auto-patched)
Reporter:        hiimguardian (HackerOne bug bounty)
Researcher:      WatchTowr (Jake Knott) -- reproduction + honeypot exploitation
Exploited by:    Unknown/unattributed opportunistic scanning observed by WatchTowr
                 honeypot network within days of disclosure
Patch / Version Audit Checklistrun this first
# Version and exposure audit -- run against every self-managed GitLab instance
[ ] Confirm version is 19.2.4 / 19.1.6 / 19.0.8 / 18.11.11 or later (Playbook 1)
[ ] If not yet patched: restrict unauthenticated access to /api/graphql NOW (Playbook 2)
[ ] Inventory which projects on this instance are set to Public visibility (Playbook 3)
[ ] Confirm GitLab Audit Events (or equivalent log export) are being retained/ingested
[ ] Verify last-known-good backup predates 2026-08-17 (pre-disclosure) for comparison
[ ] Confirm whether this instance is GitLab.com / Dedicated (no action) or self-managed (action required)

Import note: this pack ships no rows for Falcon IOC Management by design (see the CSV block above). The importable content here is procedural (patch/version state and audit checklist), not atomic threat infrastructure.

11

Detection Validation Gates

Gate 1 — Telemetry ready

  • Q1 requires log ingestion that most tenants do not have by default. Before treating a zero-hit Q1 result as "clean," confirm GitLab/reverse-proxy/WAF logs are actually flowing into Falcon NG-SIEM (or wherever Q1 runs) — a zero-hit result against an empty log source is not a clean bill of health, it's a blind spot.
  • Confirm ProcessRollup2/SyntheticProcessRollup2 and NewExecutableWritten/ELFFileWritten are populated for your GitLab application-server hosts specifically (not just Windows fleet defaults — these are typically Linux hosts).
  • Confirm ScheduledTaskRegistered/CreateService coverage on your CI/CD runner fleet before relying on Q6.

Gate 2 — Benign baseline

  • Run Q2, Q3, Q4, and Q5 over 7 days with no exclusions and record hit counts before promoting any to Monitor mode. Expect Q4 and Q5 to be noisy — that noise is the baseline you tune against, not a false alarm to dismiss.
  • Q1 should return zero in a clean environment absent scanning activity; a non-zero baseline needs individual triage (Section 9 triage checklist), not blanket suppression.

Gate 3 — Positive tests

  • In a non-production log source, submit a benign test event containing the literal string @gl_introduced and confirm Q1 fires end-to-end (ingestion → search → alert).
  • On a lab GitLab host, manually spawn a shell from a process named like one of the Q2 parent patterns (e.g. a test script invoked as ruby) and confirm Q2 fires; clean up afterward.

Gate 4 — Promotion criteria

  • Q1 promotes to an active Scheduled Search alert only after Gate 1 (telemetry confirmed present) and Gate 3 (positive test fired) both pass.
  • Q2/Q3 promote from Monitor to Alert only after 7+ days at a stable, explained baseline (Gate 2) with your GitLab-internal exclusions applied.
  • Do not promote Q4, Q5, or Q6 to alerting in this release — they remain investigate-only pending environment-specific tuning.
12

Hardening — Tiered

Immediate — deploy this week
  • Patch every self-managed GitLab CE/EE instance to 19.2.4 / 19.1.6 / 19.0.8 / 18.11.11 (or later) (MITRE M1051 · vendor advisory). This is a critical, out-of-band release with confirmed in-the-wild exploitation — do not wait for the next normal patch window. Verify with Playbook 1.
  • Restrict unauthenticated access to /api/graphql at the reverse proxy or WAF for any instance that cannot be patched within the hour (MITRE M1030 · M1037 — Network Segmentation / Filter Network Traffic). Deploy with Playbook 2.
  • Audit and reduce public project visibility where it is not operationally required (GitLab visibility settings; no formal CIS benchmark covers this GitLab-specific control — ⚠ best-practice, no formal benchmark). Run the read-only inventory in Playbook 3.
Near term — 1 to 4 weeks
  • Ingest GitLab audit events and/or reverse-proxy access logs into your SIEM (MITRE M1047 — Audit; no formal CIS benchmark for this GitLab-specific control — ⚠ best-practice, no formal benchmark). This is a hard prerequisite for Q1 in Section 8 and for the native hunts in Section 7 — without it, this CVE class is effectively invisible to your detection stack.
  • Deploy a temporary WAF virtual-patch rule blocking/alerting on @gl_introduced in requests to /api/graphql as a compensating control during the patch rollout window (MITRE M1037). See Playbook 4. Retire once all instances are confirmed patched.
  • Review and harden your backup cadence and immutability for GitLab data (MITRE M1053 — Data Backup). This vulnerability's blast radius explicitly includes destructive deletion; recovery depends on backups the attacker cannot also tamper with.
Strategic — 1 to 3 months
  • Network-segment CI/CD runners and GitLab admin interfaces away from general-purpose network access (MITRE M1030). Limits the blast radius of the speculative CI/runner persistence chain in Q6.
  • Establish a formal patch-cadence SLA for GitLab self-managed critical/high advisories — subscribe to GitLab's security-release channel and commit to a defined response window (e.g. 72 hours for Critical). ⚠ best-practice, no formal benchmark
  • Enforce protected branches, required merge approvals, and (where supported) commit signing on public/high-value projects (MITRE M1047 · GitLab protected-branch settings). Directly reduces the blast radius of the forged-merge-record and CI/CD supply-chain risk described in Section 6 (T1195.002).
13

Deployable Playbooks

1 — Version verification (shell)

# Check current GitLab version on an omnibus install
sudo gitlab-rake gitlab:env:info 2>/dev/null | grep -A2 "GitLab information"

# Alternative -- read the version manifest directly
cat /opt/gitlab/version-manifest.txt | grep -i gitlab-ce

# PASS if version is 19.2.4 / 19.1.6 / 19.0.8 / 18.11.11 or later.
# FAIL (patch immediately) if version is in any of:
#   18.2 - 18.11.10, 19.0 - 19.0.7, 19.1 - 19.1.5, 19.2 - 19.2.3

Prerequisites: SSH/admin access to the GitLab omnibus host. Reboot required: no (read-only check). Rollback: none required — this is a read-only verification step.

2 — Restrict /api/graphql at the reverse proxy (nginx, temporary compensating control)

# /etc/nginx/conf.d/gitlab-graphql-restrict.conf
# Place inside (or alongside) the existing GitLab server block, then reload.
location /api/graphql {
    # Restrict to known internal/CI ranges only -- adjust to your environment.
    allow 10.0.0.0/8;
    allow 172.16.0.0/12;
    deny all;
}
sudo nginx -t && sudo nginx -s reload

Prerequisites: nginx (or equivalent reverse proxy) terminating in front of GitLab; admin access to edit its config. Reboot required: no. Rollback: remove the location /api/graphql block from the config file, then run sudo nginx -t && sudo nginx -s reload again.

3 — Public-project visibility audit (GitLab Rails console, read-only)

# Run from: sudo gitlab-rails console
Project.where(visibility_level: Gitlab::VisibilityLevel::PUBLIC).pluck(:id, :path_with_namespace)

Prerequisites: admin access to gitlab-rails console on the omnibus host. Reboot required: no. Rollback: none required — this is a read-only query; no state is changed. Changing a project's visibility afterward is a separate, deliberate admin action taken outside this playbook.

4 — Temporary WAF virtual-patch rule for @gl_introduced (ModSecurity example)

# /etc/modsecurity.d/gitlab-cve-2026-19478-virtual-patch.conf
SecRule REQUEST_URI "@streq /api/graphql" \
    "chain,id:1000019478,phase:2,deny,log,msg:'Blocked possible CVE-2026-19478 GraphQL directive abuse'"
    SecRule REQUEST_BODY "@contains @gl_introduced" "t:none"
sudo apachectl configtest && sudo systemctl reload apache2
# or, for an nginx+ModSecurity install:
sudo nginx -t && sudo nginx -s reload

Prerequisites: ModSecurity (or an equivalent WAF engine) in the request path in front of GitLab, with request-body inspection enabled. Reboot required: no (WAF config reload only). Rollback: remove or disable rule id 1000019478 and reload the WAF engine. This is explicitly a temporary compensating control — retire it once every instance is confirmed on a patched version (Playbook 1).

14

Containment Runbook

PhaseActionsOwnerEvidence to capture
1 · Isolate
0–1 h
Restrict or temporarily disable unauthenticated access to /api/graphql at the edge (Playbook 2/4) on the affected instance while investigating. Do not take the instance fully offline unless data destruction is confirmed in progress.Infra/Platform on-callFirewall/WAF change ticket, timestamp, Q1 alert detail
2 · Confirm
0–2 h
Verify current patch level (Playbook 1). Pull GitLab Audit Events for the suspected window; confirm whether project deletion, data-visibility changes, merge-request state changes, or maintainer removal actually occurred.Security IRVersion-check output, exported audit events, affected project list
3 · Scope
1–4 h
Enumerate every self-managed GitLab instance in the environment; re-run Playbook 1 and the Section 10 audit checklist against each. Run Q1 environment-wide over the last 30 days.Threat huntingInstance inventory, version audit results, Q1 hit list
4 · Eradicate
2–24 h
Patch every affected instance to 19.2.4 / 19.1.6 / 19.0.8 / 18.11.11 (or later). Rotate any CI/CD variables, deploy tokens, or credentials that were reachable from a tampered project.Platform eng.Patch confirmation, credential-rotation log
5 · Recover
1–3 d
Restore any deleted repositories from a backup confirmed to predate the tampering window (Playbook 1's version check + Section 7's backup-integrity note). Re-verify merge history for forged/backdated merge records; reinstate wrongfully removed maintainers.Platform eng. + repo ownersRestore log, merge-history diff, maintainer-reinstatement record
6 · Notify
1–5 d
If public-repo data integrity was affected, notify downstream consumers (contributors, CI/CD consumers of the affected repo, package registries mirroring it) so they can re-verify what they pulled during the tampering window.IR lead + repo ownersNotification record, list of downstream consumers contacted
7 · Harden
1–4 w
Execute the Section 12 Immediate and Near-term tiers across the full GitLab fleet, not just the affected instance. Feed lessons learned into your patch-cadence SLA.Security eng.Change records, log-ingestion confirmation, SLA document update
15

Detection Coverage Map

TechniqueBehaviorCQLIOACoverage
T1190Unauthenticated GraphQL directive injection against /api/graphqlQ1— (SIEM alert, not IOA)PARTIAL — correct query, but requires log ingestion most tenants lack by default
T1565.001Stored data manipulation / forged merge recordsGAP — no Falcon telemetry; covered only by GitLab Audit Events (Section 7)
T1485Public project / repository deletionGAP — no Falcon telemetry; covered only by GitLab Audit Events + backups (Section 7)
T1531Maintainer banned/removedGAP — no Falcon telemetry; covered only by GitLab Audit Events (Section 7)
T1059.004 (inferred)Interpreter/shell spawned by GitLab app-server processQ2Monitor-onlyPARTIAL — medium FP, hypothetical secondary chain
T1505.003 (inferred)Webshell-style file write into GitLab pathsQ3Monitor-onlyPARTIAL — medium FP, hypothetical secondary chain
T1041 / T1071 (inferred)Unusual outbound connection from app serverQ4PARTIAL — high FP, needs environment-specific tuning
T1071.004 (inferred)Rare DNS resolution from app serverQ5PARTIAL — weak signal, high FP
T1053.005 / T1543 (inferred, speculative)Persistence on CI/CD runner hostQ6GAP — speculative, high FP, not tied to the CVE by any source
T1195.002 (inferred, downstream)Supply-chain reach via tampered CI/CD configGAP — addressed only by hardening controls (Section 12), not detection

This pack's most important honesty point is here: the CVE's actual documented impact (T1565.001 / T1485 / T1531) has zero Falcon EDR coverage, because it is an application-layer data-integrity attack, not an endpoint compromise. Detection depends entirely on GitLab's own audit logging (Section 7) and, for the @gl_introduced string specifically, on ingesting web/proxy/WAF logs into a SIEM (Q1). The endpoint-telemetry queries (Q2–Q6) hunt a hypothetical secondary chain and should not be mistaken for coverage of the CVE itself. Clear Gate 1 in Section 11 — especially the log-ingestion prerequisite — before treating a zero-hit Q1 result as a clean bill of health.

16

Hunt Summary Ticket

TITLE:      Hunt -- CVE-2026-19478 GitLab unauthenticated GraphQL code injection
            (@gl_introduced directive abuse)
SEVERITY:   Critical -- CVSS 9.4, unauthenticated, exploited in the wild within
            days of disclosure. Self-managed GitLab CE/EE only; GitLab.com and
            Dedicated are already patched.
SCOPE:      Every self-managed GitLab CE/EE instance not yet on 19.2.4 / 19.1.6 /
            19.0.8 / 18.11.11. Priority: internet-facing instances with public
            projects enabled.

HYPOTHESIS: An unauthenticated attacker sent a crafted GraphQL request containing
            the @gl_introduced directive to /api/graphql, triggering a CWE-94 code
            injection that allowed rewriting or deleting public project data,
            forging merge records, and/or removing maintainers -- all without any
            credentials or user interaction. This is a DATA-INTEGRITY /
            DATA-DESTRUCTION vulnerability, not documented RCE.

QUERIES:    Q1  @gl_introduced directive string in ingested GitLab/proxy/WAF logs
                <-- MOST DIRECT SIGNAL, but requires non-default log ingestion
            Q2  Interpreter/shell spawned by a GitLab app-server process (secondary)
            Q3  New executable/script written into GitLab paths (secondary)
            Q4  Unusual outbound connection from a GitLab app-server process (secondary)
            Q5  Rare/first-seen DNS resolution from a GitLab app-server process (secondary)
            Q6  Scheduled task/service creation on a CI/CD runner host (speculative)

DO FIRST:   1. Run Playbook 1 (version check) against every self-managed GitLab
               instance. Patch anything not already on 19.2.4/19.1.6/19.0.8/18.11.11.
            2. If patching cannot happen within the hour, restrict /api/graphql
               at the edge (Playbook 2) or deploy the WAF virtual-patch (Playbook 4).
            3. Confirm whether GitLab Audit Events / access logs are being
               retained or ingested (Section 11, Gate 1) -- if not, Q1 is blind
               and Section 7's native hunts are your only detection path.

FINDINGS:   [ ] Instances confirmed patched .......................... ____
            [ ] Instances confirmed vulnerable (action required) ...... ____
            [ ] Q1 hits (log-based) ................................... ____
            [ ] GitLab Audit Events showing unexplained deletion/
                data-manipulation/maintainer-removal ................. ____
            [ ] Public projects inventoried (Playbook 3) .............. ____

GAPS:       The CVE's documented impact (data manipulation, deletion, account
            access removal) has NO Falcon EDR telemetry coverage -- it is visible
            only in GitLab's own audit/access logs. Q2-Q6 hunt a hypothetical
            secondary local-execution/persistence chain that is NOT asserted by
            any source reviewed for this pack. CISA KEV status could not be
            independently confirmed (cisa.gov blocked automated fetch).

ACTIONS:    [ ] Patch every self-managed instance (playbook 1)
            [ ] Restrict /api/graphql at the edge where unpatched (playbook 2)
            [ ] Audit public project visibility (playbook 3)
            [ ] Deploy temporary WAF virtual-patch if needed (playbook 4)
            [ ] Ingest GitLab audit/access logs into the SIEM (Section 12, Near term)
            [ ] Verify backup integrity predates 2026-08-17

OWNER:      ____________________
VERSION:    v0.1 (2026-08-25)
17

Changelog

v0.1 2026-08-25 Initial pack. Built from GitLab's official patch-release notes and NVD (both fetched 2026-08-17 publish date), corroborated by three independent press outlets reporting WatchTowr's technical analysis, plus two researcher blogs. 6 CQL queries (1 log-based direct signal, 5 host-telemetry queries hunting an explicitly-labelled hypothetical secondary chain). No atomic file/network IOCs shipped — this CVE class has none published. CISA KEV status flagged as unconfirmed (cisa.gov blocked automated fetch twice). All facts traced to eight saved source snapshots.
18

References

TierSourceUsed forPublishedAccessed
1NVD — CVE-2026-19478CVSS vector/score, CWE-94, affected version ranges2026-08-172026-08-25
1GitLab Docs — Critical Patch Release 19.2.4 / 19.1.6 / 19.0.8 / 18.11.11Official CVE description, companion CVE-2026-19650, reporter credit2026-08-172026-08-25
2The Hacker News — GitLab CVE-2026-19478 Comes Under Active ExploitationWatchTowr attribution, @gl_introduced directive name, hunt guidance2026-08-212026-08-25
2CSO Online — Critical GitLab flaw allows attackers to delete and modify public reposWatchTowr / Jake Knott direct quotes, exploitation timeline~2026-08-18/212026-08-25
2eSecurity Planet — GitLab Patches Critical CVE-2026-19478 GraphQL VulnerabilityCorroborating quotes, supply-chain framing, mitigation checklist~2026-08-18/212026-08-25
3Horizon3.ai — CVE-2026-19478 attack researchCWE-94 framing, confirms directive name absent from vendor advisory text~2026-08-18/212026-08-25
3CyCognito — Emerging Threat: CVE-2026-19478Impact detail (source, docs, CI/CD config, IaC files), mitigation checklist~2026-08-18/212026-08-25
RefMITRE ATT&CKTechnique and mitigation mapping2026-08-25

Provenance: the fetched text of all eight sources is saved alongside this pack in GitLab-CVE-2026-19478-Hunt-sources/. This pack ships no atomic file/network IOCs, so the provenance gate (check_ioc_provenance.py) has nothing to trace and passes vacuously — every factual claim in this pack is nonetheless attributable to one of the eight numbered sources above.

Unresolved gap: CISA Known Exploited Vulnerabilities (KEV) catalog status for CVE-2026-19478 could not be independently confirmed — cisa.gov returned HTTP 403 to automated fetch on both the alert page and the KEV catalog page. Validate directly at cisa.gov/known-exploited-vulnerabilities-catalog before making a KEV-status claim in a customer-facing report.