Project Nebula: Build a Simple Detection Lab

· 7min · Joe Lopes
Photo of the Crab Nebula.

Working in Detection Engineering, I often find myself compelled to test things, such as how the operating system logs an action and how it can be detected. Even with a robust SIEM at my disposal, forwarding logs from a dummy workstation to the SIEM is not a straightforward task due to the complexity of the enterprise network. This is where having a lab comes in handy.

I searched for existing projects to use, but DetectionLab is officially deprecated, and HELK hasn’t been updated since 2021. Additionally, both labs were quite complex, which went against my goal of creating something simple, easy to deploy, and resource-efficient.

I decided to build my own lab with a focus on simplicity and resource efficiency. This is the first of a four-part series detailing how I built it. In this post, I’ll cover the lab’s architecture, the tools I used, and basic configurations. The next two posts will delve into the monitoring tools (Wazuh and Elastic) and how to use them to detect specific actions. The final post will summarize my overall experience, provide a simple review of the tools, and suggest ideas for improving the lab. Let's get started! 👊🏻

abstract
Series

Mise en Place

The lab consists of two main components: the client and the server. The client side will be built with virtual machines (VMs) to allow more control over each host for testing purposes. The server side will be containerized for easier deployment and better resource utilization.

For virtualization, I’ll use VMware Fusion Pro, and for containerization, Docker. I built this lab under the following conditions, and it's important to note that different tools or versions may require different commands or approaches.

  • Host: Apple M2 (ARM 64) with macOS Sonoma 14.6
  • Virtualization: VMware Fusion Pro 13.5.2
  • Containerization: Docker 26.1.4, Docker Compose 2.29.3, and Docker Desktop 4.31.0 (life is short)
warning
Warning

I’m running on an ARM-based machine. This is important because some software, like Ubuntu Desktop, does not have an ARM version. Others, like Windows and Sysmon, require specific versions to run on ARM.

Naming

Having a well-defined naming standard isn’t mandatory, but it significantly helps with future tests as you need to refer to hosts or users. I’ve defined a convention based on NASA space missions, and the table below summarizes everything:

OSHostnameUserPasswordNotes
WindowsapolloneilMoonsh0tDisk encryption: Moonsh0t
UbuntugeminicarlMoonsh0t-
KalihubblebuzzMoonsh0tKali's default credentials

The name Nebula resembles the cloud where stars are born, making it a fitting analogy for a lab where ideas are born and tested.

Lab Topology

Before diving into the setup, I planned the lab topology to ensure that I could cover all the use cases I had in mind. The roles are as follows:

  • Windows and Ubuntu machines will act as workstations where users perform their daily tasks.
  • Wazuh/Elastic will serve as the SIEM, monitoring the workstations.
  • Kali will act as an adversary, exploiting the workstations.

The network topology is illustrated in the diagram below:

  
flowchart TD
    subgraph Legend
        X -->|Network flow| Y
        K .->|Data flow| W
        markdown["`⚠️ This diagram includes both actual connections and abstracted representations to illustrate the relationships between Docker, VMware, and the physical host. Ports represented here are based on Elastic's configuration.`"]
    end

    subgraph VMware
        A[Windows]
        B[Ubuntu]
        C[Kali]
        D((Virtual Router<br>NAT))
    end

    subgraph Docker
        E((Virtual Router<br>Bridge))
        F[Elasticsearch]
        F1[Kibana]
        F2[Fleet server]
    end

    subgraph Host
        VMware
        Docker
        G((NIC<br>RFC 1918))
    end

    A o--o D
    B o--o D
    C o--o D

    E o--o|9200| F
    E o--o|5601| F1
    E o--o|8220| F2

    D o--o G
    E o--o G

    A <.->|8220/9200| G
    B <.->|8220/9200| G
    G <.->|Port mapping| E

The host (macOS) will mediate all connections between VMware and Docker. Docker's port mappings will expose the containers using the host's IP address and the ports defined in the docker-compose.yml file. Consequently, the VMs will be able to communicate with the containers via the host's IP address and the ports mapped by Docker. For example, to send logs to Elastic, the Windows VM will use the host's IP address and port 9200. Docker will capture this traffic and forward it to the Elastic container.

warning
Warning

It’s important to note that this lab is intended for Detection Engineering purposes only. I’m not planning to run any malware ☠️ inside it. If I decide to do it in the future, I would carefully consider other options besides my own machine, as bugs and vulnerabilities in the hypervisor could potentially damage the host and expose my data, as seen here.

Workstations

With the lab topology in mind, I began setting up the VMs. VMware Fusion offers a convenient feature that allowed me to download Windows 11 ARM 🪟 directly from its interface. I simply clicked the "Get Windows from Microsoft" button on the "New VM" page, and everything went smoothly. I used the evaluation version of Windows Pro, which automatically detected that I was on an ARM machine and downloaded the correct version.

For Ubuntu 🐧, I initially intended to test Ubuntu Desktop, but unfortunately, it does not currently support ARM 😔. This wasn’t a major issue, as I substituted it with Ubuntu Server 24.04 for ARM, available here. The installation was straightforward and encountered no notable issues. Finally, I installed Kali 2024.2 ⚔️ for Apple Silicon (ARM64) from here. In all three cases, I used the credentials and names as previously outlined.

warning
Warning

When installing each VM, pay special attention to the time 🕘 and timezone 🗺️ configuration. For SIEM purposes, time discrepancies in logs can prevent proper log ingestion and event correlation. As a rule of thumb in a lab like this, use NTP to synchronize the time of all VMs with a reliable source and set the timezone to match the host.

By default, the hypervisor sets the VM's NICs to NAT mode using DHCP, so setting them up was straightforward --which is why I’m not mentioning IP addresses in this series; instead, I'll use names. The most crucial point is that the VMs need to be able to reach the host. As shown in the diagram, Docker port mapping ensures that packets from the VMs reach the containers through the host's IP address.

tip
Tip

At this stage, it’s worth creating snapshots of all VMs to avoid having to reinstall them in the future.

To Be Continued

In this post, I covered the motivation behind building a simple Detection Engineering lab, the topology I designed, and the tools I selected. I’ve also installed the VMs and taken snapshots of them to easily roll back changes. I haven’t delved into the specifics of installing the VMs, as I assume you are familiar with the process.

At this point, the "workstations" are up and running but remain unmonitored. In the next post, I’ll set up Wazuh to start monitoring. Stay tuned! 🚀