Kalos Cybersecurity LLC

Alerts to Answers

Lab 6 — Emerging Threats Investigation: Hunting an Attack Across the Network

Introduction

Objective

Reconstruct a multi-stage network attack using Suricata and the Emerging Threats Open (ET Open) ruleset — then write, deploy, and test a custom Suricata signature that detects the attack’s beacon behavior automatically.

Lab 5 kept you on the endpoint, reconstructing an attack from Sysmon. Lab 6 moves to the wire. Endpoint telemetry tells you what happened inside a machine; network telemetry tells you what happened between machines — and, as Chapter 5 puts it, packets cannot hide. An attacker may evade a host’s logging, but the communication still has to traverse the network, where Suricata is watching. This lab teaches you to read that network story: recognize reconnaissance, malicious web and DNS activity, and the periodic “beacon” pattern of command-and-control — first through the ET Open signatures that fire, then by authoring your own signature when the community rules leave a gap.

Overview

You will investigate a scripted but realistic network intrusion generated from both lab hosts, so the hunt spans multiple sources the way a real one does:

Reconnaissance (port scan) → malicious HTTP request → suspicious DNS lookup → repeating C2-style beacon

You will hunt Dashboard-first, using Wazuh’s Suricata alerts and the fields that matter (signature, signature_id, src_ip, dest_ip), and drop to raw eve.json with jq only where the Dashboard cannot answer the question — for example, to see the flow and http records behind an alert, or to prove a beacon’s timing. The lab ends where mature network defense ends: the beacon has no ET Open signature of its own, so you write one, following the local.rules discipline from Chapter 3.

Every activity follows the course pattern:

Generate → Observe in Wazuh → Confirm in eve.json (where needed) → Explain Why → Detect Automatically

Prerequisites

  • Lab 0 completed — Suricata 8.0.x validated, ET Open rules updated, and Suricata alerts confirmed reaching Wazuh.

  • ens192 in promiscuous mode (verify with the Lab 0 / ORV interface check); promisc-ens192.service active.

  • You can reach Explore → Discover and query rule.groups:"suricata".

  • jq installed on UB2604 (sudo apt install jq) for the eve.json deep-dives.

  • On WAZUH-SRV you have sudo; on UB2604 you can edit /etc/suricata/rules/local.rules and restart Suricata.

If any prerequisite is unmet, run the Operational Readiness Validation (Appendix C) — especially the Suricata interface checks — before continuing.

Lab Environment Recap

System Hostname Agent Name IP Address Role in This Lab
Ubuntu 26.04 Sensor UB2604 u2604 192.168.1.10 (ens160) / no IP (ens192) Suricata NIDS — where alerts originate and where the custom rule lives
Windows 11 Pro WIN11 win11 192.168.1.40 Traffic generator (attacker role) and endpoint
Wazuh Server 4.14.5 WAZUH-SRV 192.168.1.30 SIEM — where you hunt

Query the sensor’s alerts with agent.name:“u2604” (lowercase). Suricata alerts are attributed to the sensor agent, not the host that generated the traffic — an important distinction you will use throughout the hunt.

Learning Outcomes

By the end of this lab, you will be able to:

  • Generate a controlled, multi-stage network attack chain from two hosts.

  • Hunt ET Open alerts in Wazuh using signature, signature_id, src_ip, and dest_ip.

  • Interpret the anatomy of a Suricata alert and trace it back to its ET Open signature category.

  • Drop to raw eve.json with jq to examine flow, http, and dns records behind an alert.

  • Recognize a command-and-control beacon by its periodic timing.

  • Author, deploy, and test a custom Suricata signature in local.rules.

  • Reason about signature specificity — the line between a useful detection and an alert flood.

The ET Open Categories You Will Encounter

Stage Likely ET Open Category What It Detects ATT&CK
Port scan emerging-scan.rules (ET SCAN) Reconnaissance / service discovery T1046
Malicious HTTP emerging-user_agents / emerging-web Suspicious user agents, tool signatures T1071.001
Suspicious DNS emerging-dns.rules Anomalous or known-bad domain lookups T1071.004
C2 beacon (often none — you will write one) Periodic callback to a controller T1071 / T1571

Key Terms

