{
  "hunt": {
    "meta": {
      "tlp": "clear",
      "hunt": {
        "handoff": "promote-to-detection",
        "trigger": "intel-report",
        "methodology": "model-assisted",
        "applicability": "campaign-specific",
        "justification": "Reverse tunnels provide persistent, bypass-capable access to the internal network. Identifying these implants is critical for preventing lateral movement and data exfiltration after an initial social engineering compromise."
      },
      "name": "TerminalFix Asynchronous Shell and Reverse Tunnel",
      "type": "investigation",
      "labels": [
        "hunt",
        "attack.t1059.001",
        "attack.t1572",
        "attack.t1090.003"
      ],
      "series": {
        "slug": "terminalfix-campaign-deploys-a-reverse-tunnel-through-multistage-intrusion",
        "index": 3,
        "title": "TerminalFix campaign deploys a reverse tunnel through multistage intrusion",
        "total": 3
      },
      "related": [
        {
          "hunt": "terminalfix-initial-persistence",
          "reason": "This hunt focuses on the C2 stage; persistence mechanisms via DLL sideloading and scheduled tasks are handled in the preceding hunt.",
          "relation": "out-of-scope-alternative"
        },
        {
          "hunt": "terminalfix-clickfix-delivery-reconnaissance",
          "relation": "follows"
        }
      ],
      "targets": {
        "hunter": {
          "name": "Hunt agent",
          "agent": true
        },
        "analyst": {
          "name": "Tier-2 analyst",
          "role": "analyst"
        },
        "endpoint": {
          "name": "Endpoint telemetry (hb_ surfaces)",
          "category": "endpoint",
          "telemetry": [
            "endpoint"
          ]
        }
      },
      "analysis": "A single rule might catch the DNS lookup, but this hunt correlates the network lead with behavioral signals from PowerShell script blocks and Python process activity, baseline counts the rarity of the tunnel script, and weighs all three pieces of evidence to confirm active C2.",
      "coverage": [
        {
          "stage": "command-and-control-asynchronous-shell",
          "steps": [
            "powershell-async-shell"
          ],
          "status": "covered"
        },
        {
          "stage": "command-and-control-reverse-tunnel",
          "steps": [
            "dns-c2-lead",
            "python-tunnel-implant"
          ],
          "status": "covered"
        },
        {
          "stage": "initial-access-social-engineering",
          "reason": "Belongs to another part of the 'TerminalFix campaign deploys a reverse tunnel through multistage intrusion' series.",
          "status": "out_of_scope"
        },
        {
          "stage": "execution-powershell-launcher",
          "reason": "Belongs to another part of the 'TerminalFix campaign deploys a reverse tunnel through multistage intrusion' series.",
          "status": "out_of_scope"
        },
        {
          "stage": "defense-evasion-dll-sideloading",
          "reason": "Belongs to another part of the 'TerminalFix campaign deploys a reverse tunnel through multistage intrusion' series.",
          "status": "out_of_scope"
        },
        {
          "stage": "defense-evasion-steganography",
          "reason": "Belongs to another part of the 'TerminalFix campaign deploys a reverse tunnel through multistage intrusion' series.",
          "status": "out_of_scope"
        },
        {
          "stage": "persistence-mechanisms",
          "reason": "Belongs to another part of the 'TerminalFix campaign deploys a reverse tunnel through multistage intrusion' series.",
          "status": "out_of_scope"
        },
        {
          "stage": "discovery-domain-reconnaissance",
          "reason": "Belongs to another part of the 'TerminalFix campaign deploys a reverse tunnel through multistage intrusion' series.",
          "status": "out_of_scope"
        }
      ],
      "scenario": {
        "stages": [
          {
            "name": "Social Engineering via Fake CAPTCHA",
            "slug": "initial-access-social-engineering",
            "tactic": "initial-access",
            "techniques": [
              "T1204.001"
            ],
            "observables": [
              "Cloudflare Turnstile verification overlay",
              "Verification command copied to clipboard",
              "Instructions to open Windows Terminal or PowerShell",
              "Fake Cloudflare-themed terminal output messages"
            ]
          },
          {
            "name": "Malicious PowerShell Launcher",
            "slug": "execution-powershell-launcher",
            "tactic": "execution",
            "techniques": [
              "T1059.001",
              "T1105"
            ],
            "observables": [
              "C:\\ProgramData\\f47f2a8c21c9df4e",
              "1.bat",
              "ZIP archive download with custom User-Agent",
              "I am not a robot - Cloudflare ID: f47f2a8c21c9df4e"
            ]
          },
          {
            "name": "DLL Sideloading via LockScreenContentServer",
            "slug": "defense-evasion-dll-sideloading",
            "tactic": "defense-evasion",
            "techniques": [
              "T1574.001"
            ],
            "observables": [
              "LockScreenContentServer.exe",
              "dui70.dll (unsigned, forged timestamp 2104)",
              "LockScreenContentServer.exe loading dui70.dll from ProgramData"
            ]
          },
          {
            "name": "Steganographic Payload Extraction",
            "slug": "defense-evasion-steganography",
            "tactic": "defense-evasion",
            "techniques": [
              "T1027.003"
            ],
            "observables": [
              "p1.png",
              "p2.png",
              "p3.png",
              "gitnow.dev",
              "Extract-RawFileFromImage PowerShell function",
              "Reassembling DLL fragments from PNG pixel data"
            ]
          },
          {
            "name": "Redundant Persistence",
            "slug": "persistence-mechanisms",
            "tactic": "persistence",
            "techniques": [
              "T1547.001",
              "T1053.005"
            ],
            "observables": [
              "LockScreenContentServer_MuODG5yBM",
              "Registry Run Key: HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run",
              "Scheduled Task running every 60 minutes",
              "attrib +h +s folder hiding on C:\\ProgramData subfolders"
            ]
          },
          {
            "name": "Extensive Domain Discovery",
            "slug": "discovery-domain-reconnaissance",
            "tactic": "discovery",
            "techniques": [
              "T1018",
              "T1087.002",
              "T1482"
            ],
            "observables": [
              "nltest /domain_trusts",
              "net group \"domain admins\" /domain",
              "get-aduser",
              "get-adcomputer",
              "Ping sweeps of dc, db, backup, gateway, mail servers"
            ]
          },
          {
            "name": "Asynchronous File-Watch Command Loop",
            "slug": "command-and-control-asynchronous-shell",
            "tactic": "command-and-control",
            "techniques": [
              "T1059.001"
            ],
            "observables": [
              "PowerShell file-watch loop monitoring text files",
              "Invoke-Expression (IEX) on watched file content",
              "Command output written to disk files"
            ]
          },
          {
            "name": "Reverse WebSocket Tunneling",
            "slug": "command-and-control-reverse-tunnel",
            "tactic": "command-and-control",
            "techniques": [
              "T1572",
              "T1090.003"
            ],
            "observables": [
              "pythonw.exe",
              "client.py",
              "gitnow.dev:443",
              "Reverse WebSocket tunnel providing SOCKS proxy access"
            ]
          }
        ],
        "summary": "The TerminalFix campaign employs fake Cloudflare CAPTCHA prompts to trick users into executing malicious PowerShell commands that initiate a multi-stage infection. The attack leverages DLL sideloading and steganography to deploy a persistent Python-based reverse tunnel, enabling attackers to conduct extensive Active Directory reconnaissance and maintain encrypted SOCKS-style proxy access to the victim's internal network."
      },
      "severity": "high",
      "rationale": "Target systems with recent suspicious software installations or those that triggered earlier ClickFix-related alerts. Focus on workstations where Windows Terminal or PowerShell is frequently used by non-admins.",
      "guardrails": {
        "claims": "no_unsupported",
        "evidence": "citation_required",
        "telemetry": "untrusted",
        "missing_data": "not_benign"
      },
      "hypothesis": "An intruder has established long-term C2 presence using a PowerShell file-watch loop for asynchronous command execution and a Python-based reverse tunnel for persistent network-level proxying.",
      "parameters": {
        "c2_domains": {
          "from": {
            "ref": "msrc-blog-terminalfix",
            "kind": "article",
            "observed": "2026-08-28"
          },
          "type": "list[domain]",
          "default": [
            "gitnow.dev"
          ],
          "description": "C2 domains observed in the TerminalFix campaign."
        },
        "scope_hosts": {
          "type": "list[host]",
          "default": [],
          "description": "Optional list of hostnames to narrow the search."
        },
        "lookback_days": {
          "type": "number",
          "default": "14",
          "description": "Days of history to examine."
        }
      },
      "provenance": {
        "authors": [
          {
            "org": "huntbase.io",
            "name": "Huntbase hunt generation"
          }
        ],
        "generated": {
          "by": "huntbase-hunt-generation",
          "from": "https://www.microsoft.com/en-us/security/blog/2026/08/28/terminalfix-campaign-deploys-reverse-tunnel-through-multistage-intrusion/",
          "gates": [
            "dry-run",
            "lint",
            "critic"
          ],
          "model": "hb_google/gemini-3-flash-preview"
        }
      },
      "references": [
        {
          "url": "https://www.microsoft.com/en-us/security/blog/2026/08/28/terminalfix-campaign-deploys-reverse-tunnel-through-multistage-intrusion/",
          "name": "MSRC Blog: TerminalFix campaign deploys a reverse tunnel through multistage intrusion"
        }
      ],
      "blind_spots": [
        {
          "id": "no-script-logging",
          "risk": "An attacker can maintain a stealthy shell that leaves no process command-line artifacts.",
          "stage": "command-and-control-asynchronous-shell",
          "question": "whether the file-watch command loop is running on hosts where script logging is disabled",
          "requires": "PowerShell Script Block Logging (EID 4104)"
        },
        {
          "id": "ephemeral-tunnel-processes",
          "risk": "Short-lived proxy connections used for targeted data exfiltration might be missed.",
          "stage": "command-and-control-reverse-tunnel",
          "question": "whether the Python tunnel was established and torn down between collection intervals",
          "requires": "high-frequency hb_process_activity snapshots"
        }
      ]
    },
    "name": "TerminalFix Asynchronous Shell and Reverse Tunnel",
    "description": "The TerminalFix campaign establishes persistent control by deploying an asynchronous command shell and a reverse WebSocket tunnel. The command shell monitors a local text file using PowerShell's FileSystemWatcher and executes content via Invoke-Expression, while the reverse tunnel, typically a Python script named client.py running under pythonw.exe, connects to attacker infrastructure to provide proxy access. This hunt identifies the network lead for the C2 domain, then fanned-out searches for both the shell and the tunnel implant to settle on a per-host verdict."
  },
  "nodes": [
    {
      "id": "hypothesis",
      "type": "hypothesis",
      "label": "Hypothesis",
      "config": {
        "tags": [],
        "series": {
          "slug": "terminalfix-campaign-deploys-a-reverse-tunnel-through-multistage-intrusion",
          "index": 3,
          "title": "TerminalFix campaign deploys a reverse tunnel through multistage intrusion",
          "total": 3
        },
        "coverage": [
          {
            "stage": "command-and-control-asynchronous-shell",
            "steps": [
              "powershell-async-shell"
            ],
            "status": "covered"
          },
          {
            "stage": "command-and-control-reverse-tunnel",
            "steps": [
              "dns-c2-lead",
              "python-tunnel-implant"
            ],
            "status": "covered"
          },
          {
            "stage": "initial-access-social-engineering",
            "reason": "Belongs to another part of the 'TerminalFix campaign deploys a reverse tunnel through multistage intrusion' series.",
            "status": "out_of_scope"
          },
          {
            "stage": "execution-powershell-launcher",
            "reason": "Belongs to another part of the 'TerminalFix campaign deploys a reverse tunnel through multistage intrusion' series.",
            "status": "out_of_scope"
          },
          {
            "stage": "defense-evasion-dll-sideloading",
            "reason": "Belongs to another part of the 'TerminalFix campaign deploys a reverse tunnel through multistage intrusion' series.",
            "status": "out_of_scope"
          },
          {
            "stage": "defense-evasion-steganography",
            "reason": "Belongs to another part of the 'TerminalFix campaign deploys a reverse tunnel through multistage intrusion' series.",
            "status": "out_of_scope"
          },
          {
            "stage": "persistence-mechanisms",
            "reason": "Belongs to another part of the 'TerminalFix campaign deploys a reverse tunnel through multistage intrusion' series.",
            "status": "out_of_scope"
          },
          {
            "stage": "discovery-domain-reconnaissance",
            "reason": "Belongs to another part of the 'TerminalFix campaign deploys a reverse tunnel through multistage intrusion' series.",
            "status": "out_of_scope"
          }
        ],
        "rationale": "An intruder has established long-term C2 presence using a PowerShell file-watch loop for asynchronous command execution and a Python-based reverse tunnel for persistent network-level proxying.",
        "blind_spots": [
          {
            "id": "no-script-logging",
            "risk": "An attacker can maintain a stealthy shell that leaves no process command-line artifacts.",
            "stage": "command-and-control-asynchronous-shell",
            "question": "whether the file-watch command loop is running on hosts where script logging is disabled",
            "requires": "PowerShell Script Block Logging (EID 4104)"
          },
          {
            "id": "ephemeral-tunnel-processes",
            "risk": "Short-lived proxy connections used for targeted data exfiltration might be missed.",
            "stage": "command-and-control-reverse-tunnel",
            "question": "whether the Python tunnel was established and torn down between collection intervals",
            "requires": "high-frequency hb_process_activity snapshots"
          }
        ],
        "scoping_notes": "Target systems with recent suspicious software installations or those that triggered earlier ClickFix-related alerts. Focus on workstations where Windows Terminal or PowerShell is frequently used by non-admins.",
        "beyond_detection": "A single rule might catch the DNS lookup, but this hunt correlates the network lead with behavioral signals from PowerShell script blocks and Python process activity, baseline counts the rarity of the tunnel script, and weighs all three pieces of evidence to confirm active C2."
      }
    },
    {
      "id": "dns-c2-lead",
      "type": "query",
      "label": "DNS lookups for TerminalFix C2 domains",
      "config": {
        "dsl": "sqlite",
        "role": "detection-candidate",
        "source": "endpoint",
        "content": "SELECT device_hostname, query_hostname, COUNT(*) AS lookup_count, MIN(time) AS first_seen, MAX(time) AS last_seen FROM hb_dns_activity WHERE instr(',' || '{{c2_domains}}' || ',', ',' || LOWER(query_hostname) || ',') > 0 AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days') GROUP BY device_hostname, query_hostname",
        "surface": "hb_dns_activity",
        "description": "Identify hosts attempting to resolve the known C2 infrastructure used for reverse tunneling.",
        "expected_signal": "Specific hostnames resolving gitnow.dev. This indicates the reverse tunnel is likely active on those hosts."
      },
      "parents": [
        {
          "id": "hypothesis"
        }
      ],
      "primitive_config": {
        "dsl": "sqlite",
        "role": "detection-candidate",
        "label": "DNS lookups for TerminalFix C2 domains",
        "reads": [
          "device_hostname",
          "query_hostname",
          "time"
        ],
        "source": "hb_dns_activity",
        "target": "endpoint",
        "content": "SELECT device_hostname, query_hostname, COUNT(*) AS lookup_count, MIN(time) AS first_seen, MAX(time) AS last_seen FROM hb_dns_activity WHERE instr(',' || '{{c2_domains}}' || ',', ',' || LOWER(query_hostname) || ',') > 0 AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days') GROUP BY device_hostname, query_hostname",
        "silence": "not_evidence_of_absence",
        "expected": "Specific hostnames resolving gitnow.dev. This indicates the reverse tunnel is likely active on those hosts.",
        "verified": "dry-run",
        "verified_at": "2026-09-20"
      }
    },
    {
      "id": "powershell-async-shell",
      "type": "query",
      "label": "PowerShell file-watch command loop",
      "config": {
        "dsl": "sqlite",
        "role": "triage",
        "source": "endpoint",
        "content": "SELECT device_hostname, script_path, script_content, time FROM hb_script_activity WHERE (LOWER(script_content) LIKE '%filesystemwatcher%' AND LOWER(script_content) LIKE '%invoke-expression%' AND (LOWER(script_content) LIKE '%set-content%' OR LOWER(script_content) LIKE '%out-file%')) AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days')",
        "surface": "hb_script_activity",
        "description": "Locate the script blocks responsible for monitoring a file and executing its contents, which forms the attacker's shell.",
        "expected_signal": "Script blocks showing FileSystemWatcher being initialized on a text file followed by Invoke-Expression (IEX)."
      },
      "parents": [
        {
          "id": "dns-c2-lead"
        }
      ],
      "primitive_config": {
        "dsl": "sqlite",
        "role": "triage",
        "label": "PowerShell file-watch command loop",
        "reads": [
          "device_hostname",
          "script_content",
          "script_path",
          "time"
        ],
        "source": "hb_script_activity",
        "target": "endpoint",
        "content": "SELECT device_hostname, script_path, script_content, time FROM hb_script_activity WHERE (LOWER(script_content) LIKE '%filesystemwatcher%' AND LOWER(script_content) LIKE '%invoke-expression%' AND (LOWER(script_content) LIKE '%set-content%' OR LOWER(script_content) LIKE '%out-file%')) AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days')",
        "silence": "not_evidence_of_absence",
        "expected": "Script blocks showing FileSystemWatcher being initialized on a text file followed by Invoke-Expression (IEX).",
        "verified": "dry-run",
        "verified_at": "2026-09-20"
      }
    },
    {
      "id": "python-tunnel-implant",
      "type": "query",
      "label": "Python reverse tunnel processes",
      "config": {
        "dsl": "sqlite",
        "role": "baseline",
        "source": "endpoint",
        "content": "SELECT device_hostname, process_name, process_cmd_line, user_name, time FROM hb_process_activity WHERE (LOWER(process_name) LIKE '%pythonw.exe%' AND LOWER(process_cmd_line) LIKE '%client.py%') AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days')",
        "surface": "hb_process_activity",
        "description": "Find the specific Python runtime instances used to maintain the reverse WebSocket tunnel.",
        "expected_signal": "A pythonw.exe process running client.py, often from a hidden or non-standard directory like ProgramData."
      },
      "parents": [
        {
          "id": "dns-c2-lead"
        }
      ],
      "primitive_config": {
        "dsl": "sqlite",
        "role": "baseline",
        "label": "Python reverse tunnel processes",
        "reads": [
          "device_hostname",
          "process_cmd_line",
          "process_name",
          "time",
          "user_name"
        ],
        "source": "hb_process_activity",
        "target": "endpoint",
        "content": "SELECT device_hostname, process_name, process_cmd_line, user_name, time FROM hb_process_activity WHERE (LOWER(process_name) LIKE '%pythonw.exe%' AND LOWER(process_cmd_line) LIKE '%client.py%') AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days')",
        "silence": "not_evidence_of_absence",
        "baseline": {
          "window": "{{lookback_days}}d",
          "compare": "first_seen"
        },
        "expected": "A pythonw.exe process running client.py, often from a hidden or non-standard directory like ProgramData.",
        "verified": "dry-run",
        "prevalence": {
          "by": "device_hostname",
          "key": [
            "process_cmd_line"
          ],
          "rare_below": 3
        },
        "verified_at": "2026-09-20"
      }
    },
    {
      "id": "triage-c2-evidence",
      "type": "analytic",
      "label": "Triage C2 evidence",
      "config": {
        "cite": "required",
        "tools": [
          "endpoint"
        ],
        "context": [
          "dns-c2-lead",
          "powershell-async-shell",
          "python-tunnel-implant"
        ],
        "objective": "Determine if any host shows confirmed TerminalFix command-and-control activity by combining the DNS, script activity, and process evidence.",
        "description": "Weigh the co-occurrence of DNS resolves, the async shell script, and the Python process per host.",
        "max_iterations": 6,
        "expected_signal": "A per-host verdict confirming active C2 activity based on the combined indicators.",
        "success_criteria": "A verdict of malicious, suspicious, or benign per host, citing specific rows from all input steps."
      },
      "parents": [
        {
          "id": "powershell-async-shell",
          "kind": "merge"
        },
        {
          "id": "python-tunnel-implant",
          "kind": "merge"
        }
      ]
    },
    {
      "id": "route-on-verdict",
      "type": "checkpoint",
      "label": "Route on verdict",
      "config": {
        "fuzzy": true,
        "judge": "hunter",
        "question": "The triage verdict is malicious for at least one host, indicating an active reverse tunnel or command loop.",
        "condition": "The triage verdict is malicious for at least one host, indicating an active reverse tunnel or command loop.",
        "blind_spot": "no-script-logging",
        "confidence": "high",
        "description": "Route to containment if the triage agent confirms malicious C2 activity.",
        "checkpoint_type": "mandatory"
      },
      "parents": [
        {
          "id": "triage-c2-evidence"
        }
      ]
    },
    {
      "id": "isolate-host",
      "type": "action",
      "label": "Isolate host",
      "config": {
        "target": "endpoint",
        "description": "Sever the attacker's network-level access by isolating the compromised endpoint.",
        "instructions": "Isolate the host and revoke any active sessions for users logged into this host. Collect the client.py file and any monitored text files for forensic analysis.",
        "action_approval": "required"
      },
      "parents": [
        {
          "id": "route-on-verdict",
          "branch": "on_supports"
        }
      ]
    },
    {
      "id": "analyst-review",
      "type": "task",
      "label": "Analyst review",
      "config": {
        "assignee": "analyst",
        "description": "Final manual confirmation and review of the findings.",
        "instructions": "Review the Python process and PowerShell script activity to confirm the C2 nature; search for secondary implants or lateral movement from this host. Examine the text file content being watched for evidence of past commands."
      },
      "parents": [
        {
          "id": "route-on-verdict",
          "branch": "default"
        },
        {
          "id": "route-on-verdict",
          "branch": "on_unavailable"
        },
        {
          "id": "isolate-host"
        }
      ]
    },
    {
      "id": "close-out",
      "type": "task",
      "label": "Close out",
      "config": {
        "assignee": "analyst",
        "description": "Hunt completion for negative results.",
        "instructions": "Record that no active command loops or reverse tunnels were found on the scoped hosts. Note any legitimate Python or FileSystemWatcher usage discovered for future tuning."
      },
      "parents": [
        {
          "id": "route-on-verdict",
          "branch": "on_refutes"
        }
      ]
    }
  ]
}