Microsoft Security Research recently published an analysis of the TerminalFix campaign — a multistage intrusion chain that deploys a reverse tunnel for persistent remote access, and, most notably, uses PNG image files with embedded steganography to smuggle payloads past perimeter controls. The SANS Internet Storm Center followed up by obtaining the PNG IOCs directly from the researchers, confirming this is an active, in-the-wild tradecraft — not a theoretical exercise.
Why should defenders care? Because steganography is a defense-evasion technique that neutralizes a huge portion of the average security stack. A PNG file downloaded from the internet looks like an image to your web proxy, your email gateway, your DLP, and many sandbox solutions. The malicious payload never exists as a recognizable executable on the wire — it is reconstructed in memory on the endpoint by a loader script. Meanwhile, the final-stage reverse tunnel (SSH reverse forwarding, ngrok/cloudflared-style tunneling, or similar) punches an outbound-initiated channel back to the attacker, bypassing inbound firewall rules entirely.
This is a textbook example of two MITRE ATT&CK techniques converging: T1027 (Obfuscated Files or Information) via steganographic payload delivery, and T1572 (Protocol Tunneling) for C2. Your detection strategy must address both halves of the chain.
Technical Analysis
Campaign Overview
Based on Microsoft's published research and the SANS ISC diary entry (September 21), the TerminalFix intrusion proceeds in multiple stages:
- Initial access / lure: Victims are directed — typically via malvertising, fake software "fix" utilities, or SEO-poisoned download sites — to retrieve what appears to be a legitimate troubleshooting or repair tool (the "TerminalFix" branding).
- Stager execution: A script-based stager (commonly PowerShell on Windows targets) downloads one or more PNG image files from attacker-controlled or abused-legitimate hosting.
- Steganographic extraction: The stager decodes pixel data from the PNG — typically reading least-significant-bit (LSB) encoded bytes across the image — to reconstruct the next-stage payload in memory, avoiding writing a scannable executable to disk.
- Reverse tunnel deployment: The final payload installs a tunneling client that initiates an outbound connection to attacker infrastructure, then exposes a remote shell or RDP-like access back through that tunnel. Because the connection originates from inside the network, inbound firewall rules never see it.
- Persistence & post-exploitation: Scheduled tasks, run keys, or service installs keep the tunnel alive across reboots.
Why PNG Steganography Is Hard to Catch
- No signature on the wire. The PNG is a valid image file. Magic bytes, MIME type, and rendering all check out. Most web proxies and secure email gateways pass it without inspection.
- No payload on disk. The decoded second stage lives in memory (reflective loading), so file-based AV and EDR file-scanning telemetry miss it.
- The decoder is trivially small. A dozen lines of PowerShell or Python using
System.Drawing/GetPixel()or PIL can perform LSB extraction — indistinguishable from benign image-processing code unless you are watching the behavior.
Why Reverse Tunnels Are Hard to Catch
- Outbound-only traffic. Reverse SSH (
ssh -R), ngrok, cloudflared, bore, and similar tools connect out over 443/8443 — ports every network must allow. - Encrypted by design. The tunnel payload is TLS/SSH; content inspection yields nothing without breaking the session.
- Legitimate dual-use tools. cloudflared and ngrok have valid business use cases, so blanket blocking generates friction. Detection must focus on unsigned binaries, unusual parents, and unexpected destinations.
Exploitation Status
- Confirmed active in the wild: Microsoft Security Research tracked this as a live campaign; SANS ISC obtained and published IOCs for the steganographic PNGs.
- No CVE involved: This is a tradecraft campaign, not a vulnerability exploit. Defensive value comes from behavioral detection and egress control, not patching.
Detection & Response
The detections below target the two chokepoints every TerminalFix-style intrusion must cross: (1) a script interpreter downloading and decoding an image file, and (2) a tunneling binary establishing an outbound persistent connection. These are high-fidelity behaviors with low false-positive rates when tuned against your developer/admin baseline.
Sigma Rules
---
title: PowerShell Image Download and Pixel Extraction - Possible Steganographic Loader
id: 8b3d2f41-6a1e-4c92-b7d4-5f8a9c2e1d07
status: experimental
description: Detects PowerShell downloading image files and/or using System.Drawing pixel enumeration consistent with LSB steganographic payload extraction, as seen in the TerminalFix campaign.
references:
- https://isc.sans.edu/diary/rss/33318
- https://attack.mitre.org/techniques/T1027/
- https://attack.mitre.org/techniques/T1140/
author: Security Arsenal
date: 2026/01/15
tags:
- attack.defense_evasion
- attack.t1027
- attack.t1140
logsource:
category: process_creation
product: windows
detection:
selection_img_download:
Image|endswith:
- '\powershell.exe'
- '\pwsh.exe'
CommandLine|contains:
- '.png'
selection_download_verbs:
CommandLine|contains:
- 'DownloadFile'
- 'DownloadString'
- 'Invoke-WebRequest'
- 'iwr '
- 'curl '
- 'Start-BitsTransfer'
selection_decode:
CommandLine|contains:
- 'System.Drawing'
- 'GetPixel'
- 'FromBase64String'
- 'BitConverter'
condition: selection_img_download and (selection_download_verbs or selection_decode)
falsepositives:
- Legitimate wallpaper/theme automation scripts (rare in enterprise server environments)
- Image processing build pipelines
level: high
---
title: Reverse Tunneling Tool Execution - ngrok Cloudflared SSH Reverse Forward
id: 2f7a9c14-8d3b-4e61-a9c2-6b4d8e1f3a05
status: experimental
description: Detects execution of known reverse tunneling utilities or SSH with reverse-forward flags, the final-stage C2 mechanism observed in the TerminalFix campaign.
references:
- https://isc.sans.edu/diary/rss/33318
- https://attack.mitre.org/techniques/T1572/
author: Security Arsenal
date: 2026/01/15
tags:
- attack.command_and_control
- attack.t1572
- attack.t1090
logsource:
category: process_creation
product: windows
detection:
selection_binary:
Image|endswith:
- '\ngrok.exe'
- '\cloudflared.exe'
- '\bore.exe'
- '\chisel.exe'
- '\frpc.exe'
- '\zrok.exe'
- '\localhost.run'
selection_ssh_reverse:
Image|endswith:
- '\ssh.exe'
CommandLine|contains:
- ' -R '
- ' -R:'
selection_tunnel_args:
CommandLine|contains:
- 'tunnel --url'
- 'tcp --region'
- 'http --domain'
- 'authtoken'
condition: selection_binary or selection_ssh_reverse or selection_tunnel_args
falsepositives:
- Developers legitimately using ngrok/cloudflared for testing - baseline and whitelist known users
- IT admin SSH reverse forwarding for remote support
level: high
---
title: Script Interpreter Spawning Tunneling or Shell Binary from User-Writable Path
id: 5c1e8a36-4f92-4b7d-9a31-7e2c5d8f6b09
status: experimental
description: Detects PowerShell or script hosts spawning executables from temp/AppData paths, consistent with an in-memory decoded payload chain dropping a tunneling stage.
references:
- https://isc.sans.edu/diary/rss/33318
- https://attack.mitre.org/techniques/T1059/
author: Security Arsenal
date: 2026/01/15
tags:
- attack.execution
- attack.t1059.001
- attack.defense_evasion
logsource:
category: process_creation
product: windows
detection:
selection_parent:
ParentImage|endswith:
- '\powershell.exe'
- '\pwsh.exe'
- '\wscript.exe'
- '\cscript.exe'
- '\mshta.exe'
selection_child_path:
Image|contains:
- '\AppData\Local\Temp\'
- '\AppData\Roaming\'
- '\ProgramData\'
- '\Users\Public\'
filter_known:
Image|endswith:
- '\teams.exe'
- '\onedrive.exe'
- '\slack.exe'
condition: selection_parent and selection_child_path and not filter_known
falsepositives:
- SaaS updaters (Teams, OneDrive) - extend filter for your environment
level: medium
KQL Hunt — Microsoft Sentinel / Defender
This query hunts the full TerminalFix pattern: PowerShell reaching for image files, followed by a correlated tunneling process or suspicious outbound connection from the same device within a short window.
// Hunt 1: PowerShell downloading or decoding PNG/image files (steganographic loader behavior)
let ImageLoader = DeviceProcessEvents
| where TimeGenerated > ago(7d)
| where FileName in~ ("powershell.exe", "pwsh.exe")
| where ProcessCommandLine has_any (".png", ".jpg", ".bmp")
and ProcessCommandLine has_any ("DownloadFile", "DownloadString", "Invoke-WebRequest", "iwr", "Start-BitsTransfer", "GetPixel", "System.Drawing", "FromBase64String")
| project LoaderTime=TimeGenerated, DeviceName, DeviceId, AccountName, LoaderCmd=ProcessCommandLine;
// Hunt 2: Tunneling tools or reverse SSH on the same estate
let TunnelActivity = DeviceProcessEvents
| where TimeGenerated > ago(7d)
| where FileName in~ ("ngrok.exe", "cloudflared.exe", "chisel.exe", "frpc.exe", "bore.exe")
or (FileName =~ "ssh.exe" and ProcessCommandLine has " -R")
or ProcessCommandLine has_any ("tunnel --url", "authtoken", "tcp --region")
| project TunnelTime=TimeGenerated, DeviceName, DeviceId, AccountName, TunnelCmd=ProcessCommandLine, TunnelBin=FileName;
// Correlate: loader and tunnel on the same device = TerminalFix-style chain
ImageLoader
| join kind=inner TunnelActivity on DeviceName
| where abs(datetime_diff('minute', TunnelTime, LoaderTime)) <= 60
| project DeviceName, AccountName, LoaderTime, LoaderCmd, TunnelTime, TunnelBin, TunnelCmd
| sort by LoaderTime desc
// Hunt 3 (Syslog/CEF ingestion for Linux or network devices): outbound long-lived connections to tunnel service domains
CommonSecurityLog
| where TimeGenerated > ago(7d)
| where DestinationHostName has_any ("ngrok.io", "ngrok-free.app", "trycloudflare.com", "bore.pub", "localhost.run", "serveo.net", "zrok.io")
| project TimeGenerated, SourceIP, DestinationHostName, DestinationPort, DeviceAction, RequestURL
| sort by TimeGenerated desc
Velociraptor VQL — Endpoint Hunt
Use this across your Windows fleet to find both halves of the chain: script interpreters touching image files, and tunneling binaries or live reverse-tunnel connections.
-- TerminalFix hunt: steganographic loader processes + tunneling binaries/connections
LET procs = SELECT Pid, Ppid, Name, Exe, CommandLine, Username, CreateTime
FROM pslist()
WHERE CommandLine =~ '(?i)\.png|\.jpg|GetPixel|System\.Drawing'
OR Exe =~ '(?i)(ngrok|cloudflared|chisel|frpc|bore|zrok)\.exe$'
OR (Name =~ '(?i)ssh\.exe' AND CommandLine =~ '(?i)\s-R[\s:]')
LET conns = SELECT Pid, Name, RemoteAddr, RemotePort, Status
FROM netstat()
WHERE Status = 'ESTABLISHED'
AND RemotePort in (443, 8443, 22, 3389, 8080)
AND Name =~ '(?i)(ngrok|cloudflared|chisel|frpc|bore|ssh|powershell)'
SELECT * FROM procs
UNION ALL
SELECT Pid, NULL AS Ppid, Name, NULL AS Exe,
format(format='ESTABLISHED to %v:%v', args=[RemoteAddr, RemotePort]) AS CommandLine,
NULL AS Username, NULL AS CreateTime
FROM conns
-- Persistence check: scheduled tasks and run keys referencing tunneling tools or temp paths
SELECT Name, Command, FullName as KeyPath, ModTime
FROM glob(globs='C:/Windows/System32/Tasks/*', accessor='ntfs')
WHERE read_file(accessor='ntfs', filenames=FullName) =~ '(?i)(ngrok|cloudflared|AppData\\Local\\Temp|powershell.*\.png)'
Triage & Remediation Script (PowerShell)
Run this on suspect endpoints (or deploy via your RMM/EDR live response) to identify steganographic loader artifacts, tunneling processes, suspicious outbound sessions, and kill/quarantine them.
# TerminalFix triage & remediation - run as Administrator
$Report = @()
# 1) Find running tunneling tools and reverse-SSH sessions
$tunnelProcs = Get-CimInstance Win32_Process | Where-Object {
$_.Name -match '^(ngrok|cloudflared|chisel|frpc|bore|zrok)' -or
($_.Name -match '^ssh' -and $_.CommandLine -match '-R')
}
foreach ($p in $tunnelProcs) {
$Report += [pscustomobject]@{Type='TunnelProcess'; Name=$p.Name; PID=$p.ProcessId; Cmd=$p.CommandLine; Path=$p.ExecutablePath}
Stop-Process -Id $p.ProcessId -Force -ErrorAction SilentlyContinue
}
# 2) Flag PowerShell processes referencing image files or pixel-decoding (possible stego loaders)
$stego = Get-CimInstance Win32_Process | Where-Object {
$_.Name -match '^powershell|^pwsh' -and
$_.CommandLine -match '\.png|\.jpg|GetPixel|System\.Drawing'
}
foreach ($p in $stego) {
$Report += [pscustomobject]@{Type='StegoLoader'; Name=$p.Name; PID=$p.ProcessId; Cmd=$p.CommandLine; Path=$p.ExecutablePath}
}
# 3) Quarantine suspicious recently-downloaded images in user-writable dirs (last 14 days)
$paths = @("$env:TEMP","$env:USERPROFILE\Downloads","$env:USERPROFILE\AppData\Local\Temp","C:\Users\Public")
$suspectPngs = Get-ChildItem $paths -Recurse -Include *.png,*.jpg -ErrorAction SilentlyContinue |
Where-Object { $_.LastWriteTime -gt (Get-Date).AddDays(-14) -and $_.Length -gt 200KB }
$quarantine = "C:\Quarantine_$((Get-Date).ToString('yyyyMMdd_HHmmss'))"
if ($suspectPngs) {
New-Item -ItemType Directory -Path $quarantine -Force | Out-Null
foreach ($f in $suspectPngs) {
$Report += [pscustomobject]@{Type='SuspectImage'; Name=$f.Name; PID=''; Cmd=$f.FullName; Path="$($f.Length) bytes, $($f.LastWriteTime)"}
Move-Item $f.FullName $quarantine -Force -ErrorAction SilentlyContinue
}
}
# 4) Pull established outbound sessions for tunnel-capable processes
Get-NetTCPConnection -State Established -ErrorAction SilentlyContinue |
Where-Object { $_.RemotePort -in 22,443,8443,8080 } |
ForEach-Object {
$proc = Get-Process -Id $_.OwningProcess -ErrorAction SilentlyContinue
if ($proc.Name -match 'ngrok|cloudflared|chisel|frpc|bore|ssh|powershell') {
$Report += [pscustomobject]@{Type='OutboundSession'; Name=$proc.Name; PID=$_.OwningProcess; Cmd="$($_.RemoteAddress):$($_.RemotePort)"; Path='ESTABLISHED'}
}
}
# 5) Audit persistence: scheduled tasks & run keys referencing tunneling tools or temp paths
$tasks = Get-ScheduledTask | Where-Object {
$_.Actions.Execute -match 'ngrok|cloudflared|AppData\\Local\\Temp' -or
($_.Actions.Arguments -match '\.png|-R ')
}
foreach ($t in $tasks) { $Report += [pscustomobject]@{Type='Persistence-Task'; Name=$t.TaskName; PID=''; Cmd=$t.Actions.Execute; Path=$t.TaskPath} }
$Report | Format-Table -AutoSize
$Report | Export-Csv ".\TerminalFix_Triage_$env:COMPUTERNAME.csv" -NoTypeInformation
Write-Host "`nTriage complete. Review CSV output; preserve memory/image before wiping suspect hosts." -ForegroundColor Yellow
Remediation & Hardening
Since TerminalFix is a tradecraft campaign rather than a patchable vulnerability, remediation is about removing the attack surface and visibility gaps it exploits:
- Constrain PowerShell. Enforce Constrained Language Mode via WDAC/AppLocker for standard users, enable Script Block Logging and Module Logging (forward to your SIEM — this is what makes the stego-decoder visible), and require signed scripts where operationally feasible.
- Egress control is your kill chain breaker. Reverse tunnels die without outbound connectivity. Egress-filter at the proxy/firewall: block known tunnel domains (
*.ngrok.io,*.ngrok-free.app,*.trycloudflare.com,bore.pub,localhost.run,serveo.net) unless there is a documented business need, and alert on direct outbound 443 that bypasses the proxy. - Application control on user-writable paths. WDAC/AppLocker rules preventing unsigned executables from
%TEMP%,%APPDATA%, andC:\Users\Publicbreak the dropper stage even if the payload decodes successfully. - Block/detect dual-use tunneling binaries. Hash- and path-block ngrok, cloudflared, chisel, frp, and bore in your EDR policy for any host/user without an explicit exception.
- Ingest the published IOCs. Pull the PNG IOCs shared via the SANS ISC diary (https://isc.sans.edu/diary/rss/33318) and Microsoft's original blog into your threat intel platform; sweep proxy and DNS logs retroactively for the hosting domains, and hash-hunt the PNGs across mail gateways and download caches.
- Hunt long-lived outbound sessions. Reverse tunnels are characterized by long-duration, low-churn connections from endpoints to uncommon destinations. Build a Sentinel analytics rule on session duration > 60 minutes from non-browser processes.
- If compromised: assume full host compromise. Preserve volatile data (memory capture) before remediation, rotate credentials used on the host, review lateral movement from the host's network connections during the tunnel window, and reimage rather than clean — multistage loaders frequently leave secondary persistence.
Related Resources
Security Arsenal Penetration Testing Services AlertMonitor Platform Book a SOC Assessment vulnerability-management Intel Hub
Is your security operations ready?
Get a free SOC assessment or see how AlertMonitor cuts through alert noise with automated triage.