Java-JNDI注入
最后更新时间:
文章总字数:
预计阅读时间:
Java-JNDI注入
简要概括:jndi注入通过访问RMI服务或LADP服务来利用动态类加载完成攻击。
JNDI基本概念
jndi的全称为Java Naming and Directory Interface(java命名和目录接口)SUN公司提供的一种标准的Java命名系统接口,JNDI提供统一的客户端API,通过不同的服务供应接口(SPI)的实现,由管理者将JNDI API映射为特定的命名服务和目录系统,使得Java应用程序可以和这些命名服务和目录服务之间进行交互。
简单来说就是jndi提供了统一的接口来方便地访问各种命名服务和目录系统
命名服务
命名服务就是一种简单的键值对绑定,可以通过键名来检索值,RMI就是一种命名服务。
目录服务
目录服务是命名服务的拓展,它与命名服务的区别就是它可以通过对象属性来检索对象,这种层级关系很像目录关系(在磁盘里找文件),LDAP就是一种目录服务。
其实,仔细一琢磨就会感觉其实命名服务与目录服务的本质是一样的,都是通过键来查找对象,只不过目录服务的键要灵活且复杂一点。
jndi是对各种访问目录服务的逻辑进行了再封装,也就是以前我们访问rmi与ldap要写的代码差别很大,但是有了jndi这一层,我们就可以用jndi的方式来轻松访问rmi或者ldap服务,这样访问不同的服务的代码实现基本是一样的。
JNDI代码实现
服务器端与实现RMI并无差别,一样写接口,实现接口,绑定到rmi注册表
而用户端就要实现JNDI工厂,并配置url和端口。
1 | public class RMIclient { |
JNDI注入攻击
JNDI动态协议转换
上面的客户端不仅配置了jndi的初始化,还配置了Context.PROVIDER_URL,这个属性指定了应该去哪里加载本地没有的类,所以,上面的客户端代码中ctk.lookup("rmi://localhost:1099/hello")
这一处代码改为ctk.lookup("hello")
也是没啥问题的。
但是,由于动态协议转换,即时提前配置了Context.PROVIDER_URL属性,当我们调用lookup()方法时,如果lookup方法的参数像上述代码中那样是一个uri地址,那么客户端就会去lookup()方法参数指定的uri中加载远程对象,而不是去Context.PROVIDER_URL设置的地址去加载对象。
正是因为有这个特性,才导致当lookup()方法的参数可控时,攻击者可以通过提供一个恶意的url地址来控制受害者加载攻击者指定的恶意类。
但是仅仅加载一个恶意的类并不能直接调用方法,需要让类初始化来调用静态代码块,这就需要借助JNDI Naming Reference。
JNDI Naming Reference
Reference类表示对存在于命名/目录系统以外的对象的引用。如果远程获取 RMI 服务上的对象为 Reference 类或者其子类,则在客户端获取到远程对象存根实例时,可以从其他服务器上加载 class 文件来进行实例化。
Java为了将Object对象存储在Naming或Directory服务下,提供了Naming Reference功能,对象可以通过绑定Reference存储在Naming或Directory服务下,比如RMI、LDAP等。
在使用Reference时,我们可以直接将对象传入构造方法中,当被调用时,对象的方法就会被触发,从而实现动态类加载运行代码,创建Reference实例时三个比较关键的属性:
className:远程加载时所使用的类名;
classFactory:加载的class中需要实例化类的名称;
classFactoryLocation:远程加载类的地址,提供classes数据的地址可以是file/ftp/http等协议;
服务端创建Reference实例并进行远程绑定到注册中心:
1 | String url = "http://127.0.0.1:8080/"; |
这个url是用来远程加载类的,提供恶意class数据。
JNDI注入
原理其实就是把恶意的Reference
类,绑定在RMI的Registry 里面,在客户端调用lookup
远程获取远程类的时候,就会获取到Reference
对象,获取到Reference
对象后,会去寻找Reference
中指定的类,如果查找不到则会在Reference
中指定的远程地址去进行请求,请求到远程的类后会在本地进行执行。
按上面的代码来说就是获取到Reference对象reference后,寻找指定的test类,找不到就会去指定url中寻找,然后实例化在url中找到的类,进而导致代码执行。
客户端通过jndi来lookup:
1 | String url = "rmi://localhost:1099/obj"; |
参考链接: