Zero Dollar Detection and Response Orchestration with n8n, Security Onion, TheHive, and Velociraptor

Introduction

Tools Overview

Security Onion — Alerts

Security Onion — Alerts interface

TheHive — Cases

Observables in TheHive

Getting Started

Security Onion/TheHive

Velociraptor

n8n

Getting the Work Flowing

n8n — TheHive Trigger Node

webhooks {
n8nWebhook {
url = "http://n8n/$webhook"
}
}

n8n — Switch Node

n8n — Velociraptor Hash Hunt Node

  • pyvelociraptor should be installed on the n8n node (pip3 install pyvelociraptor).
  • You may need to run the following if you receive errors:
pip3 install -U pip
pip3 install cryptography
python3 pyvelociraptor --config api.config.yaml “SELECT * from info()”

n8n — Workflow Execution

{
"nodes": [
{
"parameters": {
"events": [
"case_artifact_create"
]
},
"name": "TheHive Trigger",
"type": "n8n-nodes-base.theHiveTrigger",
"typeVersion": 1,
"position": [
400,
180
],
"webhookId": "b1dcd275-0940-4925-9798-df9e121edb95",
"alwaysOutputData": true,
"retryOnFail": false,
"notesInFlow": false,
"executeOnce": false,
"continueOnFail": true
},
{
"parameters": {
"command": "=python3 /home/wlambert/.local/bin/pyvelociraptor --config /home/wlambert/api_client.yaml 'SELECT hunt(description=\"TheHive Hash Hunt::{{$json[\"body\"][\"object\"][\"_parent\"]}}::{{$json[\"body\"][\"object\"][\"data\"]}}\", expires=(now() + 60) * 1000000, artifacts=[\"Generic.Forensic.LocalHashes.Query\"],spec=dict(`Generic.Forensic.LocalHashes.Query`=dict(Hashes=\"Hash\\n{{$json[\"body\"][\"object\"][\"data\"]}}\\n\"))) AS Hunt from scope()'"
},
"name": "Velociraptor - Hash Hunt",
"type": "n8n-nodes-base.executeCommand",
"typeVersion": 1,
"position": [
810,
80
]
},
{
"parameters": {
"dataType": "string",
"value1": "={{$json[\"body\"][\"object\"][\"dataType\"]}}",
"rules": {
"rules": [
{
"value2": "hash"
},
{
"value2": "filename",
"output": 1
}
]
}
},
"name": "Route by Observable Type",
"type": "n8n-nodes-base.switch",
"typeVersion": 1,
"position": [
580,
180
]
},
{
"parameters": {
"command": "=python3 /home/wlambert/.local/bin/pyvelociraptor --config /home/wlambert/api_client.yaml 'SELECT hunt(description=\"TheHive Filename Hunt::{{$json[\"body\"][\"object\"][\"_parent\"]}}::{{$json[\"body\"][\"object\"][\"data\"]}}\", expires=(now() + 60) * 1000000, artifacts=[\"Windows.Forensics.FilenameSearch\"],spec=dict(`Windows.Forensics.FilenameSearch`=dict(yaraRule=\"wide nocase:{{$json[\"body\"][\"object\"][\"data\"]}}\"))) AS Hunt from scope()'"
},
"name": "Velociraptor - Filename Hunt",
"type": "n8n-nodes-base.executeCommand",
"typeVersion": 1,
"position": [
810,
240
]
}
],
"connections": {
"TheHive Trigger": {
"main": [
[
{
"node": "Route by Observable Type",
"type": "main",
"index": 0
}
]
]
},
"Route by Observable Type": {
"main": [
[
{
"node": "Velociraptor - Hash Hunt",
"type": "main",
"index": 0
}
],
[
{
"node": "Velociraptor - Filename Hunt",
"type": "main",
"index": 0
}
]
]
}
}
}

Testing our Work

Modified YARA rule for obfuscated batch files
File hashes and other details
Batch tokens and keywords
TheHive case created from Security Onion
TheHive observable creation
n8n workflow
Hash hunt started from TheHive in Velociraptor
  1. The hunt name/type (Ex. TheHive Hash Hunt)
  2. The TheHive case ID (Ex. p_l_vXcBUvmttZAJVYkn )
  3. The observable value (Ex. 67d419cd42edc4b4754d9f3a5c191d86)
Hash query flow issued by Velociraptor
Hit for Velociraptor hash query