Key Term Description
Signature (Suricata rule) A detection rule describing a network behavior; when matching traffic is seen, Suricata generates an alert.
SID (Signature ID) The unique numeric identifier of a Suricata rule (e.g., 2100498); how analysts research why an alert fired.
ET Open Emerging Threats Open — the community-maintained Suricata ruleset, updated daily, supported by Proofpoint.
eve.json Suricata’s structured JSON log: alert, flow, http, dns, tls, and other record types — the sensor’s authoritative record.
Flow record An eve.json entry summarizing a complete conversation (bytes, packets, duration) between two endpoints.
Beacon The periodic, regular-interval callback a compromised host makes to a command-and-control server.
C2 (Command and Control) The channel an attacker uses to control a compromised host and issue commands.
local.rules The file (/etc/suricata/rules/local.rules) reserved for analyst-authored signatures; never overwritten by suricata-update.
HOME_NET / EXTERNAL_NET Suricata variables defining “our” network vs. everything else; used in rule headers to set direction.

Part 1 — Generate the Network Attack Chain

The activity is produced by two coordinated scripts (full source and explanation in the Manual Appendix): Invoke-Lab6Attacker_v18.ps1 on WIN11 and lab6-beacon_v18.sh on UB2604. Together they emulate a realistic intrusion using only safe tooling — nothing malicious executes; the “C2” is a harmless HTTP callback to a benign test endpoint.

Task 1.1 — Snapshot and Record the Window

Snapshot both WIN11 and UB2604 if you want a clean rollback. Then record the exact start time — it anchors the entire hunt, exactly as in Lab 5.

Why this matters: Network telemetry is high-volume; without a time anchor you will drown in benign flows. Every query in this lab is bounded by “the window after this timestamp.” Scoping time is the first move of every network hunt.

Task 1.2 — Stage 1: Reconnaissance (from WIN11)

On WIN11, run the attacker script’s scan stage (or manually):

::: {custom-style="CodeLabel"} WIN11 · PowerShell :::

nmap -sS 192.168.1.40

Wait — read that IP carefully. The scan target is 192.168.1.40, but the traffic must cross the segment ens192 monitors. The Manual Appendix explains the exact source/target the generator uses; for the hunt, what matters is that a SYN scan crosses the wire.

Task 1.3 — Stage 2: Malicious HTTP + Suspicious DNS

The generator then issues an HTTP request with a tool-like user agent and resolves a suspicious-looking domain. These are the kinds of requests ET Open user-agent and DNS rules are written to catch.

Task 1.4 — Stage 3: The C2 Beacon

Finally, lab6-beacon_v18.sh on UB2604 issues a small HTTP request to a fixed endpoint every 30 seconds, several times — the defining signature of a C2 beacon: not the content, but the regularity. Let it run for at least five intervals before investigating so the pattern is visible.

Why this matters: Real beacons are found by timing, not payload — a callback every 30 or 60 seconds, with low jitter, is deeply abnormal for legitimate traffic. Recognizing periodicity is one of the most valuable network-hunting skills, and no single ET Open signature reliably catches a novel beacon — which is exactly why you will write one in Part 4.

Task 1.5 — Confirm Telemetry Exists

Before hunting, confirm Suricata is writing events. On UB2604:

::: {custom-style="CodeLabel"} UB2604 · Terminal :::

sudo tail -n 5 /var/log/suricata/eve.json

You should see recent JSON records. If eve.json is not updating, stop and run the Lab 0 / Appendix D interface checks — the problem is before Wazuh.

CONCLUSION: You generated a four-stage network attack from two hosts and anchored your hunt in time. Treat the generators’ internals as unknown from here — you are the analyst who sees only what crossed the wire.

Part 2 — The Investigation (Dashboard-First)

You are the network analyst. Suricata has been alerting; your job is to reconstruct the intrusion from those alerts, dropping to eve.json only when the Dashboard cannot answer a question.

Task 2.1 — Survey the Sensor’s Alerts

In Discover, index wazuh-alerts-*, time range set to your window:

::: {custom-style="CodeLabel"} UB2604 · Expected output :::

rule.groups:"suricata" and agent.name:"u2604"

Scan the results. Note the variety of data.alert.signature values — you should see distinct alerts corresponding to the attack stages.

Why this matters: Starting with the full set of Suricata alerts, then sorting by signature, gives you the shape of the intrusion before you commit to a theory. This is the network equivalent of Lab 5’s “start broad, then narrow.” A good analyst surveys before drilling.

Task 2.2 — Stage 1: Identify the Reconnaissance

Filter to scan-category alerts:

::: {custom-style="CodeLabel"} UB2604 · Expected output :::

