Chapter 39

Sliver, Havoc, and Metasploit

As detection of Cobalt Strike improved, red teams and threat actors diversified to alternative C2 frameworks: Sliver (an open-source Go-based framework), Havoc (a newer framework targeting the same operator market as Cobalt Strike), and Metasploit's Meterpreter (the original offensive framework). Each has distinctive network characteristics. Understanding all three alongside Cobalt Strike gives a detection engineer coverage of the majority of real-world C2 frameworks encountered in incident response.

Scenario

A nation-state intrusion uses Sliver as their primary implant framework. You see encrypted mTLS (mutual TLS) traffic on port 31337 — Sliver's default mTLS transport. Unlike Cobalt Strike, Sliver requires the client to present a certificate during the TLS handshake (mutual authentication). In PCAP: both sides present certificates during the handshake, the client certificate is generated by Sliver's internal CA, and the JA3 matches Sliver's Go TLS fingerprint. The port 31337 is also a distinctive default.

Framework Network Fingerprints

FrameworkDefault ProtocolDefault PortKey Identifier
Cobalt Strike (default)HTTP/HTTPS80/443Java TLS JA3, IE7 UA, /submit.php
Sliver (mTLS)Mutual TLS (raw TCP)31337mTLS (client cert), Go TLS JA3
Sliver (HTTP)HTTP/HTTPS80/443Go TLS JA3, /rpc.php URI
Sliver (DNS)DNS tunneling53High-frequency DNS to one domain
HavocHTTPS443Custom Demon agent JA3, specific staging URI
Metasploit (Meterpreter)HTTPS reverse shell443Specific JA3, large initial data transfer, no SNI
Metasploit (staged)HTTP4444Non-standard port, stage download, small MSF headers
Brute RatelHTTPS443Distinctive HTTP profile, specific URI patterns

Sliver Detection

bashsliver-detect.sh
#!/bin/bash
PCAP="$1"

echo "=== Sliver mTLS (port 31337 or non-standard TLS) ==="
# mTLS: TLS where client also presents a certificate (handshake type 11 from client side)
tshark -r "$PCAP" -n \
  -Y "tls.handshake.type==11 and tcp.srcport != 443 and tcp.srcport != 8443" \
  -T fields \
  -E separator="\t" \
  -e frame.time_epoch -e ip.src -e ip.dst -e tcp.srcport \
  | sort | uniq -c | sort -rn | head -20

echo ""
echo "=== Non-standard TLS ports with client certificates (Sliver mTLS) ==="
tshark -r "$PCAP" -n \
  -Y "ssl.handshake.certificate and not (tcp.port==443 or tcp.port==8443 or tcp.port==8080)" \
  -T fields \
  -E separator="\t" \
  -e ip.src -e ip.dst -e tcp.dstport \
  | sort | uniq -c | sort -rn | head -20

echo ""
echo "=== Sliver HTTP mode (Go UA, /rpc.php path) ==="
tshark -r "$PCAP" -n \
  -Y "http.user_agent contains \"Go-http-client\"" \
  -T fields \
  -E separator="\t" \
  -e frame.time_epoch -e ip.src -e ip.dst -e http.request.uri \
  | head -20

echo ""
echo "=== TLS with client certificate (mutual TLS) ==="
# Count Certificate messages sent by clients (not servers)
# Client certs appear before the server Certificate in the handshake
tshark -r "$PCAP" -n -Y "tcp.dstport!=443 and tls.handshake.type==11" \
  -T fields -E separator="\t" \
  -e ip.src -e ip.dst -e tcp.dstport \
  | sort | uniq -c | sort -rn | head -20

Metasploit Meterpreter Detection

bashmeterpreter-detect.sh
#!/bin/bash
PCAP="$1"

echo "=== Metasploit default port 4444 ==="
tshark -r "$PCAP" -n \
  -Y "tcp.port==4444" \
  -T fields \
  -E separator="\t" \
  -e frame.time_epoch -e ip.src -e ip.dst -e tcp.flags

echo ""
echo "=== Meterpreter HTTPS: TLS with no SNI, large initial data ==="
# Meterpreter often connects with no SNI and sends a large initial data blob
tshark -r "$PCAP" -n \
  -Y "tls.handshake.type==1 and not tls.handshake.extensions_server_name" \
  -T fields \
  -E separator="\t" \
  -e frame.time_epoch -e ip.src -e ip.dst -e tcp.dstport \
  | head -30

echo ""
echo "=== Known Metasploit JA3 ==="
# Metasploit uses Ruby's OpenSSL; JA3: de9f2c7fd25e1b3afad3e85a0226a4c4
echo "Known Metasploit JA3: de9f2c7fd25e1b3afad3e85a0226a4c4"
echo "Would need Zeek ssl.log or custom JA3 script to match"

echo ""
echo "=== Non-standard TLS ports (MSF often uses 4444, 8443) ==="
tshark -r "$PCAP" -n \
  -Y "tls and (tcp.port==4444 or tcp.port==8443 or tcp.port==9443)" \
  -T fields \
  -E separator="\t" \
  -e ip.src -e ip.dst -e tcp.dstport \
  | sort | uniq -c | sort -rn | head -20
Why Go-based C2 frameworks have distinctive TLS fingerprints

Sliver, Havoc, and several other newer C2 frameworks are written in Go. Go's crypto/tls standard library produces a distinctive JA3 fingerprint — the default cipher suites, extensions, and curves offered by Go's TLS client are different from Chrome (BoringSSL), Firefox (NSS), or Java. This means any C2 framework using Go's default TLS configuration can be identified by JA3 as "Go-based application" even if the operator hasn't customized anything else. The Go TLS default JA3 (which varies by Go version) is a starting point for clustering C2 traffic. Go developers can manually specify cipher suites and extensions to mimic browser fingerprints, but this requires explicit effort and is often not done in default framework configurations. When you see a Go TLS JA3 in your environment from a host that shouldn't be running Go applications, that's worth investigating.

Q & A

Q: How do I keep my C2 detection signatures current as new frameworks emerge?

C2 framework network signatures change with every new version and every operator customization. Strategies for staying current: (1) Subscribe to framework release notes: Sliver, Havoc, and Brute Ratel publish release notes; watch for changes to default profiles, ports, and TLS configurations. (2) Run frameworks in your own lab: the most reliable way to get current signatures is to run the framework yourself, capture traffic, and extract the JA3/JARM/URI patterns. This also gives you known-good data for testing your own detection stack. (3) Follow threat intel reports: Mandiant, CrowdStrike, Recorded Future, and similar firms regularly publish detailed C2 traffic analysis when they encounter new or updated frameworks in real intrusions. These reports include updated JA3 hashes, PCAP examples, and IOC lists. (4) Maintain a JA3 database: use a tool like Suricata with the ja3-and-ja3s rules, or Zeek with the ja3 package, and collect all JA3 hashes seen in your environment. When a new C2 framework appears in the wild, you can retroactively search your JA3 database to see if it appeared before the discovery.