← All posts
Hunting Sliver C2 with Microsoft Defender XDR and Sentinel: A Practical Guide

Hunting Sliver C2 with Microsoft Defender XDR and Sentinel: A Practical Guide

Sun Feb 01 updated Mon May 25 14 min read
threat-huntingdefender-xdrsentinelc2-detectionsliveradvanced-huntingkql

When Bishop Fox released Sliver as an open-source C2 framework, it was meant for legitimate red team operations. Fast forward to 2026, and we're seeing Sliver adopted by ransomware operators and APT groups including APT29 (Cozy Bear). The UK NCSC, Microsoft, and multiple DFIR teams have confirmed it: Sliver isn't just a red team exercise anymore. It's a live threat.

From my MDR work, I've learned that C2 detection is the difference between catching an intrusion during initial access and responding to a full-blown ransomware incident. Today I'm sharing practical hunting strategies for Sliver that focus on behaviors, not fragile IOCs.

Why is Sliver hard to detect with traditional tooling?

Traditional detection breaks against Sliver for three structural reasons: Go binaries defeat AV signature matching, configurable transports defeat IOC lists, and in-memory execution defeats disk-based EDR rules. Detection that survives these properties is behavioural — it targets what the implant does on the host and the network, not the bytes of the implant itself. The detections below all share that property and are stable across Sliver profile changes.

Sliver presents unique detection challenges:

  • Go-based implants: Static compilation makes traditional AV signature detection difficult
  • Multiple C2 protocols: HTTP/HTTPS, DNS, mTLS, WireGuard, and TCP
  • Highly configurable: URLs, certificates, ports, and encoders can be changed per campaign
  • In-memory execution: Supports process injection and token impersonation
  • Custom encoders: Base32/58/64, hex, and even "English words" encoding for DNS

The good news? These same features create detectable behavioral patterns that persist regardless of configuration.

Where Sliver maps in MITRE ATT&CK

Sliver behaviour ATT&CK technique Defender XDR table
HTTPS C2 with default profile T1071.001 — Application Layer Protocol: Web Protocols DeviceNetworkEvents
DNS tunnelling T1071.004 — Application Layer Protocol: DNS DeviceNetworkEvents (DnsQueryResponse)
mTLS operator channel T1573.002 — Encrypted Channel: Asymmetric Cryptography DeviceNetworkEvents
Shellcode into notepad T1055.012 — Process Injection: Process Hollowing DeviceNetworkEvents + DeviceProcessEvents
Interactive shell T1059.001 — PowerShell DeviceProcessEvents
WireGuard pivot T1572 — Protocol Tunneling DeviceNetworkEvents

Endpoint hunting: what does Sliver do on the host?

The most distinctive on-host behaviours are PowerShell spawning patterns from Sliver's interactive shell, process injection into notepad.exe, and the appearance of large unsigned Go binaries running with network activity. None of these are unique to Sliver in isolation, but each is sufficiently unusual that an analyst chasing a single hit will typically find something worth escalating.

Detecting Sliver's PowerShell Patterns

Sliver's interactive shell frequently spawns PowerShell with specific UTF-8 encoding arguments:

// Hunt for Sliver's characteristic PowerShell spawning pattern
DeviceProcessEvents
| where Timestamp > ago(30d)
| where FileName in~ ("powershell.exe", "pwsh.exe")
| where ProcessCommandLine has_all ("-NoExit", "UTF8")
    or ProcessCommandLine has "[System.Text.Encoding]::UTF8"
| extend ParentInfo = strcat(InitiatingProcessFileName, " (", InitiatingProcessFolderPath, ")")
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, ParentInfo
| where InitiatingProcessFileName !in~ ("code.exe", "devenv.exe", "WindowsTerminal.exe")

Hunting Process Injection via Notepad

Sliver's default execute-shellcode command injects into notepad.exe. This creates an obvious behavioral anomaly:

