Just like Security Obscurity did, I wanted to make an experiment with how difficult it would be to make a known java 0-day bypass antivirus protection.
_This is not an in-depth look at AV by a malware specialist - I have no experience with packers and malware obfuscators. For sure, there are heavy tools for obfuscating java code which I’m sure could make AV bypass a breeze. _
To start off,I used the payload in metasploit (java_jre17_jmxbean), decompiled the class files and configured an Eclipse-project which could generate the applet.
I also started a Windows VM with unpatched java, to check that my exploit still worked, and started doing some modifications.
First off, having only removed the metasploit-specific payload, and instead using only
calc.exe to show pwnage,
Virustotal detection ratio: 18 / 46
Instead of renaming variables, I reached for Proguard , using the configuration below in my build-file. (yes, I still use ant)
Virustotal detection ratio: 14 / 46
Some progress. There are a few more easy things to do, first of all this chunk:
This is a piece of which disables the security manager. It is a compiled version which, decompiled, looks something like this:
It is used very late in the game, and is loaded using a classloader which the previous parts
of the exploit have managed to conjure. This means that the
.class-file does not actually have to be present, as long as the resource can be loaded somehow. The original exploit contained the
file as a hex string.
Let’s replace the hex string! I wrote a little helper to take the B.class and output into a java byte array instead:
Virustotal results : 12 / 46
Still only down 2/3 from our original! Nice work, AV. Let’s get serious!
Next, I just replace all strings (6), using a helper tool to generate
byte arrays instead:
Results: 11 / 46, hardly progress at all.
A shot from the hip - will it make any difference if I insert some junk statements into the codebase?
Results : 10 / 46. Apparently, it works.
Now, let’s make some structural changes,pro_exploit6.jar, inlining some calls and duplicating others to remove local variables.
[Results] (https://www.virustotal.com/en/file/9dd473c304bddd6625075822d05f4ba8f02fcac2627348b61de541f695dd2fdf/analysis/1362698236/): 8 / 46
Right, finally more than half of them bypassed. The remaining contenders are :
Interesting thing to note at this stage - what if we remove proguard obfuscation but retain other fixes : 9/46.
Fortinet does not like proguard, but finds the vulnerability without obfuscation on:
Since we don’t have access to a classloader, we cannot use reflection as in Security Obscurity did, thus some calls that we make (e.g. MethodHandles) will be visible in the class file. What we can do, however, is spread it out across several classes and obscure the types in the ‘main’ class.
Instead of doing this call:
We can define a new abstract class:
And then make an inline class which subclasses the
This should make the program flow a lot more difficult to analyse, since each .class-file should only contain small parts of the exploit, therefore hopfully slipping under the radar of AV fingerprinting. Classes defined inline wind up in separate class files :
After externalizing all actual malicious stuff, the main applet class is a very benign creature which only performs these kinds of operations:
After this is performed for all calls, we’re down to down to 2/45! Results.
Interesting to note, without proguard-obfuscation, we get different Results : 6 / 45 .
Some vendors obviously get fooled by Proguard, others (DrWeb) benefit from it. Proguard is a double-edged sword; it simplifies application flow, thus in a sense canonicalizing it, which may conflict with the obfuscation of program flow that we are doing.
At this point I noticed an oversight; the B.class, containing a payload which I had already moved over to a byte-array back in step 1, was needlessly included in the .jar-file. DOH! Removing that obvious giveaway
Proguard-version Results 2/45:
Well, we already know that DrWeb will drop off if we don’t use proguard… Non-proguard-version [Results] (https://www.virustotal.com/en/file/ad2e17976676963469b0091afd1d11e73ca1fcf1d2029d59b5e8b5560a8cfdb8/analysis/1363354324/): 1/45 :
So, the winner of this little game is…
Oh, does it still pop?
Of course, I can’t leave it like that. Need to go deeper. So, the first thing to do is to check exactly which class it is that Kaspersky triggers on. Above, I wound up with one main class – the Applet, one private class and 16 inline classes:
To check which one triggers detection, I uploaded them all to VT and got…
Not a single AV trigger
So, no single class file triggered AV reactions - but Kaspersky still triggers on a jar-file containing them all. Obviously, Kaspersky is doing something right.
However, after having gone through the trouble of spreading out the individual parts of the malicious program into separate classes, which by themselves are not enough to trigger any AV suspicions, there is one more trick that can be used. It has to do with the behaviour of the Applet class loader.
From Securing Java:
If the class is not found by the Primordial Class Loader, the Applet Class Loader typically loads it via HTTP using methods of the URL class. Code is fetched from the
CODEBASEspecified in the
What this means for an attacker is that I only need to include the main Applet class. All remaining classes can be deferred for later, and served directly from the web server as needed. That is, when the runtime notices that a class is missing. Indeed - there’s no need for a jar-file at all, I can just specify the applet class directly.
Another interesting aspect that is that when the actual jar-file (and, later, the classe) are loaded, it is not the browser that does the fetching: it is the Java VM, using the URL class as specified above. This means that an attacker does not have to use any magic to determine the java version; the UA-string used by Java will let us know.
User-Agent: Mozilla/4.0 (Windows 7 6.1) Java/1.7.0_05
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)
Anyway, in essence what we’ve ended up with is features pretty close to something out of a dynamic language:
Unfortunately, I don’t actually have Kaspersky to test with, so I can’t be 100% sure that it works. However, given that no individual
-file is flagged as malicious, I don’t see how an AV could possibly stop this kind of attack, since it would require the AV to keep historic record in a way that I don’t believe it does.
As I said in the beginning, this is nothing new - anyone in the security industry already knows that signature-based AV is a very blunt tool which provides a very limited degree of protection.
A non-signature-based AV, such as FireEye, would definitely catch this, since FireEye is basically a VM which executes the code in the same way the target would.