GitLab Critical API Exploitation
An adversary is exploiting unauthenticated path traversal in the GitLab repository commits API to read server configuration or using insecure deserialization in Duo Chat to extract sensitive credentials.
Based on research by Rapid7 2026-09-29 11 steps · 3 queries T1190 T1552.004
Brief
Why now
Rapid7 recently detailed active exploitation of CVE-2026-85706, a critical path traversal vulnerability in GitLab. This flaw allows unauthenticated attackers to read arbitrary files from the server. Simultaneously, GitLab addressed CVE-2026-87719, an insecure deserialization issue in Duo Chat that can lead to credential extraction. Because these vulnerabilities target self-managed instances and offer a direct path to application secrets, we are publishing a hunt to identify both exposure and active exploitation.
How the hunt flows
The hunt begins with a scoping phase using software inventory data. The first query identifies every host running GitLab and records the specific package version. This provides a baseline of the estate's exposure without yet processing heavy traffic logs.
An agent then assesses these versions against the vulnerable ranges specified in the advisory: 18.7 to 19.1.7, 19.2 to 19.2.5, and 19.3 to 19.3.1. If the environment contains no vulnerable versions, the hunt concludes. This gate ensures that deep behavioral analysis only occurs on high-risk systems.
When the hunt identifies a vulnerable host, it executes two parallel queries against HTTP activity logs. The first query searches for directory traversal sequences like ../ or encoded variations, specifically targeting sensitive paths such as gitlab.rb or database.yml. The second query uses stack-counting to identify rare GraphQL or Duo Chat API paths that differ from standard fleet traffic, highlighting potential subscription-based exploitation.
Finally, an agent triages the results by correlating the version risk with the observed HTTP telemetry. The agent issues a verdict for each host, distinguishing between successful file retrieval and unsuccessful probes.
What the hunt cannot see
This hunt relies on endpoint-based software inventory and HTTP telemetry. It faces two primary blind spots. First, unmanaged GitLab instances—those without an agent—remain invisible and will not appear in the scoping results. Second, because most HTTP telemetry does not capture the full POST body, the hunt cannot see the specific arguments or payloads within GraphQL subscriptions. We can identify that a rare API endpoint was used, but not exactly what data the adversary requested.
Steps
-
Find GitLab instances and versions
Query · scopingIdentify every self-managed GitLab installation in the estate and record its version to assess exposure to CVE-2026-85706.
reads hb_software_inventorysqlSELECT device_hostname, package_name, package_version, vendor_name FROM hb_software_inventory WHERE (LOWER(package_name) LIKE '%gitlab%' OR LOWER(vendor_name) LIKE '%gitlab%') AND asset_scope = 'endpoint'What a hit looks like. A list of hosts running GitLab with their current versions. Silence indicates no GitLab software is installed on agent-enrolled hosts.
-
Assess version vulnerability
Agent triageDetermine if the identified GitLab versions are within the vulnerable ranges specified in the Rapid7 advisory.
-
Gate: Proceed to behavioral analysis?
DecisionAvoid analyzing large volumes of HTTP telemetry if no vulnerable GitLab instances are present.
-
Detect path traversal behavior
Query · detection candidateIdentify HTTP requests containing traversal sequences or targeting sensitive configuration files.
reads hb_http_activitysqlSELECT device_hostname, src_endpoint_ip, url_path, url_full, status_code, time FROM hb_http_activity WHERE ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND (instr(url_full, '../') > 0 OR instr(url_full, '..%2f') > 0 OR instr(',' || '{{sensitive_files}}' || ',', ',' || url_path || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days')What a hit looks like. HTTP requests targeting /etc/passwd or gitlab.rb. Status 200 OK on these paths indicates successful file retrieval.
-
Identify rare API path usage
Query · baselineUse stack-counting to identify anomalous GraphQL or Duo Chat API paths that differ from standard fleet traffic.
reads hb_http_activitysqlSELECT url_path, COUNT(DISTINCT device_hostname) AS hosts, COUNT(*) AS requests, MIN(time) AS first_seen FROM hb_http_activity WHERE ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND url_path LIKE '%/api/%' AND time >= datetime('now', '-{{lookback_days}} days') GROUP BY url_path HAVING hosts <= 2 ORDER BY hosts ASCWhat a hit looks like. API endpoints used on only one or two hosts, highlighting potential exploitation of GraphQL subscriptions.
-
Triage exploitation evidence
Agent triageCorrelate identified vulnerable versions with behavioral artifacts to confirm exploitation.
-
Route on triage verdict
DecisionDirect the response based on the agent's findings of confirmed compromise.
-
Isolate GitLab server
Response actionContain the GitLab server to prevent further data exfiltration or lateral movement.
-
Analyst forensic review
Analyst taskManually verify findings where status codes or URL patterns were ambiguous.
-
Close out and remediate
Analyst taskFinalize the hunt and ensure all vulnerable GitLab instances are upgraded.
Coverage
Scenario coverage
| Stage | Covered | How, or why not |
|---|---|---|
| GitLab Vulnerability Presence T1190 |
Yes | find-gitlab-versions, assess-vulnerability |
| Unauthenticated API Path Traversal T1190 |
Yes | detect-path-traversal |
| Duo Chat GraphQL Credential Access T1190 · T1552.004 |
Yes | rare-api-usage |
Blind spots
- Needs hb_software_inventory coverage for shadow IT. An unmanaged GitLab instance will not be identified as vulnerable and may be exploited without appearing in this hunt's results. It would answer Are there unmanaged GitLab instances missing an endpoint agent?.
- Needs HTTP POST body inspection. Malicious GraphQL subscriptions are carried in the POST body; without body inspection, we can only see the endpoint name and not the specific data extracted. It would answer What specific arguments were inside the GraphQL POST requests?.
Parameters & data
Parameters
| Parameter | Type | Default | What it is |
|---|---|---|---|
lookback_days | number | 14 | Days of history to examine for behavioral signals. |
scope_hosts | list[host] | — | Hostnames identified as running GitLab in the scoping step. |
sensitive_files | list[path] | /etc/passwd, gitlab.rb, database.yml | Target files for path traversal exploitation. |
Telemetry
| Source | Category | Telemetry |
|---|---|---|
| Endpoint telemetry (hb_ surfaces) | endpoint | endpoint |
| Web server / proxy logs | siem | network |
Source
---
analysis: This hunt uses a version-based gate to focus deep behavioral analysis and
stack-counting only on vulnerable hosts, allowing for higher fidelity detection
of stealthy API-based credential extraction than a simple pattern match.
blind_spots:
- id: inventory-visibility-gap
question: Are there unmanaged GitLab instances missing an endpoint agent?
requires: hb_software_inventory coverage for shadow IT
risk: An unmanaged GitLab instance will not be identified as vulnerable and may
be exploited without appearing in this hunt's results.
stage: vulnerability-exposure-assessment
- id: http-body-blindness
question: What specific arguments were inside the GraphQL POST requests?
requires: HTTP POST body inspection
risk: Malicious GraphQL subscriptions are carried in the POST body; without body
inspection, we can only see the endpoint name and not the specific data extracted.
stage: insecure-deserialization-credential-access
coverage:
- stage: vulnerability-exposure-assessment
status: covered
steps:
- find-gitlab-versions
- assess-vulnerability
- stage: unauthenticated-path-traversal
status: covered
steps:
- detect-path-traversal
- stage: insecure-deserialization-credential-access
status: covered
steps:
- rare-api-usage
guardrails:
claims: no_unsupported
evidence: citation_required
missing_data: not_benign
telemetry: untrusted
hunt:
applicability: campaign-specific
handoff: promote-to-detection
justification: CVE-2026-85706 is a CVSS 10.0 vulnerability actively exploited in
the wild to steal application secrets. Confirming that all GitLab instances are
patched is a critical control for protecting code repositories.
methodology: model-assisted
trigger: intel-report
hypothesis: An adversary is exploiting unauthenticated path traversal in the GitLab
repository commits API to read server configuration or using insecure deserialization
in Duo Chat to extract sensitive credentials.
labels:
- hunt
- attack.t1190
- attack.t1552.004
- credential access
- initial access
name: GitLab Critical API Exploitation
parameters:
lookback_days:
default: '14'
description: Days of history to examine for behavioral signals.
from:
kind: manual
observed: '2026-09-14'
ref: hunt-standard-lookback
type: number
scope_hosts:
default: []
description: Hostnames identified as running GitLab in the scoping step.
from:
kind: manual
observed: '2026-09-14'
ref: scoping-step
type: list[host]
sensitive_files:
default:
- /etc/passwd
- gitlab.rb
- database.yml
description: Target files for path traversal exploitation.
from:
kind: article
observed: '2026-09-14'
ref: rapid7-gitlab-etr
type: list[path]
provenance:
authors:
- name: Huntbase hunt generation
org: huntbase.io
generated:
by: huntbase-hunt-generation
from: https://www.rapid7.com/blog/post/etr-cve-2026-85706-critical-gitlab-path-traversal-exploited-in-the-wild
gates:
- dry-run
- lint
- critic
model: hb_google/gemini-3-flash-preview
rationale: Focus on self-managed GitLab Community and Enterprise edition servers.
Use software inventory to establish the high-risk scope first.
references:
- name: "Rapid7 \u2014 CVE-2026-85706: Critical GitLab Path Traversal Exploited in\
\ the Wild"
url: https://www.rapid7.com/blog/post/etr-cve-2026-85706-critical-gitlab-path-traversal-exploited-in-the-wild
related:
- hunt: gitlab-web-shell-activity
reason: Successful file reading often precedes the deployment of web shells.
relation: follows
scenario:
stages:
- name: GitLab Vulnerability Presence
observables:
- GitLab Community Edition versions 18.7 to 19.1.7
- GitLab Enterprise Edition versions 19.2 to 19.2.5
- GitLab Enterprise Edition versions 19.3 to 19.3.1
slug: vulnerability-exposure-assessment
tactic: initial-access
techniques:
- T1190
- name: Unauthenticated API Path Traversal
observables:
- HTTP requests to /api/v4/projects/:id/repository/commits
- Path traversal sequences (../) in repository API parameters
- Requests targeting /etc/passwd or gitlab.rb configuration files
slug: unauthenticated-path-traversal
tactic: initial-access
techniques:
- T1190
- name: Duo Chat GraphQL Credential Access
observables:
- Specially crafted GraphQL subscription arguments
- Duo Chat API interaction
- Extraction of Advanced Search instance configurations
- Access to sensitive GitLab credentials
slug: insecure-deserialization-credential-access
tactic: credential-access
techniques:
- T1190
- T1552.004
summary: Unauthenticated attackers are exploiting a critical path traversal vulnerability
(CVE-2026-85706) in the GitLab repository commits API to read arbitrary system
files. A secondary vulnerability (CVE-2026-87719) allows authenticated users with
Duo Chat access to extract sensitive credentials and configurations via insecure
deserialization in GraphQL subscriptions.
severity: high
targets:
analyst:
name: Tier-2 analyst
role: analyst
endpoint:
category: endpoint
name: Endpoint telemetry (hb_ surfaces)
telemetry:
- endpoint
hunter:
agent: true
name: Hunt agent
web:
category: siem
name: Web server / proxy logs
telemetry:
- network
tlp: clear
type: investigation
---
# GitLab Critical API Exploitation
The adversary attempts to exploit critical path traversal and deserialization vulnerabilities in self-managed GitLab instances to steal configuration files and credentials. This hunt targets CVE-2026-85706 and CVE-2026-87719 by first identifying exposed versions within the software inventory. If vulnerable versions are confirmed, it triggers a deep behavioral analysis of HTTP telemetry to find direct directory traversal sequences, access to sensitive files like gitlab.rb, and anomalous GraphQL API patterns associated with Duo Chat. An agent weighs the version risk against the observed traffic to confirm exploitation.
## find-gitlab-versions
<!-- Find GitLab instances and versions -->
Identify every self-managed GitLab installation in the estate and record its version to assess exposure to CVE-2026-85706.
```sqlite target=endpoint role=scoping
~~~yaml
expected: A list of hosts running GitLab with their current versions. Silence indicates
no GitLab software is installed on agent-enrolled hosts.
reads:
- device_hostname
- package_name
- package_version
silence: not_evidence_of_absence
source: hb_software_inventory
verified: dry-run
verified_at: '2026-09-29'
~~~
SELECT device_hostname, package_name, package_version, vendor_name FROM hb_software_inventory WHERE (LOWER(package_name) LIKE '%gitlab%' OR LOWER(vendor_name) LIKE '%gitlab%') AND asset_scope = 'endpoint'
```
## assess-vulnerability
<!-- Assess version vulnerability -->
```agent target=hunter
cite: required
context:
- find-gitlab-versions
max_iterations: 3
objective: Determine if any host is running GitLab versions 18.7 to 19.1.7, 19.2 to
19.2.5, or 19.3 to 19.3.1 based on the scoping query results.
success_criteria: A verdict for each host citing its version relative to the advisory.
tools:
- endpoint
- web
```
## gate-on-vulnerability
<!-- Gate: Proceed to behavioral analysis? -->
if~: "the assessment identifies at least one host running a vulnerable GitLab version" (confidence: high, judge=hunter)
then: → exploitation-fan-out
indeterminate: → analyst-review
unavailable: → analyst-review (blind_spot: inventory-visibility-gap)
else: → close-out
## exploitation-fan-out
<!-- Simultaneous exploitation scan -->
parallel:
- → detect-path-traversal
- → rare-api-usage
join: → triage-exploitation
## detect-path-traversal
<!-- Detect path traversal behavior -->
Identify HTTP requests containing traversal sequences or targeting sensitive configuration files.
```sqlite target=web role=detection-candidate params=(scope_hosts=scope_hosts, sensitive_files=sensitive_files, lookback_days=lookback_days)
~~~yaml
expected: HTTP requests targeting /etc/passwd or gitlab.rb. Status 200 OK on these
paths indicates successful file retrieval.
reads:
- url_path
- url_full
- status_code
- src_endpoint_ip
silence: not_evidence_of_absence
source: hb_http_activity
verified: dry-run
verified_at: '2026-09-29'
~~~
SELECT device_hostname, src_endpoint_ip, url_path, url_full, status_code, time FROM hb_http_activity WHERE ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND (instr(url_full, '../') > 0 OR instr(url_full, '..%2f') > 0 OR instr(',' || '{{sensitive_files}}' || ',', ',' || url_path || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days')
```
## rare-api-usage
<!-- Identify rare API path usage -->
Use stack-counting to identify anomalous GraphQL or Duo Chat API paths that differ from standard fleet traffic.
```sqlite target=web role=baseline params=(scope_hosts=scope_hosts, lookback_days=lookback_days)
~~~yaml
baseline:
compare: first_seen
window: '{{lookback_days}}d'
expected: API endpoints used on only one or two hosts, highlighting potential exploitation
of GraphQL subscriptions.
prevalence:
by: device_hostname
key:
- url_path
rare_below: 3
reads:
- url_path
silence: not_evidence_of_absence
source: hb_http_activity
verified: dry-run
verified_at: '2026-09-29'
~~~
SELECT url_path, COUNT(DISTINCT device_hostname) AS hosts, COUNT(*) AS requests, MIN(time) AS first_seen FROM hb_http_activity WHERE ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND url_path LIKE '%/api/%' AND time >= datetime('now', '-{{lookback_days}} days') GROUP BY url_path HAVING hosts <= 2 ORDER BY hosts ASC
```
## triage-exploitation
<!-- Triage exploitation evidence -->
```agent target=hunter
cite: required
context:
- assess-vulnerability
- detect-path-traversal
- rare-api-usage
max_iterations: 6
objective: Confirm whether any vulnerable GitLab instance shows successful path traversal
or rare API activity consistent with credential access.
success_criteria: A final verdict for every scoped host citing relevant HTTP requests.
tools:
- endpoint
- web
```
## route-on-verdict
<!-- Route on triage verdict -->
if~: "the triage verdict is malicious for at least one host due to successful path traversal or credential extraction" (confidence: high, judge=hunter)
then: → isolate-host
indeterminate: → analyst-review
unavailable: → analyst-review (blind_spot: http-body-blindness)
else: → analyst-review
## isolate-host
<!-- Isolate GitLab server -->
```action target=endpoint
~~~yaml
approval: required
~~~
Isolate the compromised GitLab host from the network. Revoke all credentials and rotate database secrets.
```
→ analyst-review
## analyst-review
<!-- Analyst forensic review -->
```manual target=analyst
Review the full HTTP activity for the cited hosts. Check GitLab application logs for unusual GraphQL activity. Confirm if /etc/passwd or config files were successfully read.
```
→ close-out
## close-out
<!-- Close out and remediate -->
```manual target=analyst
Record the patch status of every identified GitLab host. For those running vulnerable versions, coordinate immediate updates with the infrastructure team.
```
→ end
Run it
Take this hunt into your environment.
Open it in Huntbase to run every step against your own connections, with Scout weighing the evidence and your analysts in command. Or take the open hunt.md file anywhere that reads the format.
Machine-drafted by huntbase-hunt-generation using hb_google/gemini-3-flash-preview, gated by dry-run, lint, critic, then reviewed by a person.