Implementing Detection-as-Code: Constructing a Pipeline for Sysmon Configuration – Hacker

The configuration file of the Sysmon monitoring tool built into Windows can grow to thousands of lines, which describe the rules for selecting events recorded in the logs. Sysmon's standard set of functions for checking configs is inconvenient and makes the analyst's work difficult. In this article I will show how to create a CI/CD pipeline to validate the Sysmon config and make it impossible to lose events.

The main problem when working with Sysmon is that in case of an error, you receive text with the attributes of an XML file, as in the picture below.

Advertisement

Sysmon error output example

Errors can be associated with typos, incorrect parameters, new types of events – all of them can lead to disruption of the monitoring process. For automatic validation, we could use an XSD schema, which is not supplied with Sysmon.

www

On GitHub There was a similar project, the author described the XSD schema for different versions of Sysmon and developed PowerShell modules. The project is currently not supported.

To solve the problem we can use the approach Detection-as-Code (DaC). To implement it, I used the capabilities of GitLab CI/CD.

info

DevOps practices are gaining momentum across all areas of IT. Detection engineering is no exception – the collaboration is called Detection-as-Code.

Advertisement

A Brief Introduction to Detection-as-Code

Detection-as-Code is an approach to incorporating DevOps practices into the activities of SOC and CERT analysts, as well as implementation engineers of information security and monitoring tools. DaC allows you to structure the management of detection rules, configuration files, and conduct testing before using them in the final product.

In most cases, to create a new rule or edit an existing one, the analyst must open the built-in editor of the product being used. Changes made to the user interface are difficult and inconvenient to track. The DaC approach allows us to be sure that we won't break anything. In the CI/CD process, the content that is created is tested before it is delivered to the target system.

Pipeline

Stages

My pipeline includes five stages:

  1. ".pre" — building a Docker container.
  2. build — assembling a config from modules for the required Sysmon version of the circuit.
  3. test-schema — testing the resulting configs for compliance with the Sysmon scheme.
  4. test-on-windows — testing configs on hosts (if necessary, you can use Windows 10 and Windows 7 if you need a config for legacy).
  5. deploy — delivery of the config to users.
Sysmon error output example
Sysmon error output example

This is what the beginning of the file looks like .gitlab-ci.yml:

stages:

- ".pre"

- build

- test-schema

- test-on-windows

- deploy

workflow:

rules:

- changes:

- templates/*.xml

Keyword stages defines the stages in the pipeline, and workflow — a rule describing when it will run. My pipeline only runs when config modules change.

Zero stage. Building a Docker image

To build a Docker image I use the approach Docker-in-Docker. The Docker file is also in the repository. In this step we install Ansible, pywinrm and the Ansible Community.Windows collection into the image we are building.

FROM python:3.12-bookworm

LABEL maintainer="d3f0x0"

RUN apt-get update && apt-get upgrade -y && pip install --upgrade pip && apt-get install ansible -y && pip3 install pywinrm && ansible-galaxy collection install community.windows && apt-get clean

VOLUME ( "/data" )

WORKDIR /data

CMD ("ansible-playbook", "--version")

Job for GitLab looks like this:

build-docker:

stage: ".pre"

image: docker:dind

tags:

- docker

services:

- name: docker:dind

script:

- echo -n "$CI_REGISTRY_PASSWORD" | docker login -u $CI_REGISTRY_USER $CI_REGISTRY --password-stdin

- docker pull "$CI_REGISTRY_IMAGE:latest" || true

- >

docker build

--pull

--cache-from $CI_REGISTRY_IMAGE:latest

--label "org.opencontainers.image.title=$CI_PROJECT_TITLE"

--label "org.opencontainers.image.url=$CI_PROJECT_URL"

--label "org.opencontainers.image.created=$CI_JOB_STARTED_AT"

--label "org.opencontainers.image.revision=$CI_COMMIT_SHA"

--label "org.opencontainers.image.version=$CI_COMMIT_REF_NAME"

--tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA

.

- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA

As a result, the image will be added to the Docker registry of the corresponding GitLab project. We will use it in the remaining stages.

First stage. Config build

The first step in ensuring a reliable Sysmon config is to check the structure of the XML file. The XML must be valid for Sysmon to interpret the config correctly.

I decided to divide my config into modules that the Sysmon driver can register (ProcessCreate, NetworkConnection, and so on). This method is used in the repository sysmon-modular.

Sysmon error output example
Sysmon error output example

To be able to work with different versions of Sysmon, I have provided an option where the final config is assembled based on the schemes supplied with Sysmon.

Advertisement