Part 2: Threat Detection Engineering and Incident Response with AuditD and Sentinel — Combine Events by ID with Laurel before sending to Sentinel as JSON and Parser for event searching and alert building

Truvis Thornton
8 min readMay 27, 2024

--

NOTE: This article is based off the following:

In this guide we will install Laurel which will combine all the AuditD events which will make creating higher fidelty alerts easier.

Install Laurel

For those new to Laurel, it’s a pluginf or AuditD which will combine allt he events together into a json object. This makes it easier to get a bigger picture as to what is going on.

The plugin has been written in rust and needs to be compiled. This is an easy way to install the latest version of Rust which we will need:

curl https://sh.rustup.rs -sSf | sh

From there we need to do some house keeping. Lets install needed libraries and create needed items:


source "$HOME/.cargo/env"
apt-get install libclang-dev, libacl1-dev
mkdir /home/laurel
mkdir /etc/laurel/
cd /home/laurel/laurel
useradd --system --home-dir /var/log/laurel --create-home _laurel

Now lets download Laurel, compile and install:

git clone https://github.com/threathunters-io/laurel
cargo build --release
install -m755 target/release/laurel /usr/local/sbin/laurel

Configure Laurel and AuditD

Add to /etc/laurel/config.toml

# Write log files relative to this directory
directory = "/var/log/laurel"
# Drop privileges from root to this user
user = "_laurel"
# The periodical time window in seconds for status information to be printed to Syslog.
# Status report includes the running version, config and parsing stats.
# Default is 0 --> no status reports.
statusreport-period = 0
# By default, audit events are read from stdin ("stdin"). Alternatively, they
# can be consumed from an existing UNIX domain socket ("unix:/path/to/socket")
input = "stdin"

# A string that is written to the log on startup and
# whenever Laurel writes a status report.
# marker = "correct-horse-battery-staple"

[auditlog]
# Base file name for the JSONL-based log file. Set to "-" to log to stdout. In this case
# other log file related settings will be ignored.
file = "audit.log"
# Rotate when log file reaches this size (in bytes)
size = 5000000
# When rotating, keep this number of generations around
generations = 10
# Grant read permissions on the log files to these users, using
# POSIX ACLs
read-users = [ "_laurel" ]

# Add a prefix to every output line. The CEE cookie can be used to
# instruct consumers to parse the JSON document, cf.
# https://www.rsyslog.com/doc/master/configuration/modules/mmjsonparse.html
# line-prefix = "@cee: "

# [debug]
# dump-state-period = 120

# [debug.log]
# file = "debug.log"
# size = 1000000
# generations = 3

# [debug.parse-error-log]
# file = "parse-error.log"
# size = 1000000
# generations = 3

# [filterlog]
# # If filter.filter-action is set to "log", filtered events are
# # written to this log. It is configured just like [auditlog].
# file = "filtered.log"
# size = 1000000
# generations = 3
# read-users = [ "splunk" ]

[transform]

# "array" (the default) causes EXECVE a0, a1, a2 … arguments to be
# output as a list of strings, "ARGV". This is the default, it allows
# analysts to reliably reproduce what was executed.
#
# "string" causes arguments to be concatenated into a single string,
# separated by space characters, "ARGV_STR". This form allows for
# easier grepping, but it is impossible to tell if space characters in
# the resulting string are a separator or were part of an individual
# argument in the original command line.

execve-argv = [ "array" ]

# execve-argv = [ "array", "string" ]

# Trim excessively long EXECVE.ARGV and EXECVE.ARGV_STR entries.
# Excess is cut from the middle of the argument list and a marker
# indicating how many arguments / bytes have been cut is inserted.

# execve-argv-limit-bytes = 10000

[translate]

# Perform translations of numeric values that can also be done by
# auditd if configured with log_format=ENRICHED.

# arch, syscall, sockaddr structures
universal = false
# UID, GID values
user-db = false
# Drop raw (numeric) syscall, arch, UID, GID values if they are translated
drop-raw = false

[enrich]

# Add context (event-id, comm, exe, ppid) for *pid entries
pid = true

# List of environment variables to log for every EXECVE event
execve-env = [ "LD_PRELOAD", "LD_LIBRARY_PATH" ]

# Add container context to SYSCALL-based events
container = true

# Add script context to SYSCALL execve events
script = true

# Add groups that the user (uid) is a member of. Default: true
user-groups = true

[label-process]

# Audit records that contain certain keys can be reused as a label
# attached to the process.
#
# This is useful in combination with audit rules such as:
# -w <path> -p x -k <key>
# e.g.: -w /usr/bin/dpkg -p x -k software_mgmt
label-keys = [ "software_mgmt" ]

# Labels can be attached to or removed from processes that run certain
# programs. The file program file path (SYSCALL.exe or /proc/pid/exe)
# is matched against regular expressions. This is useful for programs
# that cannot be identified through auditd file watches (-w <path> -p
# x -k <key>).
label-exe.'^/opt/.*/bin/java$' = 'java'
label-exe.'^/usr/lib/jvm/.*/bin/java$' = 'java'
label-exe.'^/snap/amazon-ssm-agent/\d+/' = 'amazon-ssm-agent'

unlabel-exe."bin/php$" = "java"

# Labels can be attached to or removed from processes that have been identified as
# scripts.
label-script."^/root/maint-.*[.]sh$" = "maint"

