Detect Bulk Exfiltration Over Ad-Hoc TLS Tunnel (openssl s_client / stunnel / socat / ncat --ssl) in Google Chronicle
Adversaries who want to move bulk data out of an environment without reusing their C2 implant's channel frequently stand up an ad-hoc TLS tunnel with a general-purpose networking utility — `openssl s_client -connect`, `stunnel`, `socat OPENSSL-CONNECT:`, or `ncat --ssl` — and pipe staged archives through it to an attacker-controlled listener. The session is protected by the tool's own asymmetric TLS handshake against an attacker-supplied (usually self-signed) certificate, so the enterprise proxy cannot decrypt or content-inspect the payload, and the destination is typically a bare VPS IP on a non-standard port rather than a recognisable SaaS endpoint. This is distinct from the SFTP/rsync-over-SSH pattern already in this corpus (which keys on file-transfer clients on ports 21/22/989/990) and from cloud-storage sync exfiltration (which uses named provider APIs over 443): here the transport is a generic crypto utility that has no business making sustained outbound connections to the public internet from a workstation or application server. The reliable signal is process identity plus command-line intent (TLS-client arguments) correlated with repeated or long-lived public-IP sessions, since neither payload nor certificate subject can be trusted for classification. APT41 and FIN13 have both used dual-use networking tooling to relay collected data off Linux hosts, and ransomware affiliates routinely ship a static `socat`/`ncat` binary alongside their staging archive for exactly this purpose.
MITRE ATT&CK
- Tactic
- Exfiltration
YARA-L Detection Query
rule tls_asymmetric_non_c2_tunnel_exfiltration {
meta:
author = "df00tech"
description = "Detects bulk exfiltration over ad-hoc TLS tunnels built with openssl s_client, stunnel, socat or ncat --ssl"
severity = "HIGH"
mitre_attack_tactic = "Exfiltration"
mitre_attack_technique = "T1048.002"
events:
$e.metadata.event_type = "NETWORK_CONNECTION"
re.regex($e.principal.process.file.full_path, `(?i)(openssl|stunnel4?|ncat|socat)(\.exe)?$`)
re.regex($e.principal.process.command_line, `(?i)(s_client|--ssl|openssl-connect|-connect\s)`)
not net.ip_in_range_cidr($e.target.ip, "10.0.0.0/8")
not net.ip_in_range_cidr($e.target.ip, "172.16.0.0/12")
not net.ip_in_range_cidr($e.target.ip, "192.168.0.0/16")
$host = $e.principal.hostname
$dest = $e.target.ip
match:
$host, $dest over 1h
condition:
#e >= 5
} Chronicle YARA-L 2.0 rule matching NETWORK_CONNECTION UDM events whose initiating process is a generic TLS/networking utility invoked with TLS-client arguments, excluding internal destinations with net.ip_in_range_cidr, and firing only when the same host contacts the same external destination at least five times within an hour.
Data Sources
Required Tables
False Positives & Tuning
- Scheduled certificate monitoring producing repeated openssl connections to the same external endpoint
- Approved stunnel/socat TLS wrappers with sustained partner connectivity
- Interactive TLS troubleshooting sessions that happen to retry several times
Other platforms for THREAT-Exfiltration-TLSAsymmetricNonC2Tunnel
Testing Methodology
Validate this detection against 3 adversary techniques from Atomic Red Team. Each test below lists the behaviour to exercise and the telemetry you should expect to see. Executable commands and cleanup steps are available with Pro.
- Test 1Simulate openssl s_client TLS Tunnel Exfiltration to Loopback Listener
Expected signal: auditd / Sysmon for Linux: execve record for openssl with `s_client -connect 127.0.0.1:9443` in the command line, followed by a TCP connect event to port 9443 from the same process.
- Test 2Simulate socat OPENSSL-CONNECT Tunnel on Windows
Expected signal: Sysmon Event ID 1: socat.exe executing from C:\Windows\Temp with OPENSSL-CONNECT in the command line. Sysmon Event ID 3: outbound TCP connection to 127.0.0.1:9443 sharing the same ProcessGuid.
- Test 3Simulate ncat --ssl Encrypted Egress from macOS Endpoint
Expected signal: Endpoint Security Framework / EDR: process execution of ncat with `--ssl` in the argument vector, followed by an outbound TCP connection to port 9443 from the same process.
Response Playbook
Triage
- Pull the full command line for the tunnel process and extract the destination host:port and any `-cert`/`-key`/`CAfile` arguments — an attacker-supplied certificate or a bare IP with a high non-standard port is a strong indicator.
- Resolve the destination IP to an ASN and owner: consumer VPS/bulletproof-hosting ranges are far more likely to be exfiltration endpoints than recognised enterprise or CDN ranges.
- Determine the parent process and account: an openssl or socat invocation whose parent is a web shell, a scripting interpreter, or a service account with no interactive history is materially more suspicious than one launched from an admin's shell.
- Check the preceding hours for bulk file staging on the same host — archive creation (zip/7z/tar), unusual access to file shares, or database dump activity that would supply the payload for the tunnel.
- Retrieve actual transferred volume and session duration from netflow, proxy, or firewall logs for the destination IP, since EDR network telemetry alone does not record byte counts.
- Confirm whether the binary is a legitimate installed package or a dropped static build in a user-writable or temp directory — a stray `socat`/`ncat` in /tmp or C:\Users\Public is almost never legitimate.
Containment
- Block the destination IP and port at the egress firewall; because the tunnel carries its own TLS, proxy content inspection and URL filtering provide no coverage and IP/port blocking is the only effective network control.
- Isolate the host via EDR if a dropped tunnel binary is confirmed or the destination resolves to attacker-controlled infrastructure.
- Quarantine the tunnel binary if it was dropped rather than installed, and hash-block it fleet-wide to catch parallel deployments on other hosts.
- Rotate credentials, API keys, and certificates that were resident on the host, treating anything reachable from the compromised account as exposed until the transferred volume is bounded.
Evidence Collection
- Full process command line, parent process chain, and binary hash/path for the tunnel utility
- Sysmon Event ID 3 / DeviceNetworkEvents records for the destination IP: timestamps, ports, and connection counts establishing session span
- Netflow, firewall, or proxy session records for the destination IP supplying the byte volume that endpoint telemetry lacks
- Staged archives or dump files created shortly before the tunnel session, including their file listings and total on-disk size to bound the exfiltrated scope
- Any attacker-supplied certificate, key, or stunnel configuration file referenced on the command line, which frequently pivots to other infrastructure
- Shell history (.bash_history, PowerShell ScriptBlock logs / Event ID 4104) showing how the tunnel was constructed and what was piped through it
Escalation Criteria
- !Tunnel binary was dropped into a temp or user-writable directory rather than installed by a package manager or approved software deployment
- !Destination IP is on a consumer VPS/bulletproof-hosting ASN with no business relationship, or matches known threat intelligence
- !Tunnel session correlates with bulk archive creation or large-scale file share / database access on the same host
- !The same destination IP is contacted by more than one internal host, indicating a coordinated campaign rather than a single misconfiguration
- !Parent process is a web shell, scripting interpreter, or remote-management agent rather than an interactive administrator session
Investigation Guide
Related Techniques
Forensic Artifacts
- >
Sysmon Event ID 1 and Event ID 3 records correlated on ProcessGuid, giving command line plus destination for the same process instance - >
Dropped tunnel binaries (socat, ncat, stunnel, statically linked openssl) in temp or user-writable directories with recent creation timestamps - >
stunnel configuration files (stunnel.conf) and attacker-supplied certificate/key material referenced by the command line - >
Shell history and PowerShell ScriptBlock logs (Event ID 4104) showing pipe constructions such as `tar czf - /data | openssl s_client -connect` - >
Staged archive files and their creation times, bounding the volume and scope of collected data - >
Netflow/firewall session records for the destination IP, which are usually the only source of transferred byte counts
Tuning Guidance
Start by baselining which hosts legitimately run openssl, stunnel, socat, or ncat against the internet — in most estates this is a small set of build servers, monitoring hosts, and admin jump boxes, and excluding them by device group removes the bulk of the noise without weakening workstation and application-server coverage. Keep the command-line argument filter in place: an unqualified process-name match on `openssl` will fire constantly on certificate tooling and TLS troubleshooting. Prefer the session-shape signals (repeated connections, 30+ minute span, more than two distinct destination ports) over any single connection, because a lone `openssl s_client -connect partner.example.com:443` is routine debugging. Always use ipv4_is_in_range / cidrmatch / INCIDR for the internal-range exclusions rather than KQL `in` or SPL `IN`, which compare strings and will silently fail to exclude anything. Because MDE DeviceNetworkEvents carries no byte-count column, wire netflow, firewall, or proxy session data into the triage step so analysts can bound transferred volume; if you have that data centrally, promote a volume threshold into the rule itself for higher precision. Finally, pair this with T1560.001 archive-staging detections — the combination of a fresh archive followed by a sustained TLS tunnel to a VPS is far higher-fidelity than either signal alone. Ad-hoc TLS tunnels are typically short-campaign infrastructure — the destination IP is usually seen for only a few days and rarely resolves to a domain — so first-seen and low-day-count logic outperforms reputation feeds here.
Hunting Queries
Builds a 30-day baseline of external destinations contacted by generic TLS/networking utilities and highlights destinations that are either newly seen (active on three days or fewer) or contacted by more than one host — the two shapes that separate campaign infrastructure from an engineer's routine certificate check.
// Baseline every public-IP connection made by generic TLS/networking utilities over 30 days
DeviceNetworkEvents
| where Timestamp > ago(30d)
| where RemoteIPType == "Public"
| where InitiatingProcessFileName in~ ("openssl.exe", "openssl", "stunnel.exe", "stunnel", "stunnel4", "ncat.exe", "ncat", "socat", "socat.exe")
| summarize Connections=count(), Days=dcount(bin(Timestamp, 1d)), Ports=make_set(RemotePort, 15), Hosts=dcount(DeviceName) by InitiatingProcessFileName, RemoteIP
| where Days <= 3 or Hosts > 1
| sort by Connections desc index=wineventlog sourcetype="XmlWinEventLog:Microsoft-Windows-Sysmon/Operational" EventCode=3 earliest=-30d
Image IN ("*\\openssl.exe", "*\\stunnel.exe", "*\\ncat.exe", "*\\socat.exe")
| where NOT cidrmatch("10.0.0.0/8", DestinationIp) AND NOT cidrmatch("172.16.0.0/12", DestinationIp) AND NOT cidrmatch("192.168.0.0/16", DestinationIp)
| stats count AS Connections, dc(host) AS Hosts, values(DestinationPort) AS Ports BY Image, DestinationIp
| sort - Connections Hunts for TLS tunnel utilities executing from temporary or user-writable directories rather than a managed install path, which is the clearest discriminator between attacker-dropped tooling and an approved openssl/stunnel deployment.
// Find tunnel utilities running from temp or user-writable paths, a strong dropped-binary signal
DeviceProcessEvents
| where Timestamp > ago(30d)
| where FileName in~ ("openssl.exe", "openssl", "stunnel.exe", "stunnel", "ncat.exe", "ncat", "socat", "socat.exe")
| where FolderPath has_any ("\\Temp\\", "\\Users\\Public\\", "\\AppData\\", "\\ProgramData\\", "/tmp/", "/var/tmp/", "/dev/shm/")
| project Timestamp, DeviceName, AccountName, FileName, FolderPath, ProcessCommandLine, InitiatingProcessFileName, SHA256
| sort by Timestamp desc index=wineventlog sourcetype="XmlWinEventLog:Microsoft-Windows-Sysmon/Operational" EventCode=1 earliest=-30d
Image IN ("*\\openssl.exe", "*\\stunnel.exe", "*\\ncat.exe", "*\\socat.exe")
(Image="*\\Temp\\*" OR Image="*\\Users\\Public\\*" OR Image="*\\AppData\\*" OR Image="*\\ProgramData\\*")
| table _time, host, User, Image, CommandLine, ParentImage, SHA256
| sort - _time Atomic Red Team Tests
Stands up a local TLS listener with a self-signed certificate and pipes a staged test archive through `openssl s_client -connect`, reproducing the T1048.002 ad-hoc TLS exfiltration pattern entirely on loopback without contacting external infrastructure.
Command
openssl req -x509 -newkey rsa:2048 -keyout /tmp/atomic_t1048002.key -out /tmp/atomic_t1048002.crt -days 1 -nodes -subj "/CN=atomic-test" 2>/dev/null
dd if=/dev/urandom of=/tmp/atomic_t1048002_stage.bin bs=1M count=5 2>/dev/null
openssl s_server -quiet -cert /tmp/atomic_t1048002.crt -key /tmp/atomic_t1048002.key -accept 9443 > /tmp/atomic_t1048002_received.bin 2>/dev/null &
sleep 2
openssl s_client -quiet -connect 127.0.0.1:9443 -verify_return_error 0 < /tmp/atomic_t1048002_stage.bin 2>/dev/null || true
sleep 2
echo 'Atomic test THREAT-Exfiltration-TLSAsymmetricNonC2Tunnel (openssl s_client) complete - check auditd / Sysmon-for-Linux telemetry' Cleanup
pkill -f 'openssl s_server' 2>/dev/null; rm -f /tmp/atomic_t1048002.key /tmp/atomic_t1048002.crt /tmp/atomic_t1048002_stage.bin /tmp/atomic_t1048002_received.bin Expected Telemetry
auditd / Sysmon for Linux: execve record for openssl with `s_client -connect 127.0.0.1:9443` in the command line, followed by a TCP connect event to port 9443 from the same process.
Expected Detection
KQL/SPL match on InitiatingProcessFileName `openssl` with `s_client`/`-connect` in the command line correlated to a network connection; against a public destination this would satisfy the ConnectionCount/SessionSpanMinutes thresholds and raise ThreatType Exfil_AsymmetricEncryptedNonC2Tunnel.
Executes the Windows socat build with an OPENSSL-CONNECT target against a loopback listener, reproducing the dropped-static-binary tunnel pattern that ransomware affiliates ship alongside their staging archive.
Command
fsutil file createnew C:\Windows\Temp\atomic_t1048002_stage.bin 5000000
Start-Process -FilePath "C:\Windows\Temp\socat.exe" -ArgumentList 'OPENSSL-LISTEN:9443,cert=C:\Windows\Temp\atomic.pem,verify=0,fork','OPEN:C:\Windows\Temp\atomic_t1048002_received.bin,create' -WindowStyle Hidden
Start-Sleep -Seconds 2
& "C:\Windows\Temp\socat.exe" -u FILE:C:\Windows\Temp\atomic_t1048002_stage.bin OPENSSL-CONNECT:127.0.0.1:9443,verify=0 Cleanup
Get-Process socat -ErrorAction SilentlyContinue | Stop-Process -Force; Remove-Item C:\Windows\Temp\atomic_t1048002_stage.bin, C:\Windows\Temp\atomic_t1048002_received.bin -Force -ErrorAction SilentlyContinue Expected Telemetry
Sysmon Event ID 1: socat.exe executing from C:\Windows\Temp with OPENSSL-CONNECT in the command line. Sysmon Event ID 3: outbound TCP connection to 127.0.0.1:9443 sharing the same ProcessGuid.
Expected Detection
SPL EventCode=1/EventCode=3 ProcessGuid join fires on socat.exe with OPENSSL-CONNECT; the temp-directory execution path also matches the dropped-binary hunting query.
Uses `ncat --ssl` to push a staged file through a TLS-wrapped session to a loopback listener, covering the macOS variant of ad-hoc encrypted non-C2 exfiltration where openssl and socat may not be present.
Command
mkdir -p /tmp/atomic_t1048002
dd if=/dev/urandom of=/tmp/atomic_t1048002/stage.bin bs=1m count=5 2>/dev/null
ncat --ssl --listen 127.0.0.1 9443 > /tmp/atomic_t1048002/received.bin 2>/dev/null &
sleep 2
ncat --ssl 127.0.0.1 9443 < /tmp/atomic_t1048002/stage.bin 2>/dev/null || true
sleep 2
echo 'Atomic test THREAT-Exfiltration-TLSAsymmetricNonC2Tunnel (ncat --ssl) complete - check ESF/EDR process and network telemetry' Cleanup
pkill -f 'ncat --ssl' 2>/dev/null; rm -rf /tmp/atomic_t1048002 Expected Telemetry
Endpoint Security Framework / EDR: process execution of ncat with `--ssl` in the argument vector, followed by an outbound TCP connection to port 9443 from the same process.
Expected Detection
KQL/SPL match on InitiatingProcessFileName `ncat` with `--ssl` in the command line correlated to a network connection, contributing to the ConnectionCount threshold when repeated against an external destination.
Related Detections
Tactic Hub
Detection Variants (3)
Different telemetry and tradecraft for the same technique — pick the one that matches the data you collect.
- THREAT-Exfiltration-GPGAsymmetricArchiveExfilAsymmetric Public-Key Encrypted Archive Exfiltration (GPG/OpenSSL/age)
- THREAT-PGPEmail-AsymmetricEncryptedExfilData Exfiltration via PGP/GPG-Encrypted Email Attachments
- THREAT-Ransomware-AffiliateExfilToolingRansomware-Affiliate Custom Exfiltration Tooling (StealBit & Exmatter)