// Notepad with network connections = process injection indicator
DeviceNetworkEvents
| where Timestamp > ago(30d)
| where InitiatingProcessFileName =~ "notepad.exe"
| where RemoteIPType != "Loopback"
| join kind=inner (
    DeviceProcessEvents
    | where Timestamp > ago(30d)
    | where FileName =~ "notepad.exe"
    | project DeviceId, NotepadPID = ProcessId, NotepadParent = InitiatingProcessFileName,
        NotepadCmd = ProcessCommandLine, NotepadStart = Timestamp
) on DeviceId
| where InitiatingProcessId == NotepadPID
| project Timestamp, DeviceName, RemoteIP, RemotePort, RemoteUrl,
    NotepadParent, NotepadCmd, NotepadStart
| extend Alert = "Notepad.exe making external network connections - possible injection"

This is one of the highest-signal queries in the Defender XDR Advanced Hunting toolbox — false-positive rate is near zero on a fleet of any size, because notepad.exe simply does not make external network calls during legitimate use.

Detecting Large Unsigned Go Binaries

Sliver implants are statically compiled Go binaries. They're typically larger than normal Windows executables:

// Hunt for suspicious large unsigned executables making network calls
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType == "FileCreated"
| where FileName endswith ".exe"
| where FileSize > 5000000 // > 5MB (Go binaries are chunky)
| where FolderPath !has_any ("\\Program Files", "\\Windows\\", "\\Microsoft")
| join kind=inner (
    DeviceProcessEvents
    | where Timestamp > ago(30d)
    | where ProcessVersionInfoCompanyName == "" or isempty(ProcessVersionInfoCompanyName)
    | project DeviceId, SHA256, ProcessStart = Timestamp,
        IsNetworkActive = (InitiatingProcessFileName != "")
) on DeviceId, SHA256
| project Timestamp, DeviceName, FileName, FolderPath, FileSize, SHA256, ProcessStart
| summarize FirstSeen = min(Timestamp), Devices = dcount(DeviceName) by FileName, SHA256, FileSize

Network hunting: what are Sliver's C2 fingerprints?

The four network signals worth hunting are DNS tunnelling (abnormally long first labels), HTTP C2 profile (default file-extension mix), mTLS on the default operator port (TCP 31337), and unsanctioned WireGuard traffic. Sliver supports many other transports, but these four cover the vast majority of real-world deployments because operators rarely customise every profile element before delivery.

DNS Tunneling Detection

Sliver encodes data in DNS queries using Base58/Base32. This creates abnormally long subdomain labels:

// Detect DNS tunneling patterns consistent with Sliver
DeviceNetworkEvents
| where Timestamp > ago(7d)
| where ActionType == "DnsQueryResponse"
| extend Labels = split(RemoteUrl, ".")
| extend FirstLabel = tostring(Labels[0])
| extend FirstLabelLength = strlen(FirstLabel)
| where FirstLabelLength > 40 // Sliver's Base58 creates long labels
| summarize
    QueryCount = count(),
    UniqueSubdomains = dcount(FirstLabel),
    AvgLabelLength = avg(FirstLabelLength),
    Devices = dcount(DeviceName)
    by ParentDomain = strcat(Labels[-2], ".", Labels[-1]), bin(Timestamp, 1h)
| where UniqueSubdomains > 50 and AvgLabelLength > 35
| extend Alert = "High-entropy DNS subdomain activity - possible Sliver DNS C2"

HTTP C2 Profile Detection

Sliver's default HTTP profile uses specific file extensions for different message types:

// Hunt for Sliver's default HTTP C2 file extension patterns
let sliverExtensions = dynamic([".woff", ".html", ".js", ".php", ".png"]);
DeviceNetworkEvents
| where Timestamp > ago(7d)
| where ActionType == "ConnectionSuccess"
| where RemotePort in (80, 443, 8080, 8443)
| where RemoteUrl has_any (sliverExtensions)
| extend FileExt = extract(@"(\.\w+)(?:\?|$)", 1, RemoteUrl)
| where FileExt in (sliverExtensions)
| summarize
    Hits = count(),
    Extensions = make_set(FileExt),
    UniqueUrls = dcount(RemoteUrl)
    by DeviceName, RemoteIP, bin(Timestamp, 1h)
