Skip to content

Lab 06 — Dynamic Analysis — Network

Hands-on lab · ← Back to the module concept

Setup

git clone https://github.com/plaintext-security/plaintext-labs
cd plaintext-labs/malware/06-dynamic-network
make up
make fetch-pcap        # pulls a real malware-C2 capture from Malware-Traffic-Analysis.net into pcaps/
make demo              # offline synthetic fallback (no download needed)

⚠ This lab analyzes a real capture of live malware C2 traffic. Handle it accordingly. - Analysis only. You read packets — tshark, Wireshark, Suricata. You never execute anything and never replay the C2: do not connect to any host, IP, or domain seen in the capture. The attacker infrastructure may still be live. - Isolation. All work stays inside the isolated container; the lab network is internal: true, so even the synthetic beacon's "C2 traffic" cannot leave the host. - Hygiene. The capture is fetched at lab time (password-protected zip) and is never committed.gitignore covers pcaps/, *.pcap, *.zip. The classic Malware-Traffic-Analysis.net (MTA) password is infected; MTA changed its scheme for newer captures — the current password is on the site's about page. Override with PCAP_SOURCE=… ZIP_PASSWORD=… make fetch-pcap (register the source in lib/sources.tsv). - Offline fallback. No download / MTA unreachable? Skip make fetch-pcap; make demo runs the bundled synthetic beacon.py against the local sink so you can build and test the same workflow with zero network.

Scenario

A host on your network has been flagged for repeated, regular outbound connections to an unfamiliar IP. The triage hypothesis is a Remote Access Trojan beaconing to its operator — the open-source RATs AsyncRAT (MITRE S1087) and QuasarRAT (MITRE S0262) are among the most common families you will meet, and both leave recognisable fingerprints on the wire. Before the SOC pushes a network block, the team lead wants the C2 characterised: which host and port, what protocol, what cadence, and — crucially — a durable signature that fires on the next victim.

You will work from a real labelled capture: make fetch-pcap downloads a RAT traffic-analysis exercise from Malware-Traffic-Analysis.net. Your job is to pull the C2 indicators out of it — the C2 IP and port, the beacon interval, and any protocol tells — then turn the highest-signal finding into a detection rule. AsyncRAT in particular is famous for an own-goal IOC: even when its C2 channel is TLS-encrypted, the server often presents a default self-signed certificate whose issuer/subject names the malware itself (e.g. an O=/CN= of AsyncRAT Server), so the encrypted channel announces exactly what it is. Read the real cert and the timing; that's the lab.

The capture = the real RAT C2 PCAP that make fetch-pcap drops in pcaps/ (path printed by the target). In offline mode, the capture is the bundled synthetic beacon.py traffic captured at the local sink — same workflow, deterministic structure, no download. Treat the synthetic beacon as if you didn't know its source (read data/beacon.py only after you've analysed its traffic).

