# Sigma rule — CVE-2026-85706 exploit REQUEST shape at the GitLab edge # (nginx gitlab_access.log; identical shape appears in the gitlab-workhorse # JSON access log as {method:"POST", uri:"/api/v4/projects//repository/commits/", # status:400|401|500, route_id:"api"} — see detection/README.md for the workhorse # JSON field mapping; this file targets the Combined-IO-Format-style fields # commonly shipped to SIEM by nginx (cs-method / c-uri / c-query / auth headers). # # Ground truth for every pattern below (quotes in detection/README.md): # evidence/fix-verification/run-20260912-a/phase1-stock/stock-read-fx.wire:2 # POST /api/v4/projects/1/repository/commits/ HTTP/1.1 # .../stock-read-oracle-proof.wire:2 (same shape, 401 arm) # exploit/tests/golden/wire-proof-live-capture.txt:2 (same shape, 400 leak arm) # exploit/probes/m1_matrix.py SUFFIXES + exploit/probes/p9_shape_sweep.py # ('.json' and trailing-'/' bypass arms actually swept on stock 19.3.1) # Benign parity check performed offline: the two /commits/ POST lines captured # in the lab nginx log (README 'negative-control' section) are exploit lines — # legitimate GitLab UI/API clients POST the canonical /repository/commits # WITHOUT trailing slash or .json suffix and WITH Cookie/PRIVATE-TOKEN, which # the filter excludes. title: GitLab Repository Commits API Trailing-Slash Route Bypass with file.path Parameter (CVE-2026-85706 Exploit Request) id: 032075c2-6984-4d41-9e00-7493bd31e88a description: | Detects the CVE-2026-85706 exploit REQUEST shape at the reverse proxy: an unauthenticated POST to a project repository-commits route whose path carries the Workhorse \z-anchoring bypass (trailing slash '/repository/commits/' or '/repository/commits.json') while a flat 'file.path' parameter travels in the query string. The attacker's file.path only survives to Rails on these bypass shapes; the request itself carries no Authorization header, no PRIVATE-TOKEN and no Cookie (the exploit is pre-auth). Body-only variants (file.path inside the POST body) are invisible at this log layer — covered by cve-2026-85706-rails-api-json.yaml — so treat this rule as the query-string arm plus the route-bypass-path arm, and pivot on correlation_id for the rest. status: experimental author: ARL-case-004 detection-engineer date: 2026-09-12 references: - https://docs.gitlab.com/releases/patches/patch-release-gitlab-19-3-2-released/ tags: - attack.initial-access - attack.t1190 - cve.2026-85706 logsource: category: web_server product: nginx # GitLab omnibus: /var/log/gitlab/nginx/gitlab_access.log detection: selection_method: cs-method: 'POST' selection_bypass_path: c-uri|contains: - '/repository/commits/' # trailing-slash arm (wire-proof-live-capture.txt:2) - '/repository/commits.json' # .json arm (wire_shape_goldens.json: wire_read_gitlab_yml_dotjson) - '/repository/commits%2F' # percent-encoded slash arm (p9_shape_sweep.py Y6) - '/repository/files/' # files-family sibling, same helper+fix (exploit README) selection_fileparam_query: c-uri|contains: 'file.path' # query-string form; when ingest keeps args inside c-uri c-query|contains: 'file.path' # ...or a separate c-query field — either key matches (OR) # NOTE: bare '?file%2Epath=' re-encoding is possible (exploit/probes use # literal 'file.path'); see tuning notes in detection/README.md. filter_auth: cs-authorization-header|exists: false # Extend the filter per your log schema: also exclude requests WITH a # 'Cookie' value and WITH 'PRIVATE-TOKEN' — both indicate an authenticated # client; the exploit sends neither (wire captures contain only Host, # User-Agent, Accept, Content-Type, Content-Length, Connection). condition: selection_method and selection_bypass_path and selection_fileparam_query and not filter_auth falsepositives: - 'none expected from GitLab itself: the shipped UI/API clients never POST a trailing-slash or .json commits route carrying a file.path query parameter, and never without authentication material (documented in detection/README.md §negative-control).' - 'security scanners replaying a published PoC shape for this CVE (true positives by intent, but may be your own vulnerability team — allow-list sanctioned scanners by source IP/tag).' - 'third-party integrations that hand-build commit POSTs and copy a documentation URL with a trailing slash AND separately leak a file.path query parameter while unauthenticated — exotic; triage by response status (400-with-GitLab-error / 401-after-helper are the vulnerable/attempted arms; see README response-length tuning note).' level: high logsource_notes: field_mapping_omnibus_nginx: > Omnibus gitlab_access.log is not key=value by default; map via your ingest: $request_method->cs-method, the request target split on '?' -> c-uri + c-query, Authorization header (default log format does not carry it — add $http_authorization to log_format or rely on the rails rule). field_mapping_workhorse_json: > /var/log/gitlab/gitlab-workhorse/current JSON entries for the exploit carry method="POST", uri="/api/v4/projects/1/repository/commits/", route_id="api", status=400|401|500 — same selection applies with fields renamed (uri instead of c-uri; query is inside uri).