How I Built a Home SOC Using Wazuh, MISP, Cortex & Shuffle – Open Source
These days, security becomes more critical by the hour. While there’s no shortage of security products out there, many come with a hefty price tag.
At the same time, open source is becoming increasingly important—especially when it comes to digital sovereignty. So, I decided to put this to the test and build a simple SOC/SIEM stack at home using only open-source tools.
Here’s what the stack consists of:
For this setup, I wanted to analyze outbound traffic from my Fortigate firewall. The data flow looks like this:
Here’s a quick diagram of the setup:
Curious how I put it all together? Let’s dive in.
In MISP, I enabled several feeds including CIRCL, Botvrij, and Abuse.ch. These feeds pull in events with IOCs:
To keep things performant, I decided to periodically import IOCs from MISP events into a Wazuh CDB list. I wrote a small Python script to extract IPs from MISP and write them to /var/ossec/etc/lists
.
#!/usr/bin/python3
from pymisp import ExpandedPyMISP
import re
import os
MISP_URL = 'https://misp'
MISP_KEY = 'xxxx'
VERIFY_CERT = False
OUTPUT_FILE = '/var/ossec/etc/lists/misp_ips'
print("Starting MISP IP import...")
misp = ExpandedPyMISP(MISP_URL, MISP_KEY, VERIFY_CERT)
result = misp.search(controller='attributes', type_attribute='ip-dst')
print(
f"Found {len(result['Attribute'])} attributes in MISP, writing to {OUTPUT_FILE}...")
with open(OUTPUT_FILE, 'w') as f:
pattern = r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$"
f.seek(0)
f.truncate()
for attr in result['Attribute']:
if re.match(pattern, attr['value']):
value = attr['value']
f.write(f'{value}:misp\n')
print("Restarting Wazuh manager...")
os.system("systemctl restart wazuh-manager")
print("MISP IP import completed successfully.")
Once the list is updated, restarting the Wazuh manager triggers the creation of a new lookup database.
To get syslog data from Fortigate into Wazuh, I added the following configuration in the Wazuh manager:
<remote>
<connection>syslog</connection>
<port>514</port>
<protocol>udp</protocol>
<allowed-ips>10.59.0.0/16</allowed-ips>
</remote>
Then, I configured my Fortigate to send logs to the Wazuh manager:
Wazuh comes with built-in decoders for Fortigate logs, which worked well out of the box. For traffic events, the rule with ID 81618 was triggered.
I added a custom rule to match outbound traffic where the destination IP appears in the CDB list. If there’s a hit, Wazuh triggers a new alert with ID 100010:
<rule id="100010" level="12">
<if_sid>81618</if_sid>
<list field="dstip" lookup="address_match_key">etc/lists/misp_ips</list>
<description>Outgoing traffic seen on Fortigate to known bad IP</description>
</rule>
In Cortex, I enabled the AbuseIPDB analyzer. Of course, Cortex is most useful when combining multiple analyzers, but for this proof-of-concept, one is enough to demonstrate the workflow.
I created a Shuffle workflow triggered by a webhook. The steps look like this:
To trigger the Shuffle workflow when a Wazuh rule hits, I added the following integration config to Wazuh:
<integration>
<name>shuffle</name>
<hook_url>https://shuffle.internal.remcokersten.nl/api/v1/hooks/webhook_9195d1d8-43d5-4090-b080-4c3f57a01242</hook_url>
<rule_id>100010</rule_id>
<alert_format>json</alert_format>
</integration>
With just a few open-source tools, it’s entirely possible to build a functional SOC/SIEM environment at home. While this setup is still a work in progress, it already provides valuable insights and hands-on experience in threat detection and response.
This kind of stack is ideal for learning, experimenting, and developing a deeper understanding of security operations—all without breaking the bank.