| 1 | # SOC Automation Lab |
| 2 | |
| 3 | A working security operations lab that ties endpoint telemetry, alert triage, and |
| 4 | automated case handling into a single pipeline. Wazuh handles detection, Shuffle |
| 5 | runs the SOAR playbooks, and TheHive is the analyst workspace where cases land |
| 6 | already enriched. |
| 7 | |
| 8 | I built this to stop doing the same three things by hand on every alert: pulling |
| 9 | reputation data on an indicator, opening a case, and pinging the channel. The |
| 10 | playbook does all of that in the few seconds between Wazuh firing and an analyst |
| 11 | opening TheHive. |
| 12 | |
| 13 | ## Architecture |
| 14 | |
| 15 | ```mermaid |
| 16 | flowchart LR |
| 17 | A[Windows / Linux endpoints<br/>Wazuh agent] -->|events| B[Wazuh Manager] |
| 18 | B -->|rule match| C[custom-thehive integration] |
| 19 | C -->|webhook| D[Shuffle] |
| 20 | D -->|enrich indicator| E[VirusTotal / OTX] |
| 21 | D -->|create + tag case| F[TheHive] |
| 22 | D -->|notify| G[Slack / email] |
| 23 | F -->|analyst response| B |
| 24 | ``` |
| 25 | |
| 26 | Telemetry flows up from the agents to the Wazuh manager. When a rule above the |
| 27 | configured severity fires, the manager calls a Python integration that posts the |
| 28 | alert to a Shuffle webhook. Shuffle pulls reputation on the offending indicator, |
| 29 | opens a TheHive case with the verdict attached, sets severity from the score, and |
| 30 | drops a message in the analyst channel. Everything after the rule match is hands |
| 31 | off. |
| 32 | |
| 33 | The detection layer, running with a Linux endpoint enrolled and an SSH brute-force |
| 34 | replayed against it - alerts land already mapped to MITRE ATT&CK: |
| 35 | |
| 36 |  |
| 37 |  |
| 38 | |
| 39 | ## Stack |
| 40 | |
| 41 | | Component | Role | Version pinned | |
| 42 | |-----------|------|----------------| |
| 43 | | Wazuh (indexer, manager, dashboard) | SIEM / detection | 4.9.0 | |
| 44 | | TheHive 5 + Cassandra + Elasticsearch | Case management | 5.4 | |
| 45 | | Cortex | Observable analyzers | 3.1 | |
| 46 | | Shuffle | SOAR / orchestration | 1.4.0 | |
| 47 | |
| 48 | ## Layout |
| 49 | |
| 50 | ``` |
| 51 | config/wazuh/ manager config, custom rules and decoders |
| 52 | integrations/ Wazuh -> Shuffle/TheHive integration |
| 53 | shuffle/ exported SOAR workflow + its compose file |
| 54 | scripts/ deploy and agent enrollment helpers |
| 55 | docs/ architecture notes and the playbook walkthrough |
| 56 | docker-compose.yml Wazuh + TheHive stack |
| 57 | ``` |
| 58 | |
| 59 | ## Bring it up |
| 60 | |
| 61 | ```bash |
| 62 | cp .env.example .env |
| 63 | # set the indexer/dashboard passwords and TheHive secret in .env first |
| 64 | ./scripts/deploy.sh |
| 65 | ``` |
| 66 | |
| 67 | `deploy.sh` generates the Wazuh indexer certificates, starts the stack, waits for |
| 68 | the indexer to go green, and prints the dashboard and TheHive URLs. Give it three |
| 69 | or four minutes on first run while Cassandra initializes. |
| 70 | |
| 71 | Shuffle runs as its own stack so it survives a teardown of the SIEM side: |
| 72 | |
| 73 | ```bash |
| 74 | cd shuffle && docker compose up -d |
| 75 | ``` |
| 76 | |
| 77 | Import `shuffle/workflows/wazuh-thehive-enrichment.json` from the Shuffle UI, drop |
| 78 | your VirusTotal/OTX keys and TheHive API key into the app authentication fields, |
| 79 | then copy the webhook URL into `config/wazuh/manager/ossec.conf`. |
| 80 | |
| 81 | ## Enrolling an endpoint |
| 82 | |
| 83 | Windows: |
| 84 | |
| 85 | ```powershell |
| 86 | .\scripts\enroll-agent.ps1 -Manager REDACTED-IP -Group windows |
| 87 | ``` |
| 88 | |
| 89 | Linux: |
| 90 | |
| 91 | ```bash |
| 92 | WAZUH_MANAGER=REDACTED-IP ./scripts/enroll-agent.sh |
| 93 | ``` |
| 94 | |
| 95 | The Windows group ships a Sysmon-aware config so process-creation and network |
| 96 | events come through with enough context for the rules in `local_rules.xml` to be |
| 97 | useful. |
| 98 | |
| 99 | ## What the rules cover |
| 100 | |
| 101 | `config/wazuh/rules/local_rules.xml` adds detections on top of the Wazuh |
| 102 | ruleset for the cases I actually wanted automated: |
| 103 | |
| 104 | - Sysmon process creation from suspicious paths (`AppData`, `Temp`, `ProgramData`) |
| 105 | - LSASS access patterns consistent with credential dumping |
| 106 | - New service creation and scheduled-task persistence |
| 107 | - Brute force against RDP / SSH escalated past the default threshold |
| 108 | - Outbound connections to indicators present in the CTI watchlists |
| 109 | |
| 110 | Rules at level 10 and above hand off to the integration. Everything below stays in |
| 111 | the dashboard. |
| 112 | |
| 113 | ## The playbook |
| 114 | |
| 115 | Full walkthrough with the decision points is in [docs/playbook.md](docs/playbook.md). |
| 116 | Short version: alert in, indicator enriched, case out, analyst notified, with the |
| 117 | raw alert and the enrichment verdict both attached to the case so triage starts |
| 118 | from evidence instead of a blank ticket. |
| 119 | |
| 120 | ## Notes |
| 121 | |
| 122 | This is a lab build. The compose file runs everything single-node on one host, |
| 123 | which is fine for a homelab or a demo but is not how you'd run any of these in |
| 124 | production - Wazuh indexer and TheHive's Cassandra/Elasticsearch both want their |
| 125 | own nodes and real resource headroom once you put load on them. |