rule.groups:"suricata" and data.alert.signature:*SCAN*

Open a matching alert and record:

Field What It Tells You
data.alert.signature The ET SCAN signature name (e.g., “ET SCAN Nmap…”)
data.alert.signature_id The SID — research this to understand what the rule matches
data.src_ip The scanner (attacker)
data.dest_ip The scan target
data.alert.category Suricata’s classification (e.g., “Attempted Information Leak”)

Why this matters: The signature_id is the analyst’s research key — with a SID you can look up exactly what a rule inspects and decide whether an alert is a true positive or noise. The src_ip / dest_ip pair establishes the attack’s direction: who is scanning whom. Establishing directionality early orients the entire investigation.

Task 2.3 — Stage 2: Find the Malicious HTTP and DNS

Pivot to the attacker’s IP you just identified and look for its web and DNS activity:

::: {custom-style="CodeLabel"} UB2604 · Expected output :::

rule.groups:"suricata" and data.src_ip:"<source-ip>"

Identify the HTTP alert (suspicious user agent) and the DNS alert (suspicious lookup). Record the signatures and the data.dest_ip / domain involved.

Why this matters: Pivoting on the attacker’s IP — the same disciplined pivot you used on processGuid in Lab 5 — lets you follow one actor across multiple alert types. On the network, the IP is the thread that ties reconnaissance to exploitation to callback.

Task 2.4 — Drop to eve.json: Inspect the HTTP Transaction

The Dashboard shows the alert, but the full HTTP transaction lives in eve.json. On UB2604, examine the http record behind the alert:

::: {custom-style="CodeLabel"} UB2604 · Terminal :::

sudo tail -n 500 /var/log/suricata/eve.json | jq 'select(.event_type=="http") | {timestamp, src_ip, dest_ip, hostname: .http.hostname, ua: .http.http_user_agent, url: .http.url}'

Record the http_user_agent and url — the detail the alert summarized but did not fully show.

Why this matters: This is where eve.json earns its place. An ET Open alert tells you that a suspicious user agent was seen; the http record tells you exactly what it was, plus the hostname and URL requested. Suricata logs far more than alerts — flow, http, dns, tls records — and jq turns that firehose into precise answers. This is the skill that separates an analyst who reads alerts from one who reads traffic.

Task 2.5 — Stage 3: Detect the Beacon Pattern

Now the hard part — the beacon may not have fired a dedicated ET Open alert. Hunt it by its timing. In eve.json, extract the flow or http records to the beacon’s destination and look at the timestamps:

::: {custom-style="CodeLabel"} UB2604 · Terminal :::

sudo tail -n 1000 /var/log/suricata/eve.json | jq -r 'select(.event_type=="http") | "(.timestamp) (.dest_ip) (.http.hostname)"' | sort

Look at the intervals between requests to the same destination. Record the interval.

Why this matters: A human eye spots the pattern immediately: requests at a near-constant interval (every ~30s) to the same destination. That regularity is the beacon. No payload keyword catches this — only the rhythm reveals it. You have just found something the community ruleset missed, which is the entire justification for the custom signature you will now write.

Task 2.6 — Build the Network Timeline

Assemble the stages into a timeline in your SOC Engineering Notebook:

Time Stage Evidence (signature / eve.json)
(fill in) Reconnaissance ET SCAN alert — attacker → target
(fill in) Malicious HTTP user-agent alert + http record (UA, URL)
(fill in) Suspicious DNS ET DNS alert — queried domain
(fill in) C2 beacon periodic http records, ~30s interval

Why this matters: As in Lab 5, ordering the evidence by time turns disconnected alerts into an intrusion narrative: recon, then exploitation, then callback. The timeline is the deliverable — it is what you would attach to a ticket, and it is what proves you investigated rather than merely searched.

CONCLUSION: You reconstructed a four-stage network intrusion from Suricata telemetry, used eve.json to extract detail the Dashboard could not show, and identified a C2 beacon by its timing that no ET Open signature caught. That gap is your reason to build a detection.

Part 3 — Understand the Signature That Fired

Before writing your own signature, understand one that already works. This grounds Part 4 in real syntax rather than abstraction.

Task 3.1 — Look Up a Firing Signature

Take the signature_id from the ET SCAN alert (Task 2.2). On UB2604, find the actual rule text in the compiled ruleset:

::: {custom-style="CodeLabel"} UB2604 · Terminal :::

