Malware Analysis and Network Indicators
Malware, like a biological pathogen, has to communicate to do damage at scale - it phones home for instructions, exfiltrates what it steals, or spreads to the next host. That communication has to cross the network, and the network is where defenders get their clearest look at it, even when the binary itself is heavily obfuscated.
Static vs. Dynamic Analysis
Static analysis examines a malware sample without executing it: disassembling the binary, extracting embedded strings, checking file hashes against known-bad databases, and inspecting metadata like compilation timestamps or packer signatures. It's safe by construction, since nothing runs, but modern malware is routinely packed, obfuscated, or encrypted specifically to defeat this kind of inspection.
Dynamic analysis runs the sample in a controlled, instrumented environment (a sandbox) and observes what it actually does: files it creates, registry keys it touches, processes it spawns, and - most relevant to this book - every network connection it opens. Dynamic analysis reveals behavior that static analysis can miss entirely, at the cost of requiring an isolated environment where "letting the malware run" doesn't risk anything real.
Network Indicators of Compromise
Network-based indicators of compromise (IOCs) are the traces malware leaves on the wire, and they tend to survive even when the malware author changes the binary itself to evade file-hash detection:
- C2 domains and IPs: the addresses malware contacts for instructions. These get shared through threat intelligence feeds and blocklists, though attackers rotate them constantly, which is why domain-based IOCs age quickly.
- DNS beaconing: malware that checks in with its controller on a fixed or near-fixed interval produces suspiciously regular DNS queries - a pattern easy for a human to miss in raw logs but easy for software to spot statistically.
- JA3/JA3S fingerprints: a hash of the specific fields and order a TLS client (or server) presents during the handshake. Because malware families often reuse the same TLS libraries and configuration, their JA3 fingerprint can identify the malware family even though the payload itself is encrypted and unreadable.
- Unusual User-Agent strings or HTTP header ordering: many malware HTTP clients are hand-rolled or use outdated libraries, producing headers that don't match any real browser.
- DNS-over-uncommon-ports or DNS tunneling: encoding stolen data inside DNS queries to sneak past controls that only inspect HTTP/HTTPS traffic.
Sharing Indicators: STIX and TAXII
Once an analyst extracts IOCs from a sample, sharing them efficiently matters as much as finding them. STIX (Structured Threat Information Expression) is a standardized format for describing threat intelligence - indicators, attack patterns, threat actors - as structured, machine-readable objects. TAXII (Trusted Automated Exchange of Intelligence Information) is the transport protocol used to distribute STIX data between organizations and feeds. Together they let one organization's incident response findings become another organization's blocklist update within hours instead of weeks.
A Simple Beaconing Detector
Regular-interval outbound connections are one of the more statistically reliable network indicators, because legitimate human-driven traffic is bursty and irregular, while a malware check-in loop tends toward suspiciously consistent timing. A minimal detector just needs to track inter-arrival times per destination and flag low variance.
package main
import (
"fmt"
"math"
)
// intervalVariance returns the population variance of the time gaps
// between consecutive connection timestamps, in seconds.
func intervalVariance(timestampsUnix []int64) float64 {
if len(timestampsUnix) < 3 {
return math.Inf(1) // not enough data to judge
}
gaps := make([]float64, 0, len(timestampsUnix)-1)
for i := 1; i < len(timestampsUnix); i++ {
gaps = append(gaps, float64(timestampsUnix[i]-timestampsUnix[i-1]))
}
var mean float64
for _, g := range gaps {
mean += g
}
mean /= float64(len(gaps))
var variance float64
for _, g := range gaps {
variance += (g - mean) * (g - mean)
}
variance /= float64(len(gaps))
return variance
}
func main() {
// A likely beacon: connects roughly every 60 seconds.
beacon := []int64{1000, 1060, 1121, 1179, 1240}
// Normal human browsing: irregular gaps.
human := []int64{1000, 1015, 1400, 1420, 3000}
fmt.Printf("beacon variance: %.2f\n", intervalVariance(beacon))
fmt.Printf("human variance: %.2f\n", intervalVariance(human))
}
Low variance relative to the mean interval is the signal: the beacon sample above clusters tightly around a sixty-second interval, while the human sample swings wildly. Real detectors combine this with destination reputation, connection volume, and payload size consistency, but the statistical core - "does this host talk to that destination suspiciously regularly?" - is exactly this calculation, run continuously across every host-destination pair a network sees.
Frequently Asked Questions
If a sample is packed or obfuscated, is static analysis just a waste of time? Not entirely - even a heavily packed binary still yields file hashes, a packer signature, and compilation metadata worth checking against known-bad databases, and that's often enough to identify a known family quickly. But when the packer defeats deeper inspection, that's exactly the moment dynamic analysis earns its keep, because watching what the sample actually does once it unpacks itself in memory reveals behavior static inspection alone would miss.
Our lab VM has outbound internet access for dynamic analysis. Is that safe? Only if that access is fully blocked or routed through a monitored, disposable proxy - never a direct route to the real internet or your production network. A live sample given a genuine path out can reach its actual command-and-control infrastructure or attempt to spread, which defeats the entire purpose of isolating it in the first place; snapshot before running anything and revert immediately afterward.
TLS encrypts almost everything now. Doesn't that make network-based indicators useless? It changes what's visible, but it doesn't blind you. Payload content is genuinely opaque under TLS 1.3, but connection timing, packet sizes, destinations, JA3-style handshake fingerprints, and certificate details all remain visible without decrypting a single byte - and that metadata alone carries enough signal to flag a malware family or a beaconing pattern.
Why does the beaconing detector look at variance instead of just the average interval? Because the average alone can't tell a beacon from ordinary browsing - both might average out to the same number of seconds between connections over a long enough window. What separates them is consistency: a malware check-in loop clusters tightly around its interval with very low variance, while human-driven traffic is bursty and irregular by nature, so variance is the statistic that actually distinguishes the two.
Why bother with STIX/TAXII instead of just emailing a blocklist to other teams? Because a blocklist in an email or a spreadsheet has to be manually re-entered everywhere it needs to apply, which is slow and error-prone at any real scale. STIX gives indicators a structured, machine-readable shape and TAXII gives them a standard transport, so one organization's incident-response findings can become another organization's automated blocklist update within hours instead of being retyped by hand days later.
Summary
- Static analysis is safe and fast but blind to obfuscated or packed samples; dynamic analysis reveals real behavior but requires strict isolation.
- Network IOCs - C2 addresses, DNS beaconing, JA3 fingerprints, anomalous headers - often outlive the specific malware binary that produced them.
- TLS encryption hides payloads but not connection metadata, which remains a rich source of detection signal.
- STIX/TAXII let indicator sharing scale across organizations instead of staying locked in one team's notebook.