| where array_length(Extensions) >= 3 // Multiple Sliver extensions to same IP
| extend Alert = "HTTP traffic pattern matches Sliver C2 profile"

mTLS/Multiplayer Listener Detection

Sliver's operator port defaults to TCP 31337 with predictable certificate attributes:

// Hunt for Sliver mTLS default port and certificate patterns
DeviceNetworkEvents
| where Timestamp > ago(30d)
| where RemotePort == 31337
| where ActionType == "ConnectionSuccess"
| summarize
    ConnectionCount = count(),
    FirstSeen = min(Timestamp),
    LastSeen = max(Timestamp)
    by DeviceName, RemoteIP, InitiatingProcessFileName
| where ConnectionCount > 5
| extend Alert = "Multiple connections to port 31337 - possible Sliver mTLS C2"

WireGuard Detection (Unsanctioned Tunnels)

If WireGuard isn't approved in your environment, any WireGuard traffic is suspicious:

// Detect unauthorized WireGuard traffic
DeviceNetworkEvents
| where Timestamp > ago(7d)
| where RemotePort == 51820 // WireGuard default
| where InitiatingProcessFileName !in~ ("wireguard.exe", "wg.exe") // Known legitimate
| project Timestamp, DeviceName, RemoteIP, RemotePort, InitiatingProcessFileName
| extend Alert = "Potential unauthorized WireGuard tunnel - investigate for C2"

How do I build a composite Sliver detection in Sentinel?

The right operational pattern is a single composite Sentinel scheduled rule that combines the host and network signals into a per-device score, and only alerts when the score crosses a threshold. This eliminates the per-signal false-positive noise (an ordinary CDN can produce one of the indicators) and produces a high-confidence alert when a device shows the chain of indicators the framework actually leaves behind.

// Sliver Composite Hunt - Sentinel Scheduled Rule
let DNSTunneling = DeviceNetworkEvents
| where ActionType == "DnsQueryResponse"
| extend FirstLabelLen = strlen(tostring(split(RemoteUrl, ".")[0]))
| where FirstLabelLen > 40
| summarize DNSScore = count() by DeviceId, DeviceName;
let HTTPPattern = DeviceNetworkEvents
| where RemoteUrl has_any (".woff", ".html", ".js", ".php", ".png")
| summarize HTTPScore = count() by DeviceId, DeviceName;
let ProcessInjection = DeviceNetworkEvents
| where InitiatingProcessFileName =~ "notepad.exe"
| where RemoteIPType != "Loopback"
| summarize InjectionScore = count() by DeviceId, DeviceName;
// Correlate across all signals
DeviceInfo
| join kind=leftouter DNSTunneling on DeviceId
| join kind=leftouter HTTPPattern on DeviceId
| join kind=leftouter ProcessInjection on DeviceId
| extend TotalScore = coalesce(DNSScore, 0) + coalesce(HTTPScore, 0) + (coalesce(InjectionScore, 0) * 10)
| where TotalScore > 20
| project DeviceName, TotalScore, DNSScore, HTTPScore, InjectionScore
| order by TotalScore desc

The InjectionScore * 10 weighting reflects the fact that notepad-with-network is itself a near-binary indicator — even a single hit is worth more than dozens of borderline DNS hits.

Response Playbook

When you get a hit:

  1. Isolate immediately: Use Defender XDR device isolation
  2. Collect memory: Sliver leaves artifacts in memory (the Go runtime)
  3. Trace the parent: What spawned the suspicious process?
  4. Network forensics: Identify the C2 destination before it rotates
  5. Scope horizontally: Check for lateral movement indicators

