One PC, twelve machines, a whole SOC

SocForge is a complete SOC lab built with VirtualBox on a single computer. Every detection in it was triggered by a real, benign offensive action — launched from a Kali machine or, for local techniques, on the targeted host — then observed in the SIEM and documented with its logs, UTC timestamps and screenshots. Nothing is asserted without a capture behind it.

12
virtual machines
29
detection rules written for the lab
16
scenario sheets (SC-01 to SC-16)
5
filtered network zones, default deny
6
nodes in the automated response chain
54
automated tests run on every push

35 seconds: one attack, followed to the end

On 7 October 2026 a single attack was tracked across the whole chain. From the Kali machine, three accesses to the admin share of a Windows 11 workstation with a valid local account. Wazuh flags the session coming from the untrusted zone 7 seconds after the first connection, then escalates at the third access. From the moment that alert is delivered to the orchestrator, the rest happens without a human:

Seconds after the alert reaches Shuffle
  1. 0 sWazuh alert 100141 delivered to Shuffle
  2. 7 sAlert created in TheHive, with observables
  3. 24 sAttacker address blocked on the OPNsense firewall
  4. 29 sIsolation order sent to the host through Velociraptor
  5. 35 sThreat hunt requested from the matching MISP event
What this figure is, and is not

These are timings from one simulated attack in a lab, with automation. They are not a benchmark for a production SOC. The lab also runs on a 16 GB PC, so the machines are started in groups: the real alert was handed to Shuffle a few minutes after it was raised, using Wazuh's official integration script. The chain and its configuration are the same as on a larger host; only the sequence is split.

Architecture

SocForge lab architecture: attacker, firewall, targets, network sensor and the SOC tools on a separate management network
The attacker and the targets have no interface on the management network: they can only talk through the firewall.
LayerComponentsRole
AttackPURPLE (Kali Linux)Launches the attacks, alone in its zone
NetworkOPNsense · Suricata · ZeekDefault-deny segmentation and passive network detection
TargetsDC01 · WIN01 · LINUX01Windows domain, Windows 11 workstation with Sysmon, Ubuntu server — each with a Wazuh agent
DetectionWazuhReceives the agents, the firewall syslog and the network alerts
ResponseShuffle · TheHive · Cortex · MISPOrchestration, incident tracking, analysis, threat intelligence
DFIRVelociraptorEndpoint hunting and isolation of a compromised host

Step 1 — Collect, and check that it arrives

Before writing a single rule, every source had to be confirmed as reaching the manager: Security, PowerShell and Sysmon logs on Windows; authentication logs and real-time integrity monitoring of /etc/cron.d on Linux; the firewall log over syslog. After one day of tests the Threat Hunting dashboard shows 3,956 alerts in 24 hours, 172 of them at level 12 or above, with an ATT&CK breakdown that matches the techniques actually executed.

Step 2 — Detect, and fix the rule when it does not fire

Every technique follows the same method: run a real, benign action, check whether the rule fires, fix the rule if it does not, then keep the evidence. Eleven host scenarios cover scheduled tasks, valid accounts, admin shares, encoded PowerShell, port scans, SMB brute force, sudo and cron on Linux, lateral movement, Run-key persistence, process injection and LSASS access. Four of them taught more than the others.

SC-03 · T1021.002 · rules 100139 / 100140

Admin shares: separating noise from signal

A domain controller constantly accesses its own shares with machine accounts. Alerting on every access to C$ or ADMIN$ would drown the analyst. Rule 100139 classifies those routine accesses at level 3, without an alert; rule 100140 keeps only the accesses made by a real account, at level 10.

SC-09 · T1547.001 · rule 100147

The Run key that would not alert

A value is written under CurrentVersion\Run; Sysmon logs the write (event 13) and rule 100147 should alert at level 9. It did not. This is the rule that took the most investigation: it stayed silent while chained with if_group, and has fired since it was chained with if_sid.

SC-10 · T1055 · rules 100155 / 100156

Process injection: 16 alerts out of 18 were Ctrl+C