# unlabel-script."…" = "maint"

# Process Labels can be propagated to spawned child processes. This is
# useful for marking an entire subtree of children that have been
# spawned within certain contexts (e.g. system management tools,
# container runtimes, ssh servers, cron, etc.).
propagate-labels = [ "software_mgmt", "amazon-ssm-agent" ]

[filter]

# When audit records with attached keys are being generated,
# LAUREL will discard these.

# filter-keys = ["filter-this"]

# In addition to key based filtering it is also possible to configure label based
# filtering. This alows the possibility to filter based on parent processes.

# filter-labels = ["software_mgmt"]

# Filter events without specified key

filter-null-keys = false

# Filter events that were constructed from input lines matching these
# regular expressions
# filter-raw-lines = [
# "^type=PATH msg=\\S*? item=\\S*? name=\"/var/run/nscd[.]sock\" "
# ]

# Keep the first event observed for any given process even if it would
# be filtered otherwise. This should only be turned off if
# reproducible process tracking or process tree reconstruction is not
# required.
# keep-first-per-processes = true

# What to do with filtered events? "drop" or "log" to the filterlog
# defined above.
filter-action = "drop"

If you ever need to test your config, you can always run the binary with the config manually to check for errors:

/usr/local/sbin/laurel --config /etc/laurel/config.toml

Add to /etc/audit/plugins.d/laurel.conf (AuditD Version 3)

Add to /etc/audisp/plugins.d/laurel.conf (AuditD Version 2)

Use dpkg — list auditd to get verison

active = yes
direction = out
type = always
format = string
path = /usr/local/sbin/laurel
args = --config /etc/laurel/config.toml

If you ever need to test, you can also run auditd -f to see the real time actions.

NOTE: If you are running SELinux, compile the provided policy and install it into the running kernel:

make -C contrib/selinux
semodule -i contrib/selinux/laurel.pp
restorecon -v -R -F /usr/local/sbin/laurel /etc/laurel /var/log/laurel

Tell auditd(8) to re-evaluate its configuration:

pkill -HUP auditd

We can run systemctl status auditd.service and see that it’s now running as a sub system and as it’s own user

Send logs to Sentinel

Lets add the following to /etc/ryslog.conf


$ModLoad imfile
$InputFileName /var/log/laurel/audit.log
$InputFileTag AuditD:
$InputFileStateFile audit_log
$InputFileSeverity info
$InputFileFacility local6
$InputRunFileMonitor

Restart ryslogd with systemctl rsyslog restart and we will start to see the logs flow in.

This makes it easier to work with as when we write alert logic rules we now have more information about the process and what is going on so we can extract meta data.

Parsing the Logs

We can create a parser like the following so that we can easily search out logs and build alerts based on multiple meta keys. You can build this parser out more as desired:

// AuditD Parser Function for KQL Sentinel
// Version 1.0
// https://medium.com/@truvis.thornton/part-2-threat-detection-engineering-and-incident-response-with-auditd-and-sentinel-combine-a3384e1164e6
Syslog
| where ProcessName == "AuditD"
| extend CDW = tostring(parse_json(tostring(parse_json(SyslogMessage).CWD)).cwd)
| extend USER = tostring(parse_json(tostring(parse_json(SyslogMessage).SYSCALL)).UID)
| extend EXE = tostring(parse_json(tostring(parse_json(SyslogMessage).SYSCALL)).exe)
| extend Key_ = tostring(parse_json(tostring(parse_json(SyslogMessage).SYSCALL)).key)
| extend full_cli_EXE = replace_strings(tostring(pack_array(parse_json(tostring(parse_json(tostring(parse_json(SyslogMessage).EXECVE)).ARGV)))), dynamic(['[', ']', '"', ","]), dynamic(['', '', '', ' ']))
| extend full_cli_PROCTITLE = replace_strings(tostring(pack_array(parse_json(tostring(parse_json(tostring(parse_json(SyslogMessage).PROCTITLE)).ARGV)))), dynamic(['[', ']', '"', ","]), dynamic(['', '', '', ' ']))
| extend full_cli = iff(isnotnull(full_cli_PROCTITLE),full_cli_PROCTITLE,full_cli_EXE)
| project-away full_cli_EXE,full_cli_PROCTITLE

In the next article we will go over how to fine tune Laurel so that we can better use the events for threat hunting and incident response and do log pruning to reduce log ingestion, along with detecting an exploit being launched.

☕ Like what you read? Did it help you?

Send some coffee and love https://buymeacoffee.com/truvis :)
Your support helps pay for licenses, research & development, and other costs that allow me to bring you new guides and content!

❗If you are new to my content, be sure to follow/connect with me on all my other socials for new ideas and solutions to complicated real world problems and jump start your career! New content drops daily/weekly along with tips and tricks :)

👉 W: https://truv.is
👉 T: https://twitter.com/thattechkitten
👉 Y: https://www.youtube.com/@TRUValueInformationSecurity
👉 G: https://github.com/truvis
👉 L: https://www.linkedin.com/in/truvisthornton
👉 M: https://medium.com/@truvis.thornton

--

--

Truvis Thornton

🛡Cyber Defense Architect 🕵🏼‍♂️Threat Hunter/Researcher 👨🏻‍🔬Detection Engineer 👨🏻‍💻SIEM/SOAR/SOC 💡Follow for new ideas and solutions