Key Takeaways

  • Behavior over IOCs: Sliver's configurability makes IOC-based detection fragile
  • DNS is your friend: DNS tunneling patterns are hard to mask
  • Watch notepad.exe: Network-active notepad is almost always malicious
  • Correlate signals: Individual indicators may be weak. Combined, they're strong.
  • Port 31337: Still surprisingly common in default Sliver deployments

The gap between red team tools and real-world threats continues to shrink. Sliver is just the latest example. Build these hunts into your detection pipeline now, before you need them in an incident. The same correlate-multiple-signals discipline applies to initial-access hunting — see the Tsundere Bot post for the IAB side of the kill chain, and the SharePoint AiTM playbook for identity-layer detection that pairs cleanly with this host-and-network work.


Questions about these detections? Reach out on LinkedIn.

Key takeaways

  • Sliver's strength is configurability — IOC-based detection ages out fast, but the underlying behaviours are stable across profiles.
  • Notepad.exe with an outbound TCP connection is the single highest-fidelity signal of Sliver's default `execute-shellcode` injection target.
  • DNS C2 leaks via abnormally long first labels — Base58 encoding produces labels well over 40 characters that ordinary services do not generate.
  • Sliver's default mTLS operator port is 31337 — surprisingly often left at default in real-world deployments.
  • Composite scoring across DNS, HTTP profile, and process-injection signals catches Sliver even when any single signal is below threshold.

FAQ

What is Sliver C2?

Sliver is an open-source cross-platform command-and-control (C2) framework written in Go and originally released by Bishop Fox for legitimate red-team operations. It supports multiple C2 transports (HTTP/HTTPS, DNS, mTLS, WireGuard, TCP), in-memory execution, process injection, and a wide range of post-exploitation modules. Public threat-intelligence reporting from the UK NCSC, Microsoft, and multiple DFIR teams has confirmed its adoption by ransomware operators and state-aligned groups including APT29 (Cozy Bear).

Why is Sliver harder to detect than commodity malware?

Three reasons. First, Go binaries are statically linked and produce large, low-entropy executables that defeat many traditional AV signatures. Second, Sliver supports multiple transports and per-campaign profile changes (URLs, certificates, ports, encoders), so IOC-based detection ages out within days. Third, Sliver implants run in memory and can do process injection or token impersonation, leaving minimal disk artefacts. Behaviour-based detection — what the implant does, not what it is — outperforms IOC matching.

Why is notepad.exe with a network connection a strong Sliver signal?

Sliver's default `execute-shellcode` post-exploitation command injects shellcode into `notepad.exe`. Notepad in normal Windows usage has no business making outbound network connections beyond loopback — Microsoft's text editor does not phone home. When Defender XDR's `DeviceNetworkEvents` shows notepad.exe initiating a successful external TCP connection, you are almost always looking at process injection, and Sliver is the most common attribution in 2026.

How does Sliver use DNS for C2?

Sliver supports DNS tunnelling where the implant encodes data into DNS query subdomains and the operator's authoritative DNS server decodes them. Sliver supports several encoders including Base32, Base58, Base64 and an 'English words' encoder. The most reliable detectable artefact is abnormally long first-label segments — Base58 encoding routinely produces labels in the 40-60 character range, which ordinary services (CDN sharding, DNS-based discovery) almost never reach.

What is port 31337 and why does it matter?

TCP/31337 is Sliver's default 'multiplayer' / operator port, used for the encrypted gRPC channel between the Sliver server and operator clients. It's also commonly observed as the listener for mTLS implant callbacks in default configurations. While the port is trivially changeable, public scanning data and IR reports show that a meaningful fraction of real Sliver deployments leave it at default — making a focused hunt on port 31337 surprisingly productive.

Can a single scheduled query catch Sliver reliably?

A single-signal query (only DNS tunnelling, or only HTTP profile matching) generates too much noise to alert on. A composite query that scores devices across multiple signals — DNS-label length, default HTTP extension profile, notepad-network injection, port 31337 — pushes the per-device confidence high enough for sev-2 alerting. The composite Sentinel rule at the bottom of this post is the practical implementation.