Files
splunk-security_content/detections/endpoint/hunting_for_log4shell.yml
2022-11-16 12:39:48 -07:00

119 lines
6.4 KiB
YAML

name: Hunting for Log4Shell
id: 158b68fa-5d1a-11ec-aac8-acde48001122
version: 1
date: '2021-12-14'
author: Michael Haag, Splunk
type: Hunting
datamodel:
- Web
description: 'The following hunting query assists with quickly assessing CVE-2021-44228,
or Log4Shell, activity mapped to the Web Datamodel. This is a combination query
attempting to identify, score and dashboard. Because the Log4Shell vulnerability
requires the string to be in the logs, this will work to identify the activity anywhere
in the HTTP headers using _raw. Modify the first line to use the same pattern matching
against other log sources. Scoring is based on a simple rubric of 0-5. 5 being the
best match, and less than 5 meant to identify additional patterns that will equate
to a higher total score. \
The first jndi match identifies the standard pattern of `{jndi:` \
jndi_fastmatch is meant to identify any jndi in the logs. The score is set low and
is meant to be the "base" score used later. \
jndi_proto is a protocol match that identifies `jndi` and one of `ldap, ldaps, rmi,
dns, nis, iiop, corba, nds, http, https.` \
all_match is a very well written regex by https://gist.github.com/Schvenn that identifies
nearly all patterns of this attack behavior. \
env works to identify environment variables in the header, meant to capture `AWS_ACCESS_KEY_ID`,
`AWS_SECRET_ACCESS_KEY` and `env`. \
uri_detect is string match looking for the common uri paths currently being scanned/abused
in the wild. \
keywords matches on enumerated values that, like `$ctx:loginId`, that may be found
in the header used by the adversary. \
lookup matching is meant to catch some basic obfuscation that has been identified
using upper, lower and date. \
Scoring will then occur based on any findings. The base score is meant to be 2 ,
created by jndi_fastmatch. Everything else is meant to increase that score. \
Finally, a simple table is created to show the scoring and the _raw field. Sort
based on score or columns of interest.'
search: '| from datamodel Web.Web | eval jndi=if(match(_raw, "(\{|%7B)[jJnNdDiI]{4}:"),4,0)
| eval jndi_fastmatch=if(match(_raw, "[jJnNdDiI]{4}"),2,0) | eval jndi_proto=if(match(_raw,"(?i)jndi:(ldap[s]?|rmi|dns|nis|iiop|corba|nds|http|https):"),5,0)
| eval all_match = if(match(_raw, "(?i)(%(25){0,}20|\s)*(%(25){0,}24|\$)(%(25){0,}20|\s)*(%(25){0,}7B|{)(%(25){0,}20|\s)*(%(25){0,}(6A|4A)|J)(%(25){0,}(6E|4E)|N)(%(25){0,}(64|44)|D)(%(25){0,}(69|49)|I)(%(25){0,}20|\s)*(%(25){0,}3A|:)[\w\%]+(%(25){1,}3A|:)(%(25){1,}2F|\/)[^\n]+"),5,0)
| eval env_var = if(match(_raw, "env:") OR match(_raw, "env:AWS_ACCESS_KEY_ID")
OR match(_raw, "env:AWS_SECRET_ACCESS_KEY"),5,0) | eval uridetect = if(match(_raw,
"(?i)Basic\/Command\/Base64|Basic\/ReverseShell|Basic\/TomcatMemshell|Basic\/JBossMemshell|Basic\/WebsphereMemshell|Basic\/SpringMemshell|Basic\/Command|Deserialization\/CommonsCollectionsK|Deserialization\/CommonsBeanutils|Deserialization\/Jre8u20\/TomcatMemshell|Deserialization\/CVE_2020_2555\/WeblogicMemshell|TomcatBypass|GroovyBypass|WebsphereBypass"),4,0)
| eval keywords = if(match(_raw,"(?i)\$\{ctx\:loginId\}|\$\{map\:type\}|\$\{filename\}|\$\{date\:MM-dd-yyyy\}|\$\{docker\:containerId\}|\$\{docker\:containerName\}|\$\{docker\:imageName\}|\$\{env\:USER\}|\$\{event\:Marker\}|\$\{mdc\:UserId\}|\$\{java\:runtime\}|\$\{java\:vm\}|\$\{java\:os\}|\$\{jndi\:logging/context-name\}|\$\{hostName\}|\$\{docker\:containerId\}|\$\{k8s\:accountName\}|\$\{k8s\:clusterName\}|\$\{k8s\:containerId\}|\$\{k8s\:containerName\}|\$\{k8s\:host\}|\$\{k8s\:labels.app\}|\$\{k8s\:labels.podTemplateHash\}|\$\{k8s\:masterUrl\}|\$\{k8s\:namespaceId\}|\$\{k8s\:namespaceName\}|\$\{k8s\:podId\}|\$\{k8s\:podIp\}|\$\{k8s\:podName\}|\$\{k8s\:imageId\}|\$\{k8s\:imageName\}|\$\{log4j\:configLocation\}|\$\{log4j\:configParentLocation\}|\$\{spring\:spring.application.name\}|\$\{main\:myString\}|\$\{main\:0\}|\$\{main\:1\}|\$\{main\:2\}|\$\{main\:3\}|\$\{main\:4\}|\$\{main\:bar\}|\$\{name\}|\$\{marker\}|\$\{marker\:name\}|\$\{spring\:profiles.active[0]|\$\{sys\:logPath\}|\$\{web\:rootDir\}|\$\{sys\:user.name\}"),4,0)
| eval obf = if(match(_raw, "(\$|%24)[^ /]*({|%7b)[^ /]*(j|%6a)[^ /]*(n|%6e)[^ /]*(d|%64)[^
/]*(i|%69)[^ /]*(:|%3a)[^ /]*(:|%3a)[^ /]*(/|%2f)"),5,0) | eval lookups = if(match(_raw,
"(?i)({|%7b)(main|sys|k8s|spring|lower|upper|env|date|sd)"),4,0) | addtotals fieldname=Score,
jndi, jndi_proto, env_var, uridetect, all_match, jndi_fastmatch, keywords, obf,
lookups | where Score > 2 | stats values(Score) by jndi, jndi_proto, env_var, uridetect,
all_match, jndi_fastmatch, keywords, lookups, obf, _raw | `hunting_for_log4shell_filter`'
how_to_implement: Out of the box, the Web datamodel is required to be pre-filled.
However, tested was performed against raw httpd access logs. Change the first line
to any dataset to pass the regex's against.
known_false_positives: It is highly possible you will find false positives, however,
the base score is set to 2 for _any_ jndi found in raw logs. tune and change as
needed, include any filtering.
references:
- https://gist.github.com/olafhartong/916ebc673ba066537740164f7e7e1d72
- https://gist.github.com/Neo23x0/e4c8b03ff8cdf1fa63b7d15db6e3860b#gistcomment-3994449
- https://regex101.com/r/OSrm0q/1/
- https://github.com/Neo23x0/signature-base/blob/master/yara/expl_log4j_cve_2021_44228.yar
- https://news.sophos.com/en-us/2021/12/12/log4shell-hell-anatomy-of-an-exploit-outbreak/
- https://gist.github.com/MHaggis/1899b8554f38c8692a9fb0ceba60b44c
- https://twitter.com/sasi2103/status/1469764719850442760?s=20
tags:
analytic_story:
- Log4Shell CVE-2021-44228
- CISA AA22-320A
asset_type: Web Server
confidence: 50
context:
- Scope:Network
cve:
- CVE-2021-44228
dataset:
- https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1190/java/log4shell-nginx.log
impact: 80
kill_chain_phases:
- Exploitation
message: Hunting for Log4Shell exploitation has occurred.
mitre_attack_id:
- T1190
observable:
- name: dest
type: Hostname
role:
- Victim
- name: http_method
type: Other
role:
- Other
- name: src
type: Other
role:
- Other
product:
- Splunk Enterprise
- Splunk Enterprise Security
- Splunk Cloud
required_fields:
- _time
- Web.http_method
- Web.url
- Web.url_length
- Web.src
- Web.dest
- Web.http_user_agent
- _raw
risk_score: 40
security_domain: network