{
  "hunt": {
    "meta": {
      "tlp": "clear",
      "hunt": {
        "handoff": "promote-to-detection",
        "trigger": "intel-report",
        "methodology": "model-assisted",
        "applicability": "campaign-specific",
        "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."
      },
      "name": "GitLab Critical API Exploitation",
      "type": "investigation",
      "labels": [
        "hunt",
        "attack.t1190",
        "attack.t1552.004",
        "credential access",
        "initial access"
      ],
      "related": [
        {
          "hunt": "gitlab-web-shell-activity",
          "reason": "Successful file reading often precedes the deployment of web shells.",
          "relation": "follows"
        }
      ],
      "targets": {
        "web": {
          "name": "Web server / proxy logs",
          "category": "siem",
          "telemetry": [
            "network"
          ]
        },
        "hunter": {
          "name": "Hunt agent",
          "agent": true
        },
        "analyst": {
          "name": "Tier-2 analyst",
          "role": "analyst"
        },
        "endpoint": {
          "name": "Endpoint telemetry (hb_ surfaces)",
          "category": "endpoint",
          "telemetry": [
            "endpoint"
          ]
        }
      },
      "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.",
      "coverage": [
        {
          "stage": "vulnerability-exposure-assessment",
          "steps": [
            "find-gitlab-versions",
            "assess-vulnerability"
          ],
          "status": "covered"
        },
        {
          "stage": "unauthenticated-path-traversal",
          "steps": [
            "detect-path-traversal"
          ],
          "status": "covered"
        },
        {
          "stage": "insecure-deserialization-credential-access",
          "steps": [
            "rare-api-usage"
          ],
          "status": "covered"
        }
      ],
      "scenario": {
        "stages": [
          {
            "name": "GitLab Vulnerability Presence",
            "slug": "vulnerability-exposure-assessment",
            "tactic": "initial-access",
            "techniques": [
              "T1190"
            ],
            "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"
            ]
          },
          {
            "name": "Unauthenticated API Path Traversal",
            "slug": "unauthenticated-path-traversal",
            "tactic": "initial-access",
            "techniques": [
              "T1190"
            ],
            "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"
            ]
          },
          {
            "name": "Duo Chat GraphQL Credential Access",
            "slug": "insecure-deserialization-credential-access",
            "tactic": "credential-access",
            "techniques": [
              "T1190",
              "T1552.004"
            ],
            "observables": [
              "Specially crafted GraphQL subscription arguments",
              "Duo Chat API interaction",
              "Extraction of Advanced Search instance configurations",
              "Access to sensitive GitLab credentials"
            ]
          }
        ],
        "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",
      "rationale": "Focus on self-managed GitLab Community and Enterprise edition servers. Use software inventory to establish the high-risk scope first.",
      "guardrails": {
        "claims": "no_unsupported",
        "evidence": "citation_required",
        "telemetry": "untrusted",
        "missing_data": "not_benign"
      },
      "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.",
      "parameters": {
        "scope_hosts": {
          "from": {
            "ref": "scoping-step",
            "kind": "manual",
            "observed": "2026-09-14"
          },
          "type": "list[host]",
          "default": [],
          "description": "Hostnames identified as running GitLab in the scoping step."
        },
        "lookback_days": {
          "from": {
            "ref": "hunt-standard-lookback",
            "kind": "manual",
            "observed": "2026-09-14"
          },
          "type": "number",
          "default": "14",
          "description": "Days of history to examine for behavioral signals."
        },
        "sensitive_files": {
          "from": {
            "ref": "rapid7-gitlab-etr",
            "kind": "article",
            "observed": "2026-09-14"
          },
          "type": "list[path]",
          "default": [
            "/etc/passwd",
            "gitlab.rb",
            "database.yml"
          ],
          "description": "Target files for path traversal exploitation."
        }
      },
      "provenance": {
        "authors": [
          {
            "org": "huntbase.io",
            "name": "Huntbase hunt generation"
          }
        ],
        "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"
        }
      },
      "references": [
        {
          "url": "https://www.rapid7.com/blog/post/etr-cve-2026-85706-critical-gitlab-path-traversal-exploited-in-the-wild",
          "name": "Rapid7 \u2014 CVE-2026-85706: Critical GitLab Path Traversal Exploited in the Wild"
        }
      ],
      "blind_spots": [
        {
          "id": "inventory-visibility-gap",
          "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",
          "question": "Are there unmanaged GitLab instances missing an endpoint agent?",
          "requires": "hb_software_inventory coverage for shadow IT"
        },
        {
          "id": "http-body-blindness",
          "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",
          "question": "What specific arguments were inside the GraphQL POST requests?",
          "requires": "HTTP POST body inspection"
        }
      ]
    },
    "name": "GitLab Critical API Exploitation",
    "description": "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."
  },
  "nodes": [
    {
      "id": "hypothesis",
      "type": "hypothesis",
      "label": "Hypothesis",
      "config": {
        "tags": [],
        "coverage": [
          {
            "stage": "vulnerability-exposure-assessment",
            "steps": [
              "find-gitlab-versions",
              "assess-vulnerability"
            ],
            "status": "covered"
          },
          {
            "stage": "unauthenticated-path-traversal",
            "steps": [
              "detect-path-traversal"
            ],
            "status": "covered"
          },
          {
            "stage": "insecure-deserialization-credential-access",
            "steps": [
              "rare-api-usage"
            ],
            "status": "covered"
          }
        ],
        "rationale": "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.",
        "blind_spots": [
          {
            "id": "inventory-visibility-gap",
            "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",
            "question": "Are there unmanaged GitLab instances missing an endpoint agent?",
            "requires": "hb_software_inventory coverage for shadow IT"
          },
          {
            "id": "http-body-blindness",
            "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",
            "question": "What specific arguments were inside the GraphQL POST requests?",
            "requires": "HTTP POST body inspection"
          }
        ],
        "scoping_notes": "Focus on self-managed GitLab Community and Enterprise edition servers. Use software inventory to establish the high-risk scope first.",
        "beyond_detection": "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."
      }
    },
    {
      "id": "find-gitlab-versions",
      "type": "query",
      "label": "Find GitLab instances and versions",
      "config": {
        "dsl": "sqlite",
        "role": "scoping",
        "source": "endpoint",
        "content": "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'",
        "surface": "hb_software_inventory",
        "description": "Identify every self-managed GitLab installation in the estate and record its version to assess exposure to CVE-2026-85706.",
        "expected_signal": "A list of hosts running GitLab with their current versions. Silence indicates no GitLab software is installed on agent-enrolled hosts."
      },
      "parents": [
        {
          "id": "hypothesis"
        }
      ],
      "primitive_config": {
        "dsl": "sqlite",
        "role": "scoping",
        "label": "Find GitLab instances and versions",
        "reads": [
          "device_hostname",
          "package_name",
          "package_version"
        ],
        "source": "hb_software_inventory",
        "target": "endpoint",
        "content": "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'",
        "silence": "not_evidence_of_absence",
        "expected": "A list of hosts running GitLab with their current versions. Silence indicates no GitLab software is installed on agent-enrolled hosts.",
        "verified": "dry-run",
        "verified_at": "2026-09-29"
      }
    },
    {
      "id": "assess-vulnerability",
      "type": "analytic",
      "label": "Assess version vulnerability",
      "config": {
        "cite": "required",
        "tools": [
          "endpoint",
          "web"
        ],
        "context": [
          "find-gitlab-versions"
        ],
        "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.",
        "description": "Determine if the identified GitLab versions are within the vulnerable ranges specified in the Rapid7 advisory.",
        "max_iterations": 3,
        "expected_signal": "A per-host assessment naming which systems are vulnerable.",
        "success_criteria": "A verdict for each host citing its version relative to the advisory."
      },
      "parents": [
        {
          "id": "find-gitlab-versions"
        }
      ]
    },
    {
      "id": "gate-on-vulnerability",
      "type": "checkpoint",
      "label": "Gate: Proceed to behavioral analysis?",
      "config": {
        "fuzzy": true,
        "judge": "hunter",
        "question": "the assessment identifies at least one host running a vulnerable GitLab version",
        "condition": "the assessment identifies at least one host running a vulnerable GitLab version",
        "blind_spot": "inventory-visibility-gap",
        "confidence": "high",
        "description": "Avoid analyzing large volumes of HTTP telemetry if no vulnerable GitLab instances are present.",
        "checkpoint_type": "mandatory"
      },
      "parents": [
        {
          "id": "assess-vulnerability"
        }
      ]
    },
    {
      "id": "detect-path-traversal",
      "type": "query",
      "label": "Detect path traversal behavior",
      "config": {
        "dsl": "sqlite",
        "role": "detection-candidate",
        "source": "web",
        "content": "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')",
        "surface": "hb_http_activity",
        "description": "Identify HTTP requests containing traversal sequences or targeting sensitive configuration files.",
        "expected_signal": "HTTP requests targeting /etc/passwd or gitlab.rb. Status 200 OK on these paths indicates successful file retrieval."
      },
      "parents": [
        {
          "id": "gate-on-vulnerability",
          "branch": "on_supports"
        }
      ],
      "primitive_config": {
        "dsl": "sqlite",
        "role": "detection-candidate",
        "label": "Detect path traversal behavior",
        "reads": [
          "url_path",
          "url_full",
          "status_code",
          "src_endpoint_ip"
        ],
        "source": "hb_http_activity",
        "target": "web",
        "content": "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')",
        "silence": "not_evidence_of_absence",
        "expected": "HTTP requests targeting /etc/passwd or gitlab.rb. Status 200 OK on these paths indicates successful file retrieval.",
        "verified": "dry-run",
        "verified_at": "2026-09-29"
      }
    },
    {
      "id": "rare-api-usage",
      "type": "query",
      "label": "Identify rare API path usage",
      "config": {
        "dsl": "sqlite",
        "role": "baseline",
        "source": "web",
        "content": "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",
        "surface": "hb_http_activity",
        "description": "Use stack-counting to identify anomalous GraphQL or Duo Chat API paths that differ from standard fleet traffic.",
        "expected_signal": "API endpoints used on only one or two hosts, highlighting potential exploitation of GraphQL subscriptions."
      },
      "parents": [
        {
          "id": "gate-on-vulnerability",
          "branch": "on_supports"
        }
      ],
      "primitive_config": {
        "dsl": "sqlite",
        "role": "baseline",
        "label": "Identify rare API path usage",
        "reads": [
          "url_path"
        ],
        "source": "hb_http_activity",
        "target": "web",
        "content": "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",
        "silence": "not_evidence_of_absence",
        "baseline": {
          "window": "{{lookback_days}}d",
          "compare": "first_seen"
        },
        "expected": "API endpoints used on only one or two hosts, highlighting potential exploitation of GraphQL subscriptions.",
        "verified": "dry-run",
        "prevalence": {
          "by": "device_hostname",
          "key": [
            "url_path"
          ],
          "rare_below": 3
        },
        "verified_at": "2026-09-29"
      }
    },
    {
      "id": "triage-exploitation",
      "type": "analytic",
      "label": "Triage exploitation evidence",
      "config": {
        "cite": "required",
        "tools": [
          "endpoint",
          "web"
        ],
        "context": [
          "assess-vulnerability",
          "detect-path-traversal",
          "rare-api-usage"
        ],
        "objective": "Confirm whether any vulnerable GitLab instance shows successful path traversal or rare API activity consistent with credential access.",
        "description": "Correlate identified vulnerable versions with behavioral artifacts to confirm exploitation.",
        "max_iterations": 6,
        "expected_signal": "A per-host verdict of malicious, suspicious, or benign based on the convergence of version exposure and HTTP telemetry.",
        "success_criteria": "A final verdict for every scoped host citing relevant HTTP requests."
      },
      "parents": [
        {
          "id": "detect-path-traversal",
          "kind": "merge"
        },
        {
          "id": "rare-api-usage",
          "kind": "merge"
        }
      ]
    },
    {
      "id": "route-on-verdict",
      "type": "checkpoint",
      "label": "Route on triage verdict",
      "config": {
        "fuzzy": true,
        "judge": "hunter",
        "question": "the triage verdict is malicious for at least one host due to successful path traversal or credential extraction",
        "condition": "the triage verdict is malicious for at least one host due to successful path traversal or credential extraction",
        "blind_spot": "http-body-blindness",
        "confidence": "high",
        "description": "Direct the response based on the agent's findings of confirmed compromise.",
        "checkpoint_type": "mandatory"
      },
      "parents": [
        {
          "id": "triage-exploitation"
        }
      ]
    },
    {
      "id": "isolate-host",
      "type": "action",
      "label": "Isolate GitLab server",
      "config": {
        "target": "endpoint",
        "description": "Contain the GitLab server to prevent further data exfiltration or lateral movement.",
        "instructions": "Isolate the compromised GitLab host from the network. Revoke all credentials and rotate database secrets.",
        "action_approval": "required"
      },
      "parents": [
        {
          "id": "route-on-verdict",
          "branch": "on_supports"
        }
      ]
    },
    {
      "id": "analyst-review",
      "type": "task",
      "label": "Analyst forensic review",
      "config": {
        "assignee": "analyst",
        "description": "Manually verify findings where status codes or URL patterns were ambiguous.",
        "instructions": "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."
      },
      "parents": [
        {
          "id": "gate-on-vulnerability",
          "branch": "default"
        },
        {
          "id": "gate-on-vulnerability",
          "branch": "on_unavailable"
        },
        {
          "id": "route-on-verdict",
          "branch": "default"
        },
        {
          "id": "route-on-verdict",
          "branch": "on_unavailable"
        },
        {
          "id": "route-on-verdict",
          "branch": "on_refutes"
        },
        {
          "id": "isolate-host"
        }
      ]
    },
    {
      "id": "close-out",
      "type": "task",
      "label": "Close out and remediate",
      "config": {
        "assignee": "analyst",
        "description": "Finalize the hunt and ensure all vulnerable GitLab instances are upgraded.",
        "instructions": "Record the patch status of every identified GitLab host. For those running vulnerable versions, coordinate immediate updates with the infrastructure team."
      },
      "parents": [
        {
          "id": "gate-on-vulnerability",
          "branch": "on_refutes"
        },
        {
          "id": "analyst-review"
        }
      ]
    }
  ]
}