gitlab-exploit · git:20260921.3a32414 · 2026-09-21 · sha256 0936571003ee66f4

gitlab-exploit git:20260921.3a32414C

Immutable. This exact content is served forever at /api/v1/blob/0936571003ee66f4.

---
domain: security
category: exploit
trigger:
  - gitlab-exploit
  - cve-2026-85706
  - gitlab arbitrary file read
  - gitlab unauthenticated
  - gitlab lfi
  - gitlab workhorse
  - gitlab secrets
auto_load: true
---

# gitlab-exploit — CVE-2026-85706

GitLab CE/EE unauthenticated arbitrary file read via Workhorse path-encoding bypass.

## Affected Versions

- GitLab CE/EE **18.7.0–19.1.7**
- GitLab CE/EE **19.2.0–19.2.5**
- GitLab CE/EE **19.3.0–19.3.1**

**Patched:** 19.1.8 / 19.2.6 / 19.3.2

## Vulnerability Summary

Workhorse regex validates the encoded path; Puma decodes it; Rails `File.open(file.path)` executes **before** authentication check. Malformed percent-encoding in multipart upload paths gets interpolated into the HTTP 400 error body → arbitrary file contents leak.

**Impact:** Read `/etc/passwd`, `/etc/gitlab/gitlab-secrets.json`, Rails secrets, SSH keys, database credentials, application source code.

**CVSS:** 9.1 Critical (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N)

**Reporter:** s3ntago (HackerOne)  
**PoC Ref:** `guneykabel/cve-2026-85706`

## Usage

### Single Target

```bash
# Auto-detect + loot default secrets
python3 testing/gitlab-exploit/gitlab_exploit.py -u https://gitlab.example.com --auto

# Read specific file
python3 testing/gitlab-exploit/gitlab_exploit.py -u https://gitlab.example.com --file /etc/passwd

# Interactive shell
python3 testing/gitlab-exploit/gitlab_exploit.py -u https://gitlab.example.com --shell
```

### Pipeline Mode

```bash
# Subfinder + httpx → gitlab-exploit
subfinder -d target.com -silent \
  | httpx -silent -sc -td -title \
  | python3 testing/gitlab-exploit/gitlab_exploit.py --pipe --auto -o hits.jsonl

# From file
cat hosts.txt | python3 testing/gitlab-exploit/gitlab_exploit.py --auto
```

## Interactive Shell Commands

```
cat <path>         — read arbitrary file
loot               — dump default secrets (gitlab-secrets.json, database.yml, secrets.yml)
secrets            — dump Rails secrets.yml
passwd             — read /etc/passwd
version            — detect GitLab version
project <id>       — fetch project info via API
curl <path>        — raw GET request
help               — show commands
exit               — quit shell
```

## Output

- **stdout:** color-coded progress + results
- **JSONL:** `--output hits.jsonl` for pipeline integration
- **Quiet:** `-q` suppresses banner/logs

## Detection Evasion

- Custom User-Agent: `gitlab-exploit/1.2 (+https://github.com/novaestellar/novahaku)`
- Randomized project IDs for commits endpoint discovery
- No signature in error responses (leverages existing Rails 400 handler)

## Remediation

1. Upgrade to patched version (19.1.8 / 19.2.6 / 19.3.2+)
2. WAF rule: block `%` in multipart filename fields
3. Monitor for 400 errors with file content in response body

## References

- CVE-2026-85706
- HackerOne report by s3ntago
- PoC: `guneykabel/cve-2026-85706`

## Integration

Auto-loaded when Novahaku detects: `gitlab`, `cve-2026-85706`, `workhorse`, `arbitrary file read`, or GitLab-related recon output.

**Router dispatch:** `loader.py` maps GitLab recon → `gitlab-exploit` auto-execution.

## Pitfalls

- **False negatives:** Version detection via `/api/v4/version` requires unauth API access (may be disabled). Use `--auto` to try exploit regardless.
- **Rate limiting:** GitLab may block rapid requests. Use `--delay` for large-scale scans.
- **Commit endpoint discovery:** Requires valid project ID. Tool bruteforces common IDs (1-50); extend range with `--project-range`.