grep "sid:;" /var/lib/suricata/rules/suricata.rules

You will see the full signature — header and options.

Why this matters: Every alert you have investigated originated from a line of text like this. Reading the rule that fired demystifies detection: an alert is not magic, it is a signature whose conditions the traffic satisfied. Being able to pull the exact rule behind any SID is a core analyst skill for judging true vs. false positives.

Task 3.2 — Dissect the Rule

Break the signature into its parts using the Chapter 5 model:

Component Example Meaning
Action alert Generate an alert on match
Protocol tcp / http / dns Which protocol the rule inspects
Source $EXTERNAL_NET any Who sent the traffic
Direction -> Flow direction
Destination $HOME_NET any Who received it
msg “ET SCAN …” The human-readable alert name
Options content, flags, threshold… The conditions that must match
sid 2xxxxxx Unique rule identifier
rev 1, 2, … Revision number

Why this matters: The header answers where a rule applies (who, what protocol, which direction); the options answer what must match. Reading this structure is exactly what you will invert in Part 4 to write your own rule — you define the behavior, then express it in this grammar.

CONCLUSION: You can now read a Suricata signature and map every field to a purpose. That literacy is the prerequisite for authoring one.

Part 4 — Write a Custom Signature for the Beacon

The beacon slipped past ET Open. You will close that gap with a local signature that detects the callback to the C2 destination — following the vendor-vs-local discipline: custom rules go in local.rules, never in the vendor files.

Task 4.1 — Define the Behavior First

Chapter 5’s rule: define the behavior before writing syntax. State it plainly:

Detect an outbound HTTP request from the lab network to the beacon’s destination host (or its distinctive URI/user-agent), so the periodic callback generates an alert.

Decide your match criterion. Options, from broadest to most specific:

  • Destination IP alone (broad — catches all traffic to that host)

  • HTTP host header or URI (more specific — catches the beacon’s distinctive request)

  • User-agent + URI combination (most specific — lowest false positives)

Why this matters: Defining behavior first, then choosing specificity, is the discipline that separates Engineer B from Engineer A in Chapter 5. Matching only the destination IP is easy but blunt; matching the beacon’s distinctive HTTP characteristics is the professional choice because it survives an IP change and produces fewer false positives.

Task 4.2 — Reserve a SID and Open local.rules

Use the student-laboratory SID range your instructor assigned (local rules conventionally start at 1000000). On UB2604, back up and open local.rules:

::: {custom-style="CodeLabel"} UB2604 · Terminal :::

sudo cp /etc/suricata/rules/local.rules /etc/suricata/rules/local.rules.bak
sudo nano /etc/suricata/rules/local.rules

Why this matters: local.rules is the network-side equivalent of Wazuh’s local_rules.xml — it survives suricata-update, while vendor files are overwritten. Reserving a documented SID range prevents collisions with the ~2,000,000-numbered ET Open rules. This is the same “never edit vendor content” principle you have applied to Suricata rules, Wazuh rules, and Sysmon configs.

Task 4.3 — Author the Signature

Add a rule matching the beacon’s HTTP request. A specific, low-noise example (adjust host/URI to your generator’s actual values, recorded in Task 2.4):

::: {custom-style="CodeLabel"} UB2604 · local.rules :::

alert http $HOME_NET any -> $EXTERNAL_NET any (msg:"LAB6 C2 Beacon - periodic callback detected"; flow:established,to_server; http.method; content:"GET"; http.uri; content:"/beacon"; http.header; content:"User-Agent|3a| Lab6Beacon"; classtype:trojan-activity; sid:1000601; rev:1;)

Save and exit.

Why this matters: Read the rule as behavior. $HOME_NET ... -> $EXTERNAL_NET scopes it to outbound traffic from your network — a beacon leaves, it does not enter. flow:established,to_server limits it to real client requests, not stray packets. The http.uri content /beacon and the distinctive User-Agent are what make it specific: it catches this beacon’s request shape, not all HTTP. classtype:trojan-activity and a sid in your reserved range complete it. The |3a| is a hex-encoded colon — a detail of Suricata content matching worth knowing.

Task 4.4 — Validate Before Restarting

Never restart Suricata on an unvalidated config — the Lab 0 discipline:

::: {custom-style="CodeLabel"} UB2604 · Terminal :::

sudo suricata -T -c /etc/suricata/suricata.yaml -v

Expected: “Configuration provided was successfully loaded.” If you see a rule-parsing error, fix the syntax before proceeding — a malformed rule can prevent the ruleset from loading.