Do

  1. [ ] Stage the capture and open it. Run make up, then make fetch-pcap to stage the real PCAP under pcaps/ (or skip it and use the synthetic fallback). Confirm the file and its SHA-256 (pcaps/SHA256SUMS). Open it read-only: tshark -r /lab/pcaps/<file> -q -z io,phs for a protocol hierarchy, and -q -z conv,tcp for the conversation list. Which peer does the host talk to most, and on what port?

  2. [ ] Identify the C2 endpoint. From the conversations, isolate the suspected C2: its IP, its port (RATs love non-standard / high TCP ports, not 80/443), and roughly how many flows go to it. For the synthetic beacon, the "C2" is the local sink on :8080. Record the candidate C2 IP:port.

  3. [ ] Measure the beacon interval. List the connection start times to the C2 endpoint and compute the deltas between them (tshark … -e frame.time_epoch, or feed the sink log to parse_sink_log.py). A near-constant interval — low variance, possibly with jitter — is the beaconing tell. State the mean interval and how regular it is.

  4. [ ] Read the protocol tells.

  5. If the C2 is TLS: extract the certificate from the handshake and inspect issuer/subject (tshark … -Y 'tls.handshake.type==11' -T fields -e x509sat.printableString -e x509ce.dNSName). Does the cert self-identify (AsyncRAT Server, a malware-named O=/CN=, or an implausible self-signed cert)? Note the JA3/JA3S or cipher set if present.
  6. If the C2 is cleartext HTTP (the synthetic beacon): record the URI path, the non-browser User-Agent, any custom header (e.g. an X-Session-Token), and the request body.

  7. [ ] Decode any encoded content. If a request carries an encoded payload in a body or header, decode it. (Hint: check for Base64 — python3 -c "import base64,sys; print(base64.b64decode(sys.argv[1]))" <blob>.) What does it reveal about the implant (host ID, sequence, status)?

  8. [ ] Write a detection rule. Turn your single highest-signal finding into a Suricata rule, saved as beacon-c2.rules. Key it on what's invariant for this family, not on content an operator can reshape: for a TLS RAT, a tls.cert_subject/tls.cert_issuer content match on the malware-named cert string (plus the destination port) is far more durable than any URI; for the cleartext beacon, match the URI pattern and the User-Agent together. Include protocol, direction, and your content matches.

  9. [ ] Write the network analysis report. In network-report.md: the C2 endpoint (IP, port, protocol), the beacon interval and how you measured it, the protocol fingerprint (cert CN/issuer or URI+UA), any decoded payload, the MITRE ATT&CK techniques (T1071.001 Web Protocols and/or T1573 Encrypted Channel; cite S1087/S0262 for the family), and your rule with each condition explained.

Success criteria — you're done when

  • [ ] The C2 endpoint (IP + port) is identified and recorded from the capture.
  • [ ] The beacon interval is measured (mean + regularity) and documented.
  • [ ] The protocol fingerprint is captured: a TLS cert issuer/subject (real PCAP) or the URI + User-Agent (synthetic fallback).
  • [ ] Any encoded content is decoded.
  • [ ] beacon-c2.rules contains a Suricata-compatible rule keyed on an invariant indicator.
  • [ ] network-report.md exists with the ATT&CK mapping (S1087/S0262 cited) and per-condition detection rationale.

Deliverables

beacon-c2.rules, network-report.md. Commit both. Lab artifacts — the pcaps/ capture and any extracted blobs — stay out of commits.

Automate & own it

Required. Extend parse_sink_log.py (provided in data/) so it doubles as a beacon-interval detector you can point at either source: it already reads the sink's JSON log; add a mode that reads tshark connection timestamps from the real PCAP (e.g. tshark -r file.pcap -T fields -e frame.time_epoch -e ip.dst -e tcp.dstport) and emits the same table of timestamp, endpoint, and time-delta. The delta column is the detector — flag any sequence whose delta coefficient-of-variation is under 20% as "consistent beacon interval (T1071.001 indicator)." AI drafts the tshark-parsing mode; you write the interval-variance logic yourself after reading the MITRE T1071 page, and you review every line.

AI acceleration

Give an AI your captured indicators (cert subject, port, interval, URI/UA) and ask it to write a Suricata rule. Then validate it: suricata -T -S /path/to/beacon-c2.rules (or check the OISF keyword docs). Identify and fix at least one syntax or logic error the AI introduced — a too-broad content match, a wrong flow: direction, or a keyword that doesn't exist — before committing.

Connects forward

The signature you write here is exactly the artifact Track 02 Defensive Operations deploys to a network IDS; the ATT&CK technique IDs connect this module to the ATT&CK-mapped detection work in that track's Module 08 (Detection as Code). The TLS-certificate reasoning recurs wherever encrypted C2 hides in plain sight.

Marketable proof

"I analyse real RAT C2 captures — beacon cadence, C2 endpoint, and the TLS-certificate fingerprint that betrays the family — and write durable network detection signatures from first principles."

Stretch

  • Pull JA3/JA3S hashes from the real capture and check them against a public JA3 fingerprint set; note whether the RAT's TLS stack is distinctive enough to detect on the handshake alone.
  • Capture the synthetic beacon with tcpdump inside the container, then diff your synthetic PCAP against the real one in tshark — what does the real sample do that your model doesn't, and how would each difference change your rule?

Comments

Sign in with GitHub to comment. Choose the type: Feedback (errors or suggestions on this page) · Hints (help for fellow learners — no spoilers) · General (anything else).