Velociraptor — Built-in Automation

name: Custom.Server.Automation.Quarantine
description: |
This artifact will do the following:

- Look for artifacts with successful completion and results with regard to `ArtifactRegex`
- Look for the above, in addition to Hunts with a description equating/similar to `HuntRegex`
- Quarantine relevant Windows hosts
- Update a TheHive case with a tag noting that the client was quarantined
author: Wes Lambert, @therealwlamberttype: SERVER_EVENTparameters:
- name: ArtifactRegex
default: "Hashes"
- name: HuntRegex
default: "TheHive"
- name: DisableSSLVerify
type: bool
default: true
sources:
- query: |
LET TheHiveKey <= server_metadata().TheHiveKey
LET TheHiveURL <= server_metadata().TheHiveURL
LET ClientTag = FQDN + ' - Quarantined by Velociraptor'
LET CombinedTags = array(a1=Tags,a2=ClientTag)
LET GetTags =
SELECT parse_json(data=Content).tags AS Tags FROM http_client(
headers=dict(`Content-Type`="application/json", `Authorization`=format(format="Bearer %v", args=[TheHiveKey])),
disable_ssl_security=DisableSSLVerify,
method="GET",
url=format(format="%v/api/case/%v", args=[TheHiveURL,TheHiveCaseID]))
LET FinalTags = SELECT Tags, if(condition=Tags=NULL, then=ClientTag, else=CombinedTags) AS SendTags FROM GetTags
LET PostTags =
SELECT * FROM http_client(
data=serialize(item=dict(tags=FinalTags.SendTags[0]), format="json"),
headers=dict(`Content-Type`="application/json", `Authorization`=format(format="Bearer %v", args=[TheHiveKey])),
disable_ssl_security=DisableSSLVerify,
method="PATCH",
url=format(format="%v/api/case/%v", args=[TheHiveURL,TheHiveCaseID]))
LET FlowInfo = SELECT Flow.client_id AS ClientID, client_info(client_id=ClientId).os_info.fqdn AS FQDN, Flow.request.creator AS FlowCreator, Flow FROM watch_monitoring(artifact="System.Flow.Completion") WHERE Flow.artifacts_with_results =~ ArtifactRegex
LET StartQuarantine =
SELECT ClientID,
{SELECT hunt_description from hunts(hunt_id=FlowCreator)} AS HuntDescription,
{SELECT split(string=hunt_description, sep="::")[1] from hunts(hunt_id=FlowCreator)} AS TheHiveCaseID,
{SELECT collect_client(client_id=ClientID, artifacts=["Windows.Remediation.Quarantine"], spec=dict(`Windows.Remediation.Quarantine`=dict())) FROM scope() } AS Quarantine,
FQDN
FROM FlowInfo WHERE HuntDescription =~ HuntRegex
SELECT * FROM foreach (
row=StartQuarantine,
query={ SELECT ClientID, Quarantine, {SELECT * FROM PostTags} AS TheHiveUpdate FROM scope() }
)
Automated quarantine artifact
Quarantine started by query result and server artifact
TheHive case tag(s)

Velociraptor — Additional Context and Correlation

Query to upload flow results to Elasticsearch/Logstash
Query to only write flow results to disk
Data flow summary
Hunt record for Velociraptor hunt result
{
“name”: “Velociraptor”,
“description”: “VR Client Lookup”,
“icon”: “fa-external-link-alt”,
“target”: “_blank”,
“links”: [
https://192.168.6.175:8889/app/index.html?#/collected/{value}/{:flow.id}"
]
}
Pivot from Velociraptor client ID

Alternate approaches

  • Generate hunts via webhook/n8n as described above, but do so from alert artifacts vs case observables (we don’t use TheHive alerts in Security Onion, but this could be from a standalone TheHive instance, or if you would still like to generate alerts using Elastalert, or another feeder). If an alert is generated, and the artifact type matches the criteria set by n8n, then schedule a hunt in Velociraptor.
  • Use Logstash’s HTTP output from Security Onion to send specific cloned alerts/events to the HTTP trigger in n8n, then filter them for items of interest and create a case in TheHive based on certain criteria, having Velociraptor automatically hunt for the extracted indicators.
  • Automatically trigger Cortex analyzers via n8n, retrieve the results, then initiating a Velociraptor hunt, or other actions based off of that.

Conclusion

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store