Then restart and confirm:

::: {custom-style="CodeLabel"} UB2604 · Terminal :::

sudo systemctl restart suricata
sudo systemctl status suricata --no-pager

Why this matters: suricata -T parses your new rule without touching the running sensor. This is the same “test, then apply” habit as wazuh-logtest in Lab 5 and every configuration change in this course. A typo that silently drops your whole ruleset is far worse than the beacon you are trying to catch.

Task 4.5 — Test Your Signature

Re-run the beacon generator (lab6-beacon_v18.sh on UB2604). Then, in the Dashboard:

rule.groups:“suricata” and data.alert.signature_id:“1000601”

Expected result: your custom alert fires on each beacon interval — a periodic callback that ET Open missed is now detected by a signature you wrote, mapped to trojan-activity.

Why this matters: This is the payoff. You found a gap in the community ruleset by hunting, then closed it with a custom detection — the complete network detection-engineering loop. Every mature SOC writes local signatures for exactly this reason: public rules cannot anticipate every environment or every novel beacon. An analyst who can author network detections extends the SOC’s coverage beyond what any vendor ships.

Task 4.6 — Specificity and Limits (Analyst Judgment)

No rule is free of tradeoffs. In your notebook, answer:

  • Your rule matches the beacon’s URI and user-agent. What happens if the attacker changes the user-agent string? Would a destination-IP rule have survived that change — and what would it cost in false positives?

  • Chapter 5 warns against signatures that match common strings (like matching every “GET”). Explain why your rule avoids that trap.

  • The beacon was found by timing, but your signature matches content. Could a signature match on timing/frequency directly? (Consider Suricata’s threshold/detection_filter — a beacon fires N times in M seconds.) What would make that approach stronger or weaker than content matching?

Why this matters: Every detection choice trades coverage against noise. A content rule is precise but brittle (defeated by changing the string); an IP rule is durable but blunt; a frequency rule catches the behavior (periodicity) but requires careful thresholds. A detection engineer who can reason about these tradeoffs — not just write one rule — is who a SOC actually needs.

CONCLUSION: You wrote, validated, deployed, and tested a custom Suricata signature that catches a beacon the community ruleset missed, and reasoned about its brittleness. This is network detection engineering end to end.

Lab Validation Checklist

Item Pass
Attack chain generated from both hosts; start time recorded
eve.json confirmed updating before hunting
ET SCAN alert identified (signature, SID, src/dest)
Attacker IP established and pivoted on
Malicious HTTP alert + http record (UA, URL) recovered via jq
Suspicious DNS alert identified
Beacon identified by ~30s timing in eve.json
Network timeline assembled
A firing ET Open signature located by SID and dissected
Custom beacon signature authored in local.rules
suricata -T passed before restart
Custom signature (SID 1000601) fired on re-run
Specificity/limits analysis completed

Lessons Learned

  • Packets cannot hide. Network telemetry provides visibility independent of the endpoint; an attacker who evades host logging still has to communicate.

  • The SID is your research key. Every alert traces to a signature; pulling the rule behind a SID is how you judge true vs. false positives.

  • Pivot on the IP. On the network, the source IP is the thread that ties recon to exploitation to callback — the same disciplined pivoting as processGuid on the host.

  • eve.json holds the detail alerts summarize. Dashboard-first is efficient, but flow/http/dns records in eve.json (via jq) are where the full transaction lives.

  • Beacons are found by rhythm, not payload. Periodic, low-jitter callbacks are the tell; no content keyword reliably catches a novel beacon.

  • Custom signatures close community gaps. ET Open is broad but not exhaustive; local.rules is where you extend coverage to your environment.

  • Specificity is a tradeoff. Content rules are precise but brittle; IP rules durable but noisy; frequency rules catch behavior but need careful thresholds.

Knowledge Check

1. An attacker evades all endpoint logging on a compromised host. Why can a network sensor still detect the intrusion?

A. Endpoint logging and network logging are the same data B. The host’s communication must still traverse the network, where Suricata inspects it C. Suricata reads the host’s disk directly D. It cannot — network sensors depend on endpoint agents

2. You have an alert’s data.alert.signature_id. What is its primary investigative value?

A. It is the timestamp of the alert B. It uniquely identifies the rule, letting you look up exactly what the signature matches C. It is the attacker’s IP address D. It sets the alert’s severity

