Analyze a simulated attack involving JSP web shells and Java-based HTTP tunneling. Investigate network traffic, logs, and web artifacts to uncover the attacker’s techniques and develop effective detection strategies.
An attacker exploited a deserialization flaw in an outdated PrimeFaces application by sending an encrypted payload through a dynamic-content resource request. From the internal network they then opened an authenticated MSSQL session and used a SQL Server shell-out feature to run discovery commands, which exposed the Tomcat service. A malicious WAR deployed through the Tomcat manager carried several JSP web shells (a command runner, a file browser, an HTTP tunneler and an uploader). Through the file browser the attacker read a script holding administrator credentials, pulled down a PowerShell backdoor script from their own web server, and used it to reach a second host, where they copied sensitive files into a web-served directory and uploaded a page for persistent access.
tshark or Wireshark. This is a different network from the other two capturesNote: Q1 to Q4 live in
Web.pcaponly. Q5 to Q19 live inWin-10.pcapngandDC.pcap; do not look for the PrimeFaces host in them.
Start with the TCP conversations and HTTP requests in Web.pcap. The only interesting traffic is a run of POSTs to /javax.faces.resource/dynamiccontent.properties.xhtml carrying a pfdrt field next to a long encrypted blob. That resource path and parameter pair is the PrimeFaces 5.x dynamic-content handler, a well-known deserialization RCE with a CVE assigned in 2017.
tshark -r evidence/Web.pcap -q -z conv,tcp
tshark -r evidence/Web.pcap -Y 'http.request.method==POST' -T fields -e frame.number -e http.request.uri -e http.file_data
Follow the exploit POST from Q1 (frame 370) and read the form body. pfdrt only selects the handler type; the encrypted, Base64-encoded exploit rides in the other field.
tshark -r evidence/Web.pcap -Y 'tcp.stream==X' -z follow,tcp,ascii
This one is reference knowledge, not packet data. The Q2 request sets pfdrt=sc, and the Minded Security write-up of this PrimeFaces flaw (https://blog.mindedsecurity.com/2016/02/rce-in-oracle-netbeans-opensource.html) maps that sc value to a specific handler class.
Gotcha: The
javax.faces.resourcepath suggests the generic resource handler, but thepfdrtvalue picks a different, narrower one.
Same source as Q3 (https://blog.mindedsecurity.com/2016/02/rce-in-oracle-netbeans-opensource.html). Its detection guidance says to flag requests where the parameter is Base64 encoded and longer than a fixed number of bytes, because legitimate values are short identifiers.
Gotcha: The threshold is much smaller than a round number like 50. Read it from the advisory rather than guessing.
Switch to Win-10.pcapng and list its TCP conversations. A login from 10.20.14.52 to the SQL Server default port stands out, with a long-lived stream carrying the attacker’s queries.
tshark -r evidence/Win-10.pcapng -q -z conv,tcp
From Q5 the application is SQL Server. Pull the payload strings from the 1433 traffic: the attacker used the extended stored procedure that shells out to the OS, and frame 4444 carries a process listing request wrapped in it.
tshark -r evidence/Win-10.pcapng -Y 'tcp.port==1433' -T fields -e frame.number -e tcp.payload | head
This was reasoned, not read from an artifact. Commands run through the Q6 feature execute as children of the SQL Server service process, so a shell spawned by that service is the signal. The capture shows the query but contains no process-creation telemetry.
The Q6 result comes back on the same 1433 stream as UTF-16 text. Decode the payloads and search for the Tomcat service image; its PID is in the second column of the tasklist row.
tshark -r evidence/Win-10.pcapng -Y 'tcp.port==1433' -T fields -e tcp.payload | xxd -r -p | strings -e l | grep -i tomcat
With Tomcat identified, look at HTTP on port 8080 to the victim. A POST to the manager’s /manager/html/upload deploys an archive, and the multipart body names it. The archive was carved out of the stream for the shell analysis in Q16 to Q19.
tshark -r evidence/DC.pcap -Y 'tcp.port==8080 and http.request'
Extract the WAR from the upload stream in Win-10.pcapng (stream 1179) by splitting the raw bytes at the ZIP header, then list and read the JSPs. one-lin.jsp is the command runner: it passes a request parameter into the Java call that launches OS processes.
unzip -l work/war/wsexample.war
Tip:
tsharkfollow output was unreliable on the large upload stream. Carving the raw bytes with a short Python script was faster.
The tunneler in the WAR is a reGeorg-style JSP (_example.jsp). It opens raw TCP connections on behalf of the client, and the NIO class it builds them with is the giveaway.
unzip -p work/war/wsexample.war _example.jsp | grep -n 'Channel'
The file browser is id_win.jsp. Near the top (line 37) it defines a boolean toggle that enables its upload feature.
unzip -p work/war/wsexample.war id_win.jsp | sed -n '30,45p'
The uploader is up-base.jsp. It decodes the posted content and writes it out with a stream class.
unzip -p work/war/wsexample.war up-base.jsp | grep -n 'Stream'
Requests to the Q18 file browser carry the target path in a file parameter. One GET to id_win.jsp points at a script under C:\script\ that holds the administrator’s credentials.
tshark -r evidence/DC.pcap -Y 'http.request and ip.src==10.20.14.52' -T fields -e http.request.uri
The attacker’s commands arrive as -enc PowerShell. Decoding the Base64 gives an IEX download cradle pointing at the attacker’s web server on 10.20.14.52:8080, which names the script.
tshark -r evidence/DC.pcap -Y 'http.request and ip.src==10.20.14.52' -T fields -e http.request.uri
This was reasoned from the web shell source rather than seen in a process log. The Q16 command runner launches the Windows command interpreter with the request parameter, so every command the attacker ran through Tomcat (PID from Q8) would appear as that interpreter under the Tomcat process. No process-creation telemetry is in the evidence to confirm it directly.
In DC.pcap, HTTP requests from the attacker go to 10.20.14.51 on a high port rather than 80. That is the listener started by the Q11 script.
tshark -r evidence/DC.pcap -q -z conv,tcp
Follow the Q13 backdoor streams. The commands sent to it include copy operations, and the destination of each is the IIS document root, so the files could be fetched over HTTP.
tshark -r evidence/DC.pcap -Y 'tcp.port==65512' -z follow,tcp,ascii,0
Tip: The answer mask ends with a trailing backslash, so keep it.
The backdoor also takes uploads. A POST to /upload on port 65512 carries a multipart filename, and the file lands in the web root from Q14 as a server-side page.
tshark -r evidence/DC.pcap -Y 'http.request.method==POST and tcp.port==65512' -T fields -e http.file_data
| Phase | Action |
|---|---|
| Initial Access | Encrypted payload sent in pfdrid to the PrimeFaces dynamic-content handler (CVE-2017-1000486) |
| Execution | Authenticated MSSQL session on 1433 from 10.20.14.52 |
| Discovery | xp_cmdshell runs a verbose task list; Tomcat service PID read from the output |
| Persistence | WAR deployed through the Tomcat manager, planting command, file browser, tunneler and uploader JSPs |
| Credential Access | File browser reads connect.ps1 for administrator credentials |
| Lateral Movement | Encoded PowerShell downloads webserver.ps1 and starts a backdoor on port 65512 of a second host |
| Collection | Sensitive files copied into the IIS web root |
| Command and Control | iisstart.aspx uploaded through the backdoor for persistent access |
| Type | Value |
|---|---|
| IP (attacker, MSSQL and script host) | 10.20.14[.]52 |
| IP (backdoor host) | 10.20.14[.]51 |
| Port (backdoor) | 65512 |
| File (WAR) | wsexample.war |
| File (web shells) | one-lin.jsp, id_win.jsp, _example.jsp, up-base.jsp |
| File (script) | webserver.ps1 |
| File (persistence) | iisstart.aspx |
| URL | hxxp[://]10[.]20[.]14[.]52:8080/webserver.ps1 |
| Path | C:\script\connect.ps1, C:\inetpub\wwwroot\ |
| Technique | ID | Description |
|---|---|---|
| Exploit Public-Facing Application | T1190 | PrimeFaces dynamic-content deserialization (CVE-2017-1000486) |
| Windows Command Shell | T1059.003 | xp_cmdshell and the JSP runner spawn cmd.exe |
| Process Discovery | T1057 | tasklist /v to find the Tomcat PID |
| Server Software Component: Web Shell | T1505.003 | JSP shells delivered in wsexample.war |
| Unsecured Credentials: Credentials In Files | T1552.001 | connect.ps1 read through the file browser |
| Ingress Tool Transfer | T1105 | webserver.ps1 pulled from the attacker’s server |
| PowerShell | T1059.001 | Encoded download cradle and the HTTP backdoor |
| Local Data Staging | T1074.001 | Sensitive files copied into the IIS web root |
| Protocol Tunneling | T1572 | reGeorg-style JSP tunneler in the WAR |
Patch or filter PrimeFaces dynamic-content requests. The exploit rode a pfdrid value that was Base64 encoded and longer than a legitimate identifier. A WAF rule on that parameter’s length and encoding would have blocked the first request.
Treat xp_cmdshell as a detection primitive. The discovery command ran through SQL Server, so a cmd.exe child of sqlservr.exe is the high-fidelity alert. Disabling xp_cmdshell removes the capability altogether.
Lock down the Tomcat manager. A WAR upload through /manager/html/upload handed the attacker four web shells at once. Restrict the manager to an admin network, and alert on new WARs or on cmd.exe children of the Tomcat process.
Scan deployed web content for web shell fingerprints. Runtime.getRuntime().exec, SocketChannel for tunnelers, FileOutputStream in uploaders and an ALLOW_UPLOAD = true flag in file browsers are cheap string checks that cover the whole toolkit.
Keep credentials out of scripts. The administrator’s credentials sat in a readable script and unlocked the next host. A secrets store and least-privilege service accounts would have broken the lateral move.
A single unpatched PrimeFaces endpoint led to SQL Server command execution, a Tomcat web shell toolkit, and an administrator-credentialed PowerShell backdoor on a second host. Each stage left a clear, string-matchable fingerprint, so the detection value was in watching the process tree and deployed web content, not the network edge.