A script creates a thread in a local notepad.exe with CreateRemoteThread. Sysmon produces event 8 and rule 100155 alerts at level 13 — seventeen times for a single test. Analysing the noise showed that 16 of the 18 alerts kept by the manager were the Ctrl+C signal that Windows sends to console programs. Rule 100156 now classifies that case at level 3, without an alert.

SC-11 · T1003 · rule 100103

LSASS: when the defence works, there is nothing to detect

On WIN01, Windows LSA protection refuses the opening of lsass.exe before Sysmon even sees it. On DC01, where that protection is not enabled, access is granted and rule 100103 alerts at level 14, two seconds after the attempt. Same technique, two machines, two honest outcomes — both documented.

ScenarioTechniqueWhere
SC-01T1053.005 Scheduled taskDC01
SC-02T1078 Valid account, network logonDC01
SC-03T1021.002 Admin shares, noise filteringDC01
SC-04T1059.001 Encoded PowerShellWIN01
SC-05T1046 Port scan from KaliPURPLE → DC01
SC-06T1110 SMB brute forcePURPLE → DC01
SC-07T1548.003 / T1053.003 sudo and cronLINUX01
SC-08T1021.002 + T1078 Lateral movementWIN01 → DC01
SC-09T1547.001 Run keyWIN01
SC-10T1055 Process injectionWIN01
SC-11T1003 LSASS accessWIN01 (blocked) · DC01 (detected)

Step 3 — Watch the network, then cut it into zones

A dedicated sensor listens to mirrored traffic with Suricata (52,795 ET Open signatures) and Zeek, and its alerts reach Wazuh through an agent. OPNsense separates five zones with a default deny. The whole policy fits in 17 rules, described in a JSON file and applied through the API. When the attacker scans other zones, each rejected packet gives a level 8 alert and the repetition escalates to level 12. When it tries to reach the Wazuh manager itself, every attempt is blocked: the attacker cannot reach the SOC tools.

Step 4 — Automate the response, one node at a time

The integration started by hand — one alert created in TheHive through the API — to fix the format. Then the Wazuh manager sends every alert of level 10 or above to Shuffle by itself. A second workflow extracts the observables and submits them to a Cortex analyzer that queries MISP. A MISP match triggers the next stage, and every action leaves a tag and a dated note on the alert: everything the chain does can be read on the case. Alerts become cases handled with a five-task template: triage, containment, eradication, recovery, lessons learned.

Step 5 — Hunt on endpoints from threat intelligence

The idea: an indicator published in MISP should be enough to start a search on the endpoints, and the result should come back to MISP. A server artifact written for the lab reads the MISP event and creates the Velociraptor hunt itself; a second one watches for the end of hunts and sends MISP one sighting per indicator found. On 7 October the loop was replayed on Linux: MISP event published at 14:09:00, hunt created 8 seconds later without intervention, sighting back in MISP 47 seconds after publication.

Step 6 — The evidence behind the 35 seconds

The seven captures below all come from the run of 7 October. After the exercise, the isolation of WIN01 was lifted and the address removed from the firewall alias.

Guardrails on the automated response

An automation that can block an address and isolate a host needs limits that do not depend on anyone being careful. These are tested in scenario sheet SC-14:

  • A list of addresses that are never blocked.
  • A label that protects the domain controller from isolation.
  • Time-limited blocking, released by a scheduled workflow.
  • A webhook reserved for Wazuh.
  • Access to Velociraptor through an SSH key restricted to one command.
  • HTTPS services (MISP, OPNsense) called with the lab's internal CA, without disabling verification.

What I learned

  • A rule that loads is not a rule that fires. The Run-key rule was syntactically valid and silent. Only a real action against it showed the chaining error.
  • Noise is a finding. Sixteen false alerts out of eighteen is not an embarrassment to hide; it is the measurement that justified a new rule.
  • Automated response is a governance question before it is a technical one. Deciding what may never be blocked took more thought than writing the node that blocks.
  • Constraints should be written down. A 16 GB host forced a split sequence; saying so is what makes the timings credible.

The repository holds the 29 Wazuh rules, 15 Sigma rules validated in CI, the firewall policy, the Shuffle node code, the Velociraptor artifacts, an incident report written from a real alert, and all 47 screenshots. First build from 2 to 15 August 2026; rebuild and audit from 13 September to 7 October 2026.