3. The Dashboard shows a “suspicious user-agent” alert but not the full request. Where do you find the exact user-agent string and URL?

A. In the Wazuh Manager configuration B. In the http record in eve.json (via jq) C. In local.rules D. In the Sysmon Operational log

4. What most reliably distinguishes a C2 beacon from normal web traffic?

A. The size of the payload B. The destination port C. The regular, periodic timing of the callbacks D. The source MAC address

5. Where must a custom Suricata signature be stored so suricata-update does not overwrite it?

A. /var/lib/suricata/rules/suricata.rules B. /etc/suricata/suricata.yaml C. /etc/suricata/rules/local.rules D. /var/ossec/etc/rules/local_rules.xml

6. In the rule header alert http $HOME_NET any -> $EXTERNAL_NET any, what does the direction express?

A. Traffic from the external network into your network B. Outbound traffic from your network to the external network C. Traffic between two external hosts D. Only DNS traffic

7. Which command validates a new Suricata signature’s syntax without disrupting the running sensor?

A. sudo suricata-update B. sudo suricata -T -c /etc/suricata/suricata.yaml C. sudo systemctl restart suricata D. sudo tail -f /var/log/suricata/eve.json

8. Your content-based beacon signature matches the beacon’s URI and user-agent. What is its main weakness?

A. It cannot be written in local.rules B. It is brittle — changing the user-agent or URI string defeats it C. It will overwrite ET Open rules D. It only works on encrypted traffic

Answer Key

Q Answer Why
1 B Communication must cross the network regardless of host-side evasion — packets cannot hide.
2 B The SID uniquely identifies the rule, enabling lookup of exactly what it inspects.
3 B The full transaction (UA, URL, hostname) lives in the eve.json http record, queried with jq.
4 C A beacon is defined by periodic, regular-interval callbacks, not payload or port.
5 C local.rules is reserved for custom signatures and is not overwritten by suricata-update.
6 B $HOME_NET -> $EXTERNAL_NET expresses outbound traffic leaving your network.
7 B suricata -T tests configuration/rule syntax without touching the running service.
8 B Content matching is precise but brittle; changing the matched string evades it.

Discussion Questions

  • This lab hunted the beacon by timing but detected it by content. Design (in prose) a detection that keys on frequency instead — N callbacks in M seconds. What are the risks of thresholds set too tight or too loose?

  • ET Open caught the scan and the user-agent but missed the beacon. Why do community rulesets tend to miss environment-specific C2, and what does that imply about relying solely on public signatures?

  • Lab 5 pivoted on processGuid; Lab 6 pivoted on source IP. Compare the reliability of each as an investigative thread. When might an IP be a misleading pivot (think NAT, shared hosts, proxies)?

  • The custom signature scopes to $HOME_NET -> $EXTERNAL_NET. Why is directionality important, and what would go wrong if the rule matched any -> any?

  • Network and endpoint telemetry each caught parts of these two labs’ attacks. Describe an intrusion where the network sensor is essential because the endpoint is unmonitored or compromised.

What Comes Next

You detected a beacon and identified the suspicious domain and destination it called — but you still do not know whether that destination is actually malicious. Lab 7 (MISP Threat Intelligence Investigation) teaches you to take an indicator like the beacon’s domain or IP and enrich it against threat intelligence, turning “an unknown callback” into “a known adversary infrastructure” — or clearing it as benign. Keep your Lab 6 timeline, the beacon’s indicators, and your custom signature; Lab 7 begins where this hunt ends.

Manual Appendix — The Attack Generators Explained

This appendix contains the full source of both Lab 6 generators and explains what each stage produces on the wire. Nothing is malicious: the scan is a standard nmap SYN scan against a lab host, the “malicious” HTTP uses a distinctive but harmless user agent, the DNS lookup resolves a benign test domain, and the “C2 beacon” is a harmless periodic HTTP GET to a fixed lab endpoint. The point is to produce telemetry that looks like an intrusion, safely.

Why Two Scripts and Two Hosts

A real intrusion is not confined to one machine, and neither is its telemetry. Generating traffic from WIN11 (the attacker role) and running the beacon from UB2604 gives students a multi-source hunt: the scan and web/DNS activity originate from one host, the beacon from another, and Suricata sees all of it on ens192. This mirrors how an analyst pieces together an intrusion that touches multiple systems.

Script 1 — Invoke-Lab6Attacker_v18.ps1 (runs on WIN11)

