Emotet Traffic Walkthrough
Emotet is a modular banking trojan and loader that delivers secondary payloads (QBot, IcedID, Ryuk). Its network behavior is distinctive: initial HTTP download from a compromised WordPress site, POST-based C2 check-ins with a specific URL pattern, and SMB worm propagation across the internal network. This walkthrough traces an Emotet infection from initial download through module delivery and lateral spread.
A Suricata alert fires: "ET MALWARE Emotet HTTP C2 Check-in" from 10.0.2.15. You pull the Zeek logs for that host and trace the infection backwards to a Word macro document downloaded 20 minutes earlier, and forwards to 6 internal hosts that received SMB spreader connections in the following 90 minutes.
Emotet Traffic Signatures
Emotet Network Timeline
═══════════════════════════════════════════════════════════════════
T+0:00 — Malicious document delivery (email → Outlook download)
└── smtp.log: inbound email with .docm or .xlsm attachment
└── http.log: Outlook opens URL from email body
OR: files.log: .docm file extracted from email
T+0:05 — Macro executes, downloads Emotet dropper
└── http.log: GET from 10.0.2.15 to compromised WordPress site
URI pattern: /wp-content/uploads/2024/01/[random_filename].exe
UA: PowerShell or cmd.exe UA
Response: 200 OK, ~120KB binary
T+0:06 — Emotet binary runs, initial C2 contact
└── http.log: POST to one of several hardcoded C2 IPs
URI: /[random_string]/
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 Windows NT (legitimate-looking)
Request: ~100 bytes (host fingerprint + bot ID)
Response: ~100-2000 bytes (instructions or module)
T+0:10 — Module delivery (Spam module or SMB spreader)
└── Additional HTTP POSTs to C2 IPs → larger response (module binary)
T+0:15 — SMB spreader activates
└── conn.log: 10.0.2.15 → many internal IPs on port 445
Using legitimate NTLM credentials harvested from host
Fan-out to 6+ unique internal hosts in 10 minutes
T+1:30 — Secondary payload (QBot) delivery
└── Additional HTTP GET/POST from infected internal hosts
└── QBot C2 uses encrypted POST similar to Emotet
Emotet Detection and Investigation
#!/bin/bash
LOG_DIR="${1:-/opt/zeek/logs/current}"
SUSPECT="${2:-10.0.2.15}"
echo "=== 1. Initial dropper download (check before infection time) ==="
# Look for executable downloads to suspect host
awk 'NR>8 && !/^#/' "$LOG_DIR/http.log" 2>/dev/null | \
awk -v src="$SUSPECT" -F'\t' '
$3==src &&
($9 ~ /\.exe|\.dll|\.zip|\.ps1|\.bat/ ||
$16 ~ /application\/octet-stream|application\/exe/) {
print $1"\t"$5"\t"$9"\t"$17"\t"$16
}' | head -20
echo ""
echo "=== 2. Emotet HTTP C2 check-ins (POST to external with short URI) ==="
awk 'NR>8 && !/^#/' "$LOG_DIR/http.log" 2>/dev/null | \
awk -v src="$SUSPECT" -F'\t' '
$3==src &&
$7=="POST" &&
$5 !~ /^(10\.|172\.(1[6-9]|2[0-9]|3[01])\.|192\.168\.)/ &&
length($9) < 50 {
print $1"\t"$5"\t"$9"\t"$13
}' | head -20
echo ""
echo "=== 3. C2 beacon regularity ==="
awk 'NR>8 && !/^#/' "$LOG_DIR/conn.log" | \
awk -v src="$SUSPECT" -F'\t' '
$3==src &&
$6=="80" &&
$5 !~ /^(10\.|172\.(1[6-9]|2[0-9]|3[01])\.|192\.168\.)/ {
print $1"\t"$5
}' | sort | uniq -c | sort -rn | head -10
echo ""
echo "=== 4. SMB spreader: internal fan-out from suspect ==="
awk 'NR>8 && !/^#/' "$LOG_DIR/conn.log" | \
awk -v src="$SUSPECT" -F'\t' '
$3==src &&
$6=="445" &&
$5 ~ /^(10\.|172\.(1[6-9]|2[0-9]|3[01])\.|192\.168\.)/ {
print $5
}' | sort -u | head -20
echo ""
echo "=== 5. Check which internal hosts also C2'd after SMB contact ==="
# Find internal hosts that had SMB contact from the suspect
INTERNAL_VICTIMS=$(awk 'NR>8 && !/^#/' "$LOG_DIR/conn.log" | \
awk -v src="$SUSPECT" -F'\t' '$3==src && $6=="445" {print $5}' | \
sort -u)
echo "Potential Emotet spread victims (checking for their C2 activity):"
echo "$INTERNAL_VICTIMS" | while read -r victim; do
count=$(awk 'NR>8 && !/^#/' "$LOG_DIR/http.log" 2>/dev/null | \
awk -v src="$victim" -F'\t' '$3==src && $7=="POST" &&
$5 !~ /^(10\.|172\.)/ && length($9)<50' | wc -l)
if [ "$count" -gt 0 ]; then
echo " $victim: $count suspicious POST requests (possible Emotet spread)"
fi
done
echo ""
echo "=== 6. TLS fingerprint check (some Emotet variants use HTTPS) ==="
awk 'NR>8 && !/^#/' "$LOG_DIR/ssl.log" | \
awk -v src="$SUSPECT" -F'\t' '$3==src && $7 ~ /^[0-9]+\./ {
print $1"\t"$5"\t"$7"\t"$(NF-1)
}' | head -10
Emotet's initial dropper download from legitimate-but-compromised WordPress sites is an intentional evasion tactic. Domain reputation filters and web proxies use domain reputation databases: wordpress.example.com has been running for 3 years, has legitimate pagerank, and appears in no malware blocklist — the download is allowed. If Emotet used attacker-controlled infrastructure (attacker.com registered last week), it would be blocked by most enterprise web proxies. By compromising established legitimate sites and hosting malware in /wp-content/uploads/ directories, Emotet downloads bypass URL-based controls. Detection from network telemetry: the User-Agent and URI pattern are still anomalous — PowerShell or cmd.exe UAs are not expected from a user's workstation to a WordPress site, and /wp-content/uploads/2024/01/random.exe is not a pattern seen in legitimate web browsing. The domain reputation is clean; the transaction is not. This is why content-level analysis (User-Agent + URI + response type) is more effective than domain reputation alone for detecting malware downloads.
Q & A
Q: Emotet's C2 rotates IP addresses frequently. How do I detect new C2 IPs before threat intel feeds catch up?
Emotet C2 detection without relying on threat intel feeds requires behavioral analysis: (1) POST to raw IP addresses: Emotet C2s are often accessed by raw IP, not domain name. Zeek http.log shows requests where Host: header contains an IP address rather than a domain name. Browsers occasionally do this, but workstations doing regular HTTP POSTs to raw IPs on port 80 is highly unusual. (2) Short URI POST pattern: Emotet's check-in URI is typically 1-15 random characters: /aXyZ9b/. A POST to this kind of URI on port 80 to an external IP from a workstation is nearly always malicious. (3) Response size pattern: Emotet heartbeat responses are tiny (<200 bytes). Large malware downloads arrive as the response to a subsequent POST. Consecutive small-response POSTs from the same source to the same external IP is the Emotet C2 pattern. (4) TLS cert age: when Emotet shifts to HTTPS, the certificates on new C2 IPs are fresh. ssl.log's notbefore field <14 days + external IP + POST to short URI = high confidence. (5) Network behavior anomaly: a workstation that has never sent POSTs to external port 80 suddenly starts — regardless of the specific destination IP — is an anomaly worth investigating.