THREAT-C2-TorMultiHopProxyEgress Microsoft Sentinel · KQL

Detect Command and Control via Tor Multi-Hop Anonymising Proxy Egress in Microsoft Sentinel

Adversaries route command-and-control traffic through Tor — a multi-hop anonymising proxy network — so that the true C2 endpoint is never visible in perimeter telemetry and cannot be blocklisted, sinkholed, or attributed from a destination IP alone. Operationally this takes three recognisable forms on an endpoint: (1) a Tor client process (tor.exe, the Tor Expert Bundle, or the Tor Browser bundle's embedded tor daemon) is executed and opens a local SOCKS5 listener, conventionally on TCP 9050 for the standalone daemon and TCP 9150 for the Tor Browser bundle, with the control port on 9051/9151; (2) that client establishes outbound TLS sessions to Tor relays, which by long-standing convention listen on ORPort 9001 and DirPort 9030, though relay operators are free to use any port and censorship-resistant deployments deliberately move to 443/80; and (3) malware or tooling then reaches an .onion service through the local SOCKS proxy, so the actual C2 hostname never appears in DNS. Pluggable transports (obfs4proxy, now shipped as lyrebird, plus meek-client and snowflake-client) exist specifically to make arm (2) unrecognisable by obfuscating or domain-fronting the relay handshake, which is why a detection that relies solely on relay-port egress will miss bridged clients and must be paired with process-level and local-listener signals. This detection is deliberately multi-armed for that reason: it scores Tor client binary execution, local SOCKS/control-port listener creation, and sustained outbound TCP to conventional relay ports, and alerts when two independent arms agree on the same device or when relay-port egress alone reaches meaningful volume. It differs from the T1090.003 base record — which describes the multi-hop proxy technique generically across Tor, I2P, and commercial VPN mesh chains — by encoding the concrete, field-level Tor artefacts that a SOC can actually query on each platform, and by explicitly accounting for the pluggable-transport evasion path that defeats port-only rules.

MITRE ATT&CK

Tactic
Command and Control

KQL Detection Query

Microsoft Sentinel (KQL)
kusto
let LookbackWindow = 24h;
let TorRelayPorts = dynamic([9001, 9030]);
let TorLocalSocksPorts = dynamic([9050, 9051, 9150, 9151]);
let TorClientBinaries = dynamic(["tor.exe", "obfs4proxy.exe", "lyrebird.exe", "meek-client.exe", "snowflake-client.exe", "conjure-client.exe"]);
let RelayEgress =
    DeviceNetworkEvents
    | where Timestamp > ago(LookbackWindow)
    | where ActionType == "ConnectionSuccess"
    | where Protocol =~ "Tcp"
    | where isnotempty(RemoteIP)
    | where ipv4_is_private(RemoteIP) == false
    | where RemotePort in (TorRelayPorts)
    | project Timestamp, DeviceId, DeviceName, Signal = "TorRelayPortEgress",
        Detail = strcat(InitiatingProcessFileName, " -> ", RemoteIP, ":", tostring(RemotePort));
let SocksListeners =
    DeviceNetworkEvents
    | where Timestamp > ago(LookbackWindow)
    | where ActionType == "ListeningConnectionCreated"
    | where LocalPort in (TorLocalSocksPorts)
    | project Timestamp, DeviceId, DeviceName, Signal = "TorSocksListener",
        Detail = strcat(InitiatingProcessFileName, " listening on ", tostring(LocalPort));
let TorClientProcesses =
    DeviceProcessEvents
    | where Timestamp > ago(LookbackWindow)
    | where FileName in~ (TorClientBinaries)
        or FolderPath contains @"\Tor Browser\"
        or ProcessCommandLine contains "SocksPort"
        or ProcessCommandLine contains "HiddenServiceDir"
        or ProcessCommandLine contains "torrc"
    | project Timestamp, DeviceId, DeviceName, Signal = "TorClientBinary",
        Detail = strcat(FileName, " | ", FolderPath);
union RelayEgress, SocksListeners, TorClientProcesses
| summarize
    EventCount = count(),
    SignalTypes = make_set(Signal),
    Details = make_set(Detail, 20),
    FirstSeen = min(Timestamp),
    LastSeen = max(Timestamp)
    by DeviceId, DeviceName
| extend HasRelayEgress = set_has_element(SignalTypes, "TorRelayPortEgress")
| extend HasSocksListener = set_has_element(SignalTypes, "TorSocksListener")
| extend HasTorBinary = set_has_element(SignalTypes, "TorClientBinary")
| extend TorScore = tolong(HasRelayEgress) + tolong(HasSocksListener) + tolong(HasTorBinary)
| where TorScore >= 2 or (HasRelayEgress and EventCount >= 5)
| extend DurationMinutes = datetime_diff('minute', LastSeen, FirstSeen)
| project FirstSeen, LastSeen, DeviceName, DeviceId, TorScore, EventCount, DurationMinutes,
    HasRelayEgress, HasSocksListener, HasTorBinary, SignalTypes, Details
| sort by TorScore desc, EventCount desc
high severity medium confidence

Detects Tor multi-hop proxy C2 egress on Microsoft Defender for Endpoint telemetry by scoring three independent arms and correlating them per device. Arm 1 uses DeviceNetworkEvents ConnectionSuccess records for outbound TCP to non-private RemoteIP addresses on the conventional Tor ORPort (9001) and DirPort (9030). Arm 2 uses DeviceNetworkEvents ListeningConnectionCreated records for a local SOCKS/control listener on 9050/9051 (standalone tor daemon) or 9150/9151 (Tor Browser bundle) — the arm that still fires when a bridged client with a pluggable transport avoids the conventional relay ports entirely. Arm 3 uses DeviceProcessEvents for execution of the Tor client or pluggable-transport binaries, a Tor Browser install path, or a command line referencing torrc/SocksPort/HiddenServiceDir. The rule fires when two arms agree on the same device, or when relay-port egress alone reaches five or more successful connections, which is well above an incidental single connection to a host that happens to serve on 9001. ipv4_is_private is used for the private-address exclusion rather than an 'in' list, because KQL 'in' performs string equality and cannot evaluate CIDR membership.

Data Sources

Process: Process CreationNetwork Traffic: Network Connection CreationCommand: Command ExecutionMicrosoft Defender for Endpoint advanced hunting (DeviceNetworkEvents, DeviceProcessEvents)

Required Tables

DeviceNetworkEventsDeviceProcessEvents

False Positives & Tuning

  • Privacy, journalism, legal, or threat-research teams with a documented business authorisation to run Tor Browser for open-source intelligence collection or dark-web monitoring
  • Security researchers and malware analysts operating sandboxes or detonation VMs that deliberately route sample traffic through Tor
  • Developers running local proxy or anonymity tooling that binds TCP 9050 for unrelated reasons, since these ports are not reserved and can be claimed by any application
  • Non-Tor services legitimately hosted on TCP 9001 or 9030 by a partner or SaaS provider, which will produce relay-port egress hits with no accompanying process or listener signal

Other platforms for THREAT-C2-TorMultiHopProxyEgress


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.

  1. Test 1Tor Client Bootstrap and Circuit Establishment (Linux)

    Expected signal: Process telemetry: execution of the tor binary with a command line containing SocksPort and DataDirectory. Network telemetry: a local listening socket on TCP 9050, plus multiple outbound TCP sessions to distinct public relay addresses during bootstrap — some on 9001/9030 and some on 443/80, since relay ORPorts are a convention rather than a requirement. On Linux the process arm is observed through MDE for Linux DeviceProcessEvents or auditd; the relay-port and listener arms are best corroborated with firewall or Zeek conn.log data from the same segment.

  2. Test 2Tor Client Binary Name Execution (Windows, Process Arm Only)

    Expected signal: Sysmon Event ID 1 and MDE DeviceProcessEvents: a process creation record with FileName tor.exe running from the user temp directory, with PowerShell as the parent process. No network telemetry is produced, and none should be expected.

  3. Test 3Tor SOCKS Proxy Listener Simulation (Windows, Listener Arm Only)

    Expected signal: MDE DeviceNetworkEvents: a ListeningConnectionCreated record with LocalPort 9150 and InitiatingProcessFileName powershell.exe. Sysmon has no listening-socket event type, so this test is not observable in the Sysmon-based SPL rule and should be validated on MDE telemetry or with live netstat output.


Response Playbook

Triage

  1. Establish which arms fired for the device. Two or three arms agreeing (Tor binary plus local SOCKS listener plus relay-port egress) is close to conclusive; relay-port egress alone with no Tor process on the host is far more likely to be an unrelated service listening on TCP 9001 or 9030 and should be verified against the destination's actual service before escalation.
  2. Identify the process that owns the local SOCKS listener and the relay connections. A Tor daemon launched by an interactive user from a Downloads or Desktop path indicates deliberate user installation; the same daemon launched by a service, scheduled task, or an unsigned parent process indicates malware embedding the Tor client for C2.
  3. Check whether the device belongs to a team with documented authorisation to use Tor (threat intelligence, legal, journalism, malware analysis). Absence of the device from that allowlist is the single strongest triage discriminator, because Tor has essentially no incidental presence on a standard corporate endpoint.
  4. Look for pluggable-transport binaries (obfs4proxy, lyrebird, meek-client, snowflake-client) alongside the Tor client. Their presence means the operator is deliberately evading network-level Tor blocking, which materially raises the likelihood of adversarial rather than curious use.
  5. Pull the preceding hours of process and file telemetry for the host, looking for archive creation, credential-store access, or bulk file reads that would indicate the Tor channel is being used to move collected data rather than only for interactive browsing.
  6. Search the host for a torrc configuration file and for any .onion address referenced in command lines, configuration files, or browser profile data — an .onion destination in a non-browser process is a direct indicator of onion-service C2 rather than user browsing.

Containment

  1. Isolate the endpoint via EDR network isolation before blocking at the perimeter. Tor clients are designed to survive path failures by rebuilding circuits through different relays and bridges, so a destination-IP block alone will not reliably sever the channel.
  2. Terminate the Tor client and any pluggable-transport processes, and preserve their process memory first if the investigation may need the in-memory circuit and configuration state.
  3. Block outbound TCP 9001 and 9030 to the internet at the perimeter as a baseline hygiene control, while documenting that this is a partial control only — bridged clients and relays operating on 443/80 will bypass it, which is precisely why the endpoint arms of this detection exist.
  4. Where the organisation has no legitimate Tor use case, enforce application control (WDAC, AppLocker, or the EDR equivalent) to block execution of tor.exe and the pluggable-transport binaries by name and by publisher. This is a far more durable control than any network-layer block.
  5. Rotate credentials used on the host during the activity window if any evidence of data collection or interactive C2 was found, since the anonymised channel makes post-hoc reconstruction of what was taken substantially harder.

Evidence Collection

  1. The torrc configuration file and the Tor data directory (containing cached relay descriptors, state, and any HiddenServiceDir material), which together reveal whether bridges were configured and whether the host itself published an onion service.
  2. Process memory of the running Tor client and any pluggable-transport process, captured before termination, which may retain circuit state and the target onion address.
  3. Full Sysmon Event ID 1 and Event ID 3 history for the host across the whole activity window rather than only the alerting window, since Tor clients bootstrap gradually and early relay connections often precede the alert threshold by hours.
  4. Firewall or NGFW session logs for the host, which capture relay connections on non-conventional ports (443/80) that the port-based arms of this detection deliberately do not flag.
  5. Prefetch, ShimCache, Amcache, and the user's Downloads directory, to establish how and when the Tor client or Tor Browser bundle arrived on the host and whether it was user-installed or dropped by another process.

Escalation Criteria

  • !The Tor client was launched by a service, scheduled task, or unsigned parent process rather than by an interactive user — this is embedded-C2 behaviour, not user browsing, and should go straight to incident response.
  • !Pluggable-transport binaries or a bridge configuration are present, indicating deliberate evasion of network-level Tor blocking.
  • !An .onion address appears in a non-browser process command line, configuration file, or memory, indicating onion-service command and control.
  • !The activity originates from a server, domain controller, jump host, or other privileged management system rather than a user workstation.
  • !The host also shows collection or staging behaviour (archive creation, credential access, bulk file reads) in the same window, indicating the anonymised channel is being used for exfiltration.
  • !The same Tor artefacts appear on multiple hosts in a short window, indicating tooling deployed by an operator rather than independent user choice.

Investigation Guide

Related Techniques

Forensic Artifacts

  • >torrc configuration file — records SocksPort, ORPort, UseBridges, ClientTransportPlugin, and any HiddenServiceDir, and is the single most informative artefact for determining intent.
  • >Tor data directory (cached-microdescs, cached-certs, state, lock) — establishes when the client first bootstrapped and which relays it learned about.
  • >Local listening socket on TCP 9050/9051 (standalone daemon) or 9150/9151 (Tor Browser bundle), recoverable from live triage or from DeviceNetworkEvents ListeningConnectionCreated records.
  • >Tor Browser bundle directory, typically user-writable and self-contained, which is why it commonly appears under Downloads or Desktop rather than Program Files.
  • >Pluggable-transport binaries (obfs4proxy, lyrebird, meek-client, snowflake-client) and their state files, which are the strongest available indicator of intentional censorship or detection evasion.
  • >Sysmon Event ID 3 and MDE DeviceNetworkEvents records for outbound TCP 9001/9030 to public addresses, and firewall session logs for relay traffic on non-conventional ports.

Tuning Guidance

Build the authorised-Tor allowlist first, before enabling this in alerting mode. Run the fleet-wide binary hunt over 30 days, identify every device with a documented business justification (threat intelligence, dark-web monitoring, malware analysis, legal, journalism), and exclude those DeviceIds explicitly rather than by user or subnet, since the justification attaches to the role and not the network location. After that exclusion the remaining volume is normally very low, because Tor has essentially no incidental presence on a managed endpoint — which means you can afford to lower the relay-egress-only threshold from five connections towards one or two and still keep the alert actionable. Do not treat the relay-port arm as the primary signal. Ports 9001 and 9030 are conventions, not requirements: relay operators may use any port, and censorship-resistant deployments deliberately run on 443 or 80 specifically to blend with ordinary HTTPS, so a port-only rule will produce false negatives on exactly the operators most worth catching. The process and local-listener arms are what preserve coverage in those cases, and if you must drop an arm for data-availability reasons, drop the relay-port arm rather than either of the others. Conversely, treat relay-port egress with no accompanying process or listener signal as low confidence and route it to a hunt queue rather than an alert queue, because unrelated services do legitimately listen on TCP 9001. If you operate a network sensor (Zeek, NGFW with TLS metadata) alongside endpoint telemetry, add a corroborating arm on Tor's TLS characteristics — long-lived sessions to many distinct public peers from one host within a short window is the network-layer shape of circuit building — but keep it as a corroborator rather than a standalone rule, since TLS fingerprinting of Tor is an ongoing arms race and degrades as the protocol evolves.


Hunting Queries

Fleet-wide baseline hunt for the presence of any Tor client or pluggable-transport binary over 30 days, independent of network telemetry. Because Tor has almost no incidental presence on a managed corporate endpoint, the output of this hunt is usually small enough to review host by host, and it is the correct starting point for building the authorised-user allowlist that this detection's tuning depends on. Reviewing the parent process is the key step: an interactive shell or Explorer parent means user installation, whereas a service or unsigned parent means embedded C2.

Hunting — KQL
kql
// Hunt: any Tor client or pluggable-transport binary executed anywhere in the fleet, regardless of network activity
DeviceProcessEvents
| where Timestamp > ago(30d)
| where FileName in~ ("tor.exe", "obfs4proxy.exe", "lyrebird.exe", "meek-client.exe", "snowflake-client.exe", "conjure-client.exe")
    or FolderPath contains @"\Tor Browser\"
    or ProcessCommandLine contains "torrc"
| summarize ExecutionCount = count(), Paths = make_set(FolderPath, 10), Parents = make_set(InitiatingProcessFileName, 10), Users = make_set(AccountName, 10), FirstSeen = min(Timestamp), LastSeen = max(Timestamp) by DeviceName, FileName
| sort by FirstSeen asc
Hunting — SPL
spl
index=windows sourcetype="XmlWinEventLog:Microsoft-Windows-Sysmon/Operational" EventCode=1
| eval ImageLower=lower(coalesce(Image, "unknown"))
| eval CmdLower=lower(coalesce(CommandLine, ""))
| where like(ImageLower, "%\\tor.exe") OR like(ImageLower, "%\\obfs4proxy.exe") OR like(ImageLower, "%\\lyrebird.exe") OR like(ImageLower, "%\\meek-client.exe") OR like(ImageLower, "%\\snowflake-client.exe") OR like(ImageLower, "%\\tor browser\\%") OR like(CmdLower, "%torrc%")
| stats count as ExecutionCount, values(ImageLower) as Paths, values(ParentImage) as Parents, values(User) as Users, min(_time) as FirstSeen, max(_time) as LastSeen by ComputerName
| eval FirstSeen=strftime(FirstSeen, "%Y-%m-%d %H:%M:%S"), LastSeen=strftime(LastSeen, "%Y-%m-%d %H:%M:%S")
| sort FirstSeen

Hunts for .onion hostnames surfacing in endpoint network telemetry. A correctly configured Tor client resolves onion addresses inside the circuit and never leaks them to the local resolver, so any .onion name that does reach the DNS layer (Sysmon Event ID 22) or the MDE RemoteUrl field means a non-Tor-aware process attempted the address directly — typically malware whose configuration embeds an onion C2 while the local Tor proxy is not yet running or has failed. That leak is a high-fidelity indicator and is worth alerting on independently of the volumetric arms above.

Hunting — KQL
kql
// Hunt: onion-service references reaching the network stack, which indicate C2 rendezvous rather than ordinary browsing
DeviceNetworkEvents
| where Timestamp > ago(30d)
| where isnotempty(RemoteUrl)
| where RemoteUrl endswith ".onion" or RemoteUrl contains ".onion:"
| summarize AttemptCount = count(), OnionTargets = make_set(RemoteUrl, 20), Processes = make_set(InitiatingProcessFileName, 10), FirstSeen = min(Timestamp), LastSeen = max(Timestamp) by DeviceName
| sort by AttemptCount desc
Hunting — SPL
spl
index=windows sourcetype="XmlWinEventLog:Microsoft-Windows-Sysmon/Operational" EventCode=22
| eval QueryLower=lower(coalesce(QueryName, ""))
| where like(QueryLower, "%.onion")
| stats count as AttemptCount, values(QueryLower) as OnionTargets, values(Image) as Processes, min(_time) as FirstSeen, max(_time) as LastSeen by ComputerName
| eval FirstSeen=strftime(FirstSeen, "%Y-%m-%d %H:%M:%S"), LastSeen=strftime(LastSeen, "%Y-%m-%d %H:%M:%S")
| sort - AttemptCount

Hunts for the local SOCKS proxy and control-port listeners that a Tor client creates on 9050/9051 (standalone daemon) or 9150/9151 (Tor Browser bundle). This is the highest-value arm against bridged clients, because pluggable transports change what the relay traffic looks like on the wire but do not change the fact that the client must expose a local SOCKS proxy for the calling application to use. Note the platform difference: MDE emits a dedicated ListeningConnectionCreated ActionType, whereas Sysmon has no listening-socket event, so the SPL variant approximates it by matching the local SOCKS port appearing as SourcePort on Event ID 3 loopback connections and should be corroborated with live-triage netstat output.

Hunting — KQL
kql
// Hunt: local SOCKS/control listeners on the conventional Tor ports, the arm that survives pluggable-transport evasion
DeviceNetworkEvents
| where Timestamp > ago(30d)
| where ActionType == "ListeningConnectionCreated"
| where LocalPort in (9050, 9051, 9150, 9151)
| summarize ListenerEvents = count(), Ports = make_set(LocalPort, 8), Processes = make_set(InitiatingProcessFileName, 8), ProcessPaths = make_set(InitiatingProcessFolderPath, 8), FirstSeen = min(Timestamp), LastSeen = max(Timestamp) by DeviceName
| sort by FirstSeen asc
Hunting — SPL
spl
index=windows sourcetype="XmlWinEventLog:Microsoft-Windows-Sysmon/Operational" EventCode=3
| eval SrcPort=tonumber(coalesce(SourcePort, "-1"))
| where SrcPort==9050 OR SrcPort==9051 OR SrcPort==9150 OR SrcPort==9151
| stats count as ListenerEvents, values(SrcPort) as Ports, values(Image) as Processes, min(_time) as FirstSeen, max(_time) as LastSeen by ComputerName
| eval FirstSeen=strftime(FirstSeen, "%Y-%m-%d %H:%M:%S"), LastSeen=strftime(LastSeen, "%Y-%m-%d %H:%M:%S")
| sort FirstSeen

Atomic Red Team Tests

Test 1 Tor Client Bootstrap and Circuit Establishment (Linux)
linux

Installs and runs a standalone Tor daemon with an explicit data directory and SOCKS port, allows it to bootstrap against the live Tor network, and then routes a single HTTP request through the local SOCKS5 proxy. This exercises all three detection arms at once: the tor process executes, a local SOCKS listener is created on 9050, and the client establishes outbound TCP sessions to public relays. Run only on an authorised lab host with permission to reach the Tor network from that network segment.

Command

bash
sudo apt-get install -y tor > /dev/null 2>&1 || sudo yum install -y tor > /dev/null 2>&1
mkdir -p /tmp/atomic-tor-t1090003 && chmod 700 /tmp/atomic-tor-t1090003
tor --SocksPort 9050 --DataDirectory /tmp/atomic-tor-t1090003 --RunAsDaemon 0 > /tmp/atomic-tor-t1090003/tor.log 2>&1 &
sleep 90
curl --max-time 30 --socks5-hostname 127.0.0.1:9050 -s https://check.torproject.org/api/ip || echo 'circuit not ready'
echo 'Tor client bootstrap test complete'

Cleanup

bash
pkill -f 'DataDirectory /tmp/atomic-tor-t1090003' 2>/dev/null; sleep 2; rm -rf /tmp/atomic-tor-t1090003

Expected Telemetry

Process telemetry: execution of the tor binary with a command line containing SocksPort and DataDirectory. Network telemetry: a local listening socket on TCP 9050, plus multiple outbound TCP sessions to distinct public relay addresses during bootstrap — some on 9001/9030 and some on 443/80, since relay ORPorts are a convention rather than a requirement. On Linux the process arm is observed through MDE for Linux DeviceProcessEvents or auditd; the relay-port and listener arms are best corroborated with firewall or Zeek conn.log data from the same segment.

Expected Detection

The KQL rule should score at least two arms for the host (TorClientBinary plus TorRelayPortEgress and/or TorSocksListener), giving TorScore >= 2 and firing regardless of the relay-connection count. If only relay-port egress is visible because process telemetry is unavailable on the host, the EventCount >= 5 branch should still fire once bootstrap has contacted five or more relays on 9001/9030. A bootstrap that connects exclusively to relays on 443 will not trigger the relay-port arm, which correctly demonstrates the blind spot documented in the tuning guidance.

Test 2 Tor Client Binary Name Execution (Windows, Process Arm Only)
windows

Copies a benign signed system binary to a file named tor.exe in the user temp directory and executes it. This exercises only the process-name arm of the detection without installing Tor or generating any network traffic, which makes it safe to run on a corporate endpoint where actually reaching the Tor network would be a policy violation. It is deliberately a partial test: it validates that the binary-name arm fires and that the alert routes correctly, and it must not be treated as end-to-end validation of the network arms.

Command

powershell
Copy-Item -Path "$env:SystemRoot\System32\whoami.exe" -Destination "$env:TEMP\tor.exe" -Force
& "$env:TEMP\tor.exe" | Out-Null
Write-Host 'Tor binary-name process arm test complete'

Cleanup

powershell
Remove-Item -Path "$env:TEMP\tor.exe" -Force -ErrorAction SilentlyContinue

Expected Telemetry

Sysmon Event ID 1 and MDE DeviceProcessEvents: a process creation record with FileName tor.exe running from the user temp directory, with PowerShell as the parent process. No network telemetry is produced, and none should be expected.

Expected Detection

The TorClientBinary arm sets to true for the host, giving TorScore = 1. On its own this is below the TorScore >= 2 alert threshold by design, so validate this test against the fleet-wide binary hunting query rather than the alert rule. To confirm the alert path end to end, pair this with the SOCKS listener test below so that two arms agree on the same host.

Test 3 Tor SOCKS Proxy Listener Simulation (Windows, Listener Arm Only)
windows

Binds a TCP listener on the loopback interface on port 9150, the port conventionally used by the Tor Browser bundle's embedded daemon, and holds it open long enough to be recorded. This exercises the local-listener arm — the arm that continues to work when a bridged client using a pluggable transport avoids the conventional relay ports entirely — without installing Tor or generating external traffic.

Command

powershell
$listener = [System.Net.Sockets.TcpListener]::new([System.Net.IPAddress]::Loopback, 9150)
$listener.Start()
Write-Host 'Listening on 127.0.0.1:9150 for 120 seconds'
Start-Sleep -Seconds 120
$listener.Stop()
Write-Host 'Tor SOCKS listener arm test complete'

Cleanup

powershell
Get-Process -Name powershell -ErrorAction SilentlyContinue | Where-Object { $_.Id -eq $PID } | Out-Null

Expected Telemetry

MDE DeviceNetworkEvents: a ListeningConnectionCreated record with LocalPort 9150 and InitiatingProcessFileName powershell.exe. Sysmon has no listening-socket event type, so this test is not observable in the Sysmon-based SPL rule and should be validated on MDE telemetry or with live netstat output.

Expected Detection

The TorSocksListener arm sets to true for the host, giving TorScore = 1 in isolation. Run it in the same 24-hour window as the binary-name test above so that two arms agree on the same DeviceName and the KQL rule crosses the TorScore >= 2 threshold and fires.

Related Detections