The complete, verbatim source of Invoke-Lab6Attacker_v18.ps1. Type or copy it exactly as shown; everything between the rules below is the script.

::: {custom-style="CodeLabel"} WIN11 · PowerShell (Administrator) :::

<#
================================================================================
  Invoke-Lab6Attacker_v18.ps1
  Lab 6 — Emerging Threats Investigation
  Runs on: WIN11 (192.168.1.40)   |   Run as: Administrator
================================================================================
  PURPOSE
    Generates the attacker-side stages of a network attack chain that Suricata
    and the Emerging Threats (ET Open) ruleset are designed to detect:
      Stage 1  Reconnaissance      -> SYN scan of the sensor-monitored segment
      Stage 2  Malicious HTTP      -> request with a distinctive User-Agent
      Stage 3  Suspicious DNS      -> lookup of a benign test domain

    The C2 BEACON stage runs separately on the Ubuntu sensor
    (see lab6-beacon_v18.sh). Run this script FIRST, then start the beacon.

  SAFE BY DESIGN
    Standard tooling, benign endpoints, distinctive markers for hunting.
    Nothing malicious executes. Run in an isolated lab VM only.

  BEFORE YOU RUN
    1. Snapshot WIN11 (optional).
    2. Open PowerShell as Administrator.
    3. Allow this session to run the script:
         Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass -Force
    4. RECORD THE START TIME printed below — it anchors your hunt.
    5. Requires nmap on WIN11. If nmap is not installed, run the scan stage
       from UB2604 instead:  sudo nmap -sS 192.168.1.40   (the hunt is the
       same; only the source IP differs).

  INVESTIGATE (Dashboard-first)
    rule.groups:"suricata" and agent.name:"u2604"
    Suricata alerts are attributed to the SENSOR (u2604), not this host.
================================================================================
#>

Write-Host "[*] Lab 6 attacker stage starting. RECORD THIS TIME:" (Get-Date -Format o) -ForegroundColor Cyan

# --- Stage 1: Reconnaissance ------------------------------------------------
# SYN scan of UB2604's management interface (192.168.1.10 = ens160). ens192 is
# promiscuous on the mirrored segment, so Suricata sees this scan even though
# ens192 has no address of its own.
Write-Host "[*] Stage 1: port scan (SYN)" -ForegroundColor Yellow
try {
    Start-Process -NoNewWindow -Wait nmap -ArgumentList "-sS 192.168.1.10"
} catch {
    Write-Host "    nmap not found on WIN11. Run 'sudo nmap -sS 192.168.1.40' on UB2604 instead." -ForegroundColor Red
}

