Detection: Hunting for Log4Shell

Description

The following analytic detects potential exploitation attempts of the Log4Shell vulnerability (CVE-2021-44228) by analyzing HTTP headers for specific patterns. It leverages the Web Datamodel and evaluates various indicators such as the presence of {jndi:, environment variables, and common URI paths. This detection is significant as Log4Shell allows remote code execution, posing a severe threat to systems. If confirmed malicious, attackers could gain unauthorized access, execute arbitrary code, and potentially compromise sensitive data, leading to extensive damage and data breaches.

  1
  2| from datamodel Web.Web 
  3| eval jndi=if(match(_raw, "(\{
  4|%7B)[jJnNdDiI]{4}:"),4,0) 
  5| eval jndi_fastmatch=if(match(_raw, "[jJnNdDiI]{4}"),2,0) 
  6| eval jndi_proto=if(match(_raw,"(?i)jndi:(ldap[s]?
  7|rmi
  8|dns
  9|nis
 10|iiop
 11|corba
 12|nds
 13|http
 14|https):"),5,0) 
 15| eval all_match = if(match(_raw, "(?i)(%(25){0,}20
 16|\s)*(%(25){0,}24
 17|\$)(%(25){0,}20
 18|\s)*(%(25){0,}7B
 19|{)(%(25){0,}20
 20|\s)*(%(25){0,}(6A
 21|4A)
 22|J)(%(25){0,}(6E
 23|4E)
 24|N)(%(25){0,}(64
 25|44)
 26|D)(%(25){0,}(69
 27|49)
 28|I)(%(25){0,}20
 29|\s)*(%(25){0,}3A
 30|:)[\w\%]+(%(25){1,}3A
 31|:)(%(25){1,}2F
 32|\/)[^\n]+"),5,0) 
 33| 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) 
 34| eval uridetect = if(match(_raw, "(?i)Basic\/Command\/Base64
 35|Basic\/ReverseShell
 36|Basic\/TomcatMemshell
 37|Basic\/JBossMemshell
 38|Basic\/WebsphereMemshell
 39|Basic\/SpringMemshell
 40|Basic\/Command
 41|Deserialization\/CommonsCollectionsK
 42|Deserialization\/CommonsBeanutils
 43|Deserialization\/Jre8u20\/TomcatMemshell
 44|Deserialization\/CVE_2020_2555\/WeblogicMemshell
 45|TomcatBypass
 46|GroovyBypass
 47|WebsphereBypass"),4,0) 
 48| eval keywords = if(match(_raw,"(?i)\$\{ctx\:loginId\}
 49|\$\{map\:type\}
 50|\$\{filename\}
 51|\$\{date\:MM-dd-yyyy\}
 52|\$\{docker\:containerId\}
 53|\$\{docker\:containerName\}
 54|\$\{docker\:imageName\}
 55|\$\{env\:USER\}
 56|\$\{event\:Marker\}
 57|\$\{mdc\:UserId\}
 58|\$\{java\:runtime\}
 59|\$\{java\:vm\}
 60|\$\{java\:os\}
 61|\$\{jndi\:logging/context-name\}
 62|\$\{hostName\}
 63|\$\{docker\:containerId\}
 64|\$\{k8s\:accountName\}
 65|\$\{k8s\:clusterName\}
 66|\$\{k8s\:containerId\}
 67|\$\{k8s\:containerName\}
 68|\$\{k8s\:host\}
 69|\$\{k8s\:labels.app\}
 70|\$\{k8s\:labels.podTemplateHash\}
 71|\$\{k8s\:masterUrl\}
 72|\$\{k8s\:namespaceId\}
 73|\$\{k8s\:namespaceName\}
 74|\$\{k8s\:podId\}
 75|\$\{k8s\:podIp\}
 76|\$\{k8s\:podName\}
 77|\$\{k8s\:imageId\}
 78|\$\{k8s\:imageName\}
 79|\$\{log4j\:configLocation\}
 80|\$\{log4j\:configParentLocation\}
 81|\$\{spring\:spring.application.name\}
 82|\$\{main\:myString\}
 83|\$\{main\:0\}
 84|\$\{main\:1\}
 85|\$\{main\:2\}
 86|\$\{main\:3\}
 87|\$\{main\:4\}
 88|\$\{main\:bar\}
 89|\$\{name\}
 90|\$\{marker\}
 91|\$\{marker\:name\}
 92|\$\{spring\:profiles.active[0]
 93|\$\{sys\:logPath\}
 94|\$\{web\:rootDir\}
 95|\$\{sys\:user.name\}"),4,0) 
 96| eval obf = if(match(_raw, "(\$
 97|%24)[^ /]*({
 98|%7b)[^ /]*(j
 99|%6a)[^ /]*(n
100|%6e)[^ /]*(d
101|%64)[^ /]*(i
102|%69)[^ /]*(:
103|%3a)[^ /]*(:
104|%3a)[^ /]*(/
105|%2f)"),5,0) 
106| eval lookups = if(match(_raw, "(?i)({
107|%7b)(main
108|sys
109|k8s
110|spring
111|lower
112|upper
113|env
114|date
115|sd)"),4,0)  
116| addtotals fieldname=Score, jndi, jndi_proto, env_var, uridetect, all_match, jndi_fastmatch, keywords, obf, lookups 
117| where Score > 2 
118| stats values(Score) by  jndi, jndi_proto, env_var, uridetect, all_match, jndi_fastmatch, keywords, lookups, obf, dest, src, http_method, _raw 
119| `hunting_for_log4shell_filter`

Data Source

Name Platform Sourcetype Source
Nginx Access N/A 'nginx:plus:kv' '/var/log/nginx/access.log'

Macros Used

Name Value

| hunting_for_log4shell_filter | search * |

hunting_for_log4shell_filter is an empty macro by default. It allows the user to filter out any results (false positives) without editing the SPL.

Annotations

- MITRE ATT&CK
+ Kill Chain Phases
+ NIST
+ CIS
- Threat Actors
ID Technique Tactic
T1190 Exploit Public-Facing Application Initial Access
T1133 External Remote Services Initial Access
KillChainPhase.DELIVERY
KillChainPhase.INSTALLATION
NistCategory.DE_AE
Cis18Value.CIS_13
APT28
APT29
APT39
APT41
APT5
Agrius
Axiom
BackdoorDiplomacy
BlackTech
Blue Mockingbird
Cinnamon Tempest
Dragonfly
Earth Lusca
Ember Bear
FIN13
FIN7
Fox Kitten
GALLIUM
GOLD SOUTHFIELD
HAFNIUM
INC Ransom
Ke3chang
Kimsuky
Magic Hound
Moses Staff
MuddyWater
Play
Rocke
Sandworm Team
Threat Group-3390
ToddyCat
Volatile Cedar
Volt Typhoon
Winter Vivern
menuPass
APT18
APT28
APT29
APT41
Akira
Chimera
Dragonfly
Ember Bear
FIN13
FIN5
GALLIUM
GOLD SOUTHFIELD
Ke3chang
Kimsuky
LAPSUS$
Leviathan
OilRig
Play
Sandworm Team
Scattered Spider
TeamTNT
Threat Group-3390
Volt Typhoon
Wizard Spider

Default Configuration

This detection is configured by default in Splunk Enterprise Security to run with the following settings:

Setting Value
Disabled true
Cron Schedule 0 * * * *
Earliest Time -70m@m
Latest Time -10m@m
Schedule Window auto
Creates Risk Event False
This configuration file applies to all detections of type hunting.

Implementation

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.

Associated Analytic Story

Risk Based Analytics (RBA)

Risk Message Risk Score Impact Confidence
Hunting for Log4Shell exploitation has occurred. 40 80 50
The Risk Score is calculated by the following formula: Risk Score = (Impact * Confidence/100). Initial Confidence and Impact is set by the analytic author.

References

Detection Testing

Test Type Status Dataset Source Sourcetype
Validation Passing N/A N/A N/A
Unit Passing Dataset /var/log/nginx/access.log nginx:plus:kv
Integration ✅ Passing Dataset /var/log/nginx/access.log nginx:plus:kv

Replay any dataset to Splunk Enterprise by using our replay.py tool or the UI. Alternatively you can replay a dataset into a Splunk Attack Range


Source: GitHub | Version: 4