# --- Stage 2: Malicious HTTP ------------------------------------------------
# HTTP request with a distinctive, tool-like User-Agent that ET Open
# user-agent rules are designed to flag.
Write-Host "[*] Stage 2: suspicious HTTP (distinctive User-Agent)" -ForegroundColor Yellow
Invoke-WebRequest -Uri "http://testmyids.com" `
    -UserAgent "Lab6Recon/1.0 (suspicious-agent)" -UseBasicParsing | Out-Null

# --- Stage 3: Suspicious DNS ------------------------------------------------
# Resolve a benign but hunt-worthy test domain to generate a DNS record.
Write-Host "[*] Stage 3: suspicious DNS" -ForegroundColor Yellow
Resolve-DnsName -Name testmyids.com -ErrorAction SilentlyContinue | Out-Null

Write-Host "[*] Attacker stages complete." -ForegroundColor Green
Write-Host "[*] NOW start the beacon on UB2604:  ./lab6-beacon_v18.sh" -ForegroundColor Green

Script 2 — lab6-beacon_v18.sh (runs on UB2604)

The complete, verbatim source of lab6-beacon_v18.sh. Type or copy it exactly as shown; everything between the rules below is the script.

::: {custom-style="CodeLabel"} UB2604 · Terminal :::

#!/usr/bin/env bash
# =============================================================================
#  lab6-beacon_v18.sh
#  Lab 6 — Emerging Threats Investigation
#  Runs on: UB2604 (192.168.1.10 / .15)   |   Run as: standard user
# =============================================================================
#  PURPOSE
#    Emulates a command-and-control (C2) BEACON: a periodic HTTP callback with
#    a distinctive URI and User-Agent, at a FIXED interval. The defining
#    signature of a beacon is not its content but its REGULAR TIMING.
#
#    Distinctive markers (used to write the custom signature in Part 4):
#      URI         : /beacon
#      User-Agent  : Lab6Beacon/1.0
#      Interval    : 30 seconds, 6 callbacks
#
#  SAFE BY DESIGN
#    Harmless periodic HTTP GET to a benign test endpoint. No malicious code.
#    Run in an isolated lab VM only.
#
#  BEFORE YOU RUN
#    1. Run Invoke-Lab6Attacker_v18.ps1 on WIN11 first (recon + HTTP + DNS stages).
#    2. Make this script executable:  chmod +x lab6-beacon_v18.sh
#    3. Let it run for at least 5 intervals so the timing pattern is visible.
#
#  INVESTIGATE
#    The beacon may fire NO dedicated ET Open alert — hunt it by TIMING:
#      sudo tail -n 1000 /var/log/suricata/eve.json \
#        | jq -r 'select(.event_type=="http") | "\(.timestamp) \(.dest_ip) \(.http.hostname)"' \
#        | sort
#    Look for requests at a near-constant ~30s interval to the same host.
# =============================================================================

TARGET="http://testmyids.com/beacon"
UA="Lab6Beacon/1.0"
INTERVAL=30
COUNT=6

echo "[*] Beacon starting: $(date -Is) -- $COUNT callbacks every ${INTERVAL}s"
for i in $(seq 1 "$COUNT"); do
    curl -s -A "$UA" "$TARGET" -o /dev/null
    echo "[*] beacon $i/$COUNT sent: $(date -Is)"
    sleep "$INTERVAL"
done
echo "[*] Beacon complete."

Stage-by-Stage: What Fires and Where

Stage Script / Line Telemetry Produced How to Hunt It
Recon nmap -sS (attacker) ET SCAN alert; flow records Task 2.2 — signature:SCAN
Malicious HTTP Invoke-WebRequest, custom UA ET user-agent alert; http record Task 2.3–2.4 — pivot on src_ip, jq http
Suspicious DNS Resolve-DnsName dns record; possible ET DNS alert Task 2.3 — dns alert
Beacon lab6-beacon_v18.sh loop periodic http records to /beacon Task 2.5 — timing in eve.json

The Beacon’s Distinctive Markers (for the Custom Rule)

The beacon is deliberately given two distinctive, huntable markers so students can write a precise signature in Part 4:

  • URI: /beacon

  • User-Agent: Lab6Beacon/1.0

These are what the Task 4.3 example signature matches (http.uri content /beacon, http.header content User-Agent: Lab6Beacon). If you change them per section (below), update the model rule accordingly.

A Note on Scan Source and Interface

The generator scans 192.168.1.10 (UB2604’s management interface). Because ens192 is promiscuous and the segment is mirrored to it, Suricata sees this scan even though ens192 is not itself the target and has no address of its own — that is precisely what a silent sensor is for. If your topology mirrors a different segment, adjust the target so the scan crosses the interface ens192 monitors — otherwise Suricata never sees it (the Lab 0 tcpdump ground-truth check applies). If nmap is not installed on WIN11, run the scan stage from UB2604 instead (sudo nmap -sS 192.168.1.40); the hunt is unchanged, only the src_ip differs.

Cleanup

On UB2604, remove the custom rule after a graded run if desired:

::: {custom-style="CodeLabel"} UB2604 · Terminal :::

sudo cp /etc/suricata/rules/local.rules.bak /etc/suricata/rules/local.rules
sudo suricata -T -c /etc/suricata/suricata.yaml && sudo systemctl restart suricata

Reverting to the pre-lab snapshot is the cleanest reset.

Instructor Notes

  • Per-section uniqueness: Change the beacon URI (/beacon → /checkin, etc.), the User-Agent, and the INTERVAL per section. This makes each section’s correct signature different and defeats answer sharing. Update the Answer Key’s model rule to match.

  • Beacon interval: 30s × 6 gives a clear pattern in ~3 minutes. Shorten INTERVAL for a faster demo or lengthen it to make the periodicity subtler for advanced sections.

  • Encrypted variant (advanced): Point the beacon at an HTTPS endpoint. The http record disappears (TLS), and students must hunt on tls records and flow timing instead — a strong lead-in to network-forensics discussions and the limits of content inspection.

  • ET Open variance: Exactly which ET SCAN / user-agent signatures fire depends on the installed ruleset version. Confirm on your master image before class which SIDs fire, and record them for the Answer Key.

      Version 5       Page  of