安全矩阵

 找回密码
 立即注册
搜索
查看: 2224|回复: 0

CC gadget

[复制链接]

260

主题

275

帖子

1065

积分

金牌会员

Rank: 6Rank: 6

积分
1065
发表于 2022-4-14 11:08:29 | 显示全部楼层 |阅读模式
本帖最后由 luozhenni 于 2022-4-14 11:05 编辑

CC gadget

原文链接:https://phisabella.github.io/2022/02/22/CC-gadget.html

CC链利用点


反序列化理解


说起Java RCE,第一反应就是要构造并执行Runtime.getRuntime().exec("calc")这句代码(或者别的RCE点如7u21),而在Java反序列化中会自动触发readObject()方法,也就是说,如果要利用反序列化达到RCE的效果,就需要找到某个修改了readObject()的类,并且修改内容里有能构造(通过一系列调用)Runtime.getRuntime().exec("calc")的方法。
CC链利用总结

分类

调试完七条CC链后,能够发现其实7条CC链使用了两种思路:

1.[Chained-->Constant-->Invoker]Transformer相互组合构造RCE

2.通过TemplatesImpl利用7u21 jdk本身的RCE
当然从CC链挖掘利用的顺序来讲24是一组,13是一组,567在是CC链在1.8中的适配和改进

CC1/CC5/CC6/CC7
CC1中用了LazyMap的get()函数来调三个Transformer,再用重写了了readObject的AnnotationInvocationHandler来调get()
因为AnnotationInvocationHandler被ban,因此替换为BadAttributeValueExpException,即CC5
随后又衍生出CC6(HashSet)和CC7(Hashtable)增加CC链的适用性

CC2/CC3/CC4/CB1
CC2没用三个Transformer的组合,用的是7u21RCE,为此引入了TemplatesImpl,为了调用TemplatesImpl的任意方法(newTransformer)使用了InvokerTransformer来反射执行,TransformingComparator的compare来调transform,使用PriorityQueue来调compare

CC3将CC1和CC2结合,用InstantiateTransformer代替InvokerTransformer,调transform前和CC1一样,后面和CC2一样

CC4将CC2和CC3结合,用ConstantTransformer代替TransformingComparator来调transform,其他和CC2一样

题外话:CB1链其实和CC2有些相似,都是PriorityQueue为入口,TemplatesImpl来执行,区别在于中间调用部分,CC2用的是TransformingComparator(InvokerTransformer),而CB1用了BeanComparator;CC2在siftDownUsingComparator 调的是InvokerTransformer的compare,而CB1调的是BeanComparator的)

四个Transformer

InvokerTransformer和InstantiateTransformer在CC链的地位类似,都是负责最后一步的RCE,
不过InstantiateTransformer在CC3和CC4中配合TemplatesImpl实例化造成7u21 RCE,
InvokerTransformer则在CC3其余5条链中使用,CC2中配合TemplatesImpl,1567配合ConstantTransformer和ChainedTransformer
onstantTransformer和ChainedTransformer
InvokerTransformerConstantTransformerChainedTransformerInstantiateTransformer
构造函数接受三个参数构造函数接受一个参数构造函数接受一个TransFormer类型的数组构造函数接受两个参数
transform方法通过反射可以执行一个对象的任意方法transform返回构造函数传入的参数transform方法执行构造函数传入数组的每一个成员的transform方法transform通过反射的方法返回传入参数input的实例


五个反序列化入口类
               
AnnotationInvocationHandlerPriorityQueueBadAttributeValueExpExceptionHashSetHashtable
调lazyMap(CC1/CC3)调TransformingComparator(CC2/CC4)调TiedMapEntry(CC5)调HashMap(CC6)调lazyMap(CC7)
反序列化的时候会循环调用成员变量的get方法反序列化的时候会调用TransformingComparator中的transformer的tranform方法反序列化的时候会去调用成员变量val的toString函数(TiedMapEntry的toString函数会再去调自身的getValue)反序列化的时候会去循环调用自身map中的put方法当里面包含2个及以上的map的时候,循环调用map的get方法
三个Map


lazyMapTiedMapEntryHashMap
CC1/CC3/CC5/CC6/CC7CC2CC6
通过调用lazyMap的get方法可以触发它的成员变量factory的tranform方法通过调用TiedMapEntry的getValue方法实现对他的成员变量map的get方法的调用通过调用HashMap的put方法实现对成员变量hashCode方法的调用(TiedMapEntry的hashCode函数会再去调自身的getValue)
CC链典型RCE点

CC链中最典型的构造组合是,[Invoker、Constant、Chained]Transformer相互组合构造Runtime.getRuntime().exec("calc"),它们都实现了TransFormer这个接口,都有一个transform方法,ConstantTransformer可以执行Runtime,InvokerTransformer可以凑出后续的getRuntime().exec("calc")部分,在ChainedTransformer的transform方法里可以将上述两者拼接成一条完整的RCE命令。

典型代码如下:
  1. Transformer[] transformers_exec = new Transformer[]{
  2.             #先实例化
  3.         new ConstantTransformer(Runtime.class),
  4.         new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),
  5.         new InvokerTransformer("invoke",new Class[]{Object.class, Object[].class},new Object[]{null,null}),
  6.         new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
  7. };
  8. Transformer chain = new ChainedTransformer(transformers_exec);
  9. #调用transform方法
  10. chain.transform("qwe");
复制代码



下面逐个介绍

ConstantTransformer除了CC2都用到了


transform方法会返回构造函数的参数iConstant。因此实例化时传入Runtime.class,也会返回一样的内容。只需要调用transform方法。

InvokerTransformer

12567


` transform用到了反射,执行了某个对象的某个方法,并且相关参数也是构造函数里的可控参数,因此也只需要调用transform`方法即可。

ChainedTransformer

除了CC2以外都用到了

正好,ChainedTransformer 的transform函数会遍历构造函数中的Transformer数组,并调用数组中的每一个成员的transform方法,并且上一个成员调用返回的对象会作为下一个成员transform的参数,也就是说,只需要调用ChainedTransformer 的transform方法即可构造出Runtime.getRuntime().exec("calc")
那么问题来了,怎么触发ChainedTransformer的transform方法?

InstantiateTransformer

在CC3和CC4中使用

CC链调试——触发transform

如果用一个词来总结CC链,那一定是“transform”,不管怎么变化,用什么类来改写还是调用,最终都是要想办法调用四个transform里的某一个某几个transform的transform()函数。

TransformedMap

虽然没在CC链里出现,还是可以看一看找利用链的思路是什么样的
注:因为1.8删去了关键的setValue方法,因此只能在1.7下使用
思路
从挖洞的思路来讲此时应该搜索transform(关键字来寻找有相关调用的类,但既然是复现就直接跳过这一步,找到TransformedMap类,

TransformedMap类里有三个transform调用,keyTransformer 和valurTransformer都是Transformer类型且可控,所以可以将其值赋为ChainedTransformer,但是这三个方法都是protected不能直接调用,找到4个public方法,其中有一个put()方法,调用了transformKey以及transformValue,这两个方法又都调用了transform方法,所以TransformedMap类可以满足需求

只需要实例化一个TransforomedMap对象,然后调用对象的put方法,就可以RCE。
但是并不能在反序列化中触发,因为没有readObject()类,还需要一个重写readObject()类的方法,并能调用上述的方法。
一般来讲会采取向上回溯找方法(transformKey、transformValue、checkSetValue)调用位置,或者全局搜索readObject()看看有没有哪个类直接就调用了这三个方法中的一个或者readObject中有可疑的操作,最后能够间接触发这几个方法。
从名字就能推断TransformedMap其实是Map类型,范围就可以扩大到引用了Map的readObject()类,根据摸索git得到TransformedMap里的每个entryset在调用setValue方法时会自动调用

TransformedMap类的checkSetValue方法

因此就变成了找一个对Map类型的属性的entry进行了setValue操作的readObject()类,刚好sun.reflect.annotation.AnnotationInvocationHandler类就可以满足需求(但jdk1.8已经没有了setValue)

但有一个条件,if (!var7.isInstance(var8) && !(var8 instanceof ExceptionProxy))(其实就是var1不能被转化为var2即可)
从截图代码可知,需要知道this.type是什么,

是一个可控的,需要继承Annotation,那就应该是Annotation的子类(所有注解类型的公用接口),于是找到java.lang.annotation.Retention.class这个注解类(map的键必须有”value”),但这个类的访问权限不是public,而是包访问权限,构造poc的时候只有通过反射机制来实例化它,至此构造结束。

调用链
  1. sun.reflect.annotation.AnnotationInvocationHandler(readObject()的setValue) –>
  2. TransformedMap(checkSetValue 调用this.valueTransformer即ChainedTransformer的transform()) –>
  3. ChainedTransformer循环调用数组的transform() –>
  4. ConstantTransformer and InvokerTransformer(RCE)
复制代码

CC1–LazyMap+InvokerTransformer(3.1-3.2.1,jdk<1.8)

CommonsCollections版本主要是针对在ysoserial里的

思路

LazyMap有this.factory.transform(key),而this.factory可控,factory传ChainedTransformer即可

然后找调用get的地方,老朋友AnnotationInvocationHandler的invoke符合条件,this.memberValues也是可控的(上个思路截图),传入LazyMap即可

找触发AnnotationInvocationHandler.invoke()的地方

动态代理

被动态代理的对象调用任意方法都会通过对应的InvocationHandler的invoke方法触发
(可以将InvocationHandler接口类看做一个中介类,中介类持有一个被代理对象即真实对象,在invoke()方法中调用了被代理对象相应的方法。通过聚合方式持有被代理对象的引用,把外部对invoke的调用最终都转为对被代理对象的调用。

代理类调用自己方法时,通过自身持有的中介类对象来调用中介类对象的invoke方法,从而达到代理执行被代理对象的方法。也就是说,动态代理通过中介类实现了具体的代理功能,即无侵入式的代码扩展,不用修改源码)

所以此时只需要创建一个LazyMap的动态代理,动态代理调用某个方法,但是反序列化需要readObject,即找一个类,其readObject方法可以通过动态代理调用LazyMap的某个方法(和直接调用LazyMap某个方法需要满足的条件几乎是一样的,因为某个类的动态代理与它本身实现了同一个接口)
AnnotationInvocationHandler的readObject方法会调用某个Map类型对象的entrySet()方法,而LazyMap以及他的动态代理都是Map类型,可以满足需求


调用链
  1. sun.reflect.annotation.AnnotationInvocationHandler(readObject()的get) –>

  2. LazyMap(this.factory.transform(key),即ChainedTransformer的transform()) –>

  3. ChainedTransformer循环调用数组的transform() –>

  4. ConstantTransformer and InvokerTransformer(RCE)

  5. /*
  6.         Gadget chain:
  7.                 ObjectInputStream.readObject()
  8.                         AnnotationInvocationHandler.readObject()
  9.                                 Map(Proxy).entrySet()
  10.                                         AnnotationInvocationHandler.invoke()
  11.                                                 LazyMap.get()
  12.                                                         ChainedTransformer.transform()
  13.                                                                 ConstantTransformer.transform()
  14.                                                                 InvokerTransformer.transform()
  15.                                                                         Method.invoke()
  16.                                                                                 Class.getMethod()
  17.                                                                 InvokerTransformer.transform()
  18.                                                                         Method.invoke()
  19.                                                                                 Runtime.getRuntime()
  20.                                                                 InvokerTransformer.transform()
  21.                                                                         Method.invoke()
  22.                                                                                 Runtime.exec()

  23.         Requires:
  24.                 commons-collections
  25. */
复制代码

CC2–PriorityQueue+TemplatesImpl(4.0,jdk<=7u21)

有个不同点在于没有构造Runtime,而是使用了7u21的RCE,简言之只要能调用包含恶意字节码的TemplatesImpl对象的利用链中的任意函数(getOutputProperties、newTransformer等等),就能造成RCE

思路

因此思路也有变化,只用到了InvokerTransformer(3.22被拉黑但是4.0又可以用了,然后又被拉黑了)
首先得想办法触发TemplatesImpl的函数,正好InvokerTransformer就可以用反射执行某个对象的任意方法,这里传入newTransformer方法


然后就是想办法调用transform方法,找到了TransformingComparator,

需要找一个调用其compare的类,找到PriorityQueue,其siftDownUsingComparator符合条件,把TransformingComparator放到PriorityQueue里就能调用到PriorityQueue的compare方法

为什么选PriorityQueue,因为其重写了readObject,而且,会调用heapify(),可以一路调用到siftDownUsingComparator,并且可传入任意类型的对象,可以顺利引入TemplatesImpl



至此利用链结束

调用链

PriorityQueue的readObject调用自己的heapify,一直调用到siftDownUsingComparator ,方法内调用compare–>
InvokerTransformer的compare调this.transformer.transform–>
即InvokerTransformer的transform被调用–>
一直被传递的TemplatesImpl的newTransformer被invoke,进入7u21 RCE

  1. /*
  2.         Gadget chain:
  3.                 ObjectInputStream.readObject()
  4.                         PriorityQueue.readObject()
  5.                                 ...
  6.                                         TransformingComparator.compare()
  7.                                                 InvokerTransformer.transform()
  8.                                                         Method.invoke()
  9.                                                                 Runtime.exec()
  10. */
复制代码


CC3–LazyMap+InstantiateTransformer+TemplatesImpl(3.1-3.2.1,jdk<=7u21)

和CC1基本一样,区别在于,RCE的点还是用的CC2的TemplatesImpl,但是反射调用的是InstantiateTransformer而不是InvokerTransformer

还是和CC2一样的,如果要TemplatesImplRCE,需要找newTransformer被调用的地方,InstantiateTransformer的transform可以生成任意类的实例,能够顺利进入RCE利用点,


而InstantiateTransformer自己的transform又可以用CC1的LazyMap来调用ChainedTransformer的transform调用,LazyMap的entrySet方法又被AnnotationInvocationHandler的readObject调用,构造完成。
  1. * Variation on CommonsCollections1 that uses InstantiateTransformer instead of InvokerTransformer
复制代码
CC4–PriorityQueue+InstantiateTransformer+TemplatesImpl(4.0,jdk<=7u21)

改进了CC2,类似CC3和CC2的结合。
PriorityQueue的readObject一路调下去调的是ChainedTransformer然后到InstantiateTransformer而不是InvokerTransformer,其他都是重复的内容

* Variation on CommonsCollections2 that uses InstantiateTransformer instead of InvokerTransformer.
CC5–LazyMap(3.1-3.2.1,jdk8u76)

终于不用1.7了 jdk1.8对老朋友AnnotationInvocationHandler进行了限制,需要新的类

思路
于是找到了BadAttributeValueExpException,其实变化不大,就是调用 LazyMap.get()方法的地方变了,BadAttributeValueExpException(3.2.2被拉黑)的readObject()

有一个toString方法,即TiedMapEntry


然后就调用到get了, LazyMap.get()被调用,后面的就很熟悉了,和CC1一样,还简单一些


调用链

BadAttributeValueExpException的 readObject()–>
TiedMapEntry toString() 的getValue()–>
getValue() 调get() –>
Cha∈edTranormer.tranorm()
–>
ConstantTransformer and InvokerTransformer(RCE)
  1. /*
  2.         Gadget chain:
  3.         ObjectInputStream.readObject()
  4.             BadAttributeValueExpException.readObject()
  5.                 TiedMapEntry.toString()
  6.                     LazyMap.get()
  7.                         ChainedTransformer.transform()
  8.                             ConstantTransformer.transform()
  9.                             InvokerTransformer.transform()
  10.                                 Method.invoke()
  11.                                     Class.getMethod()
  12.                             InvokerTransformer.transform()
  13.                                 Method.invoke()
  14.                                     Runtime.getRuntime()
  15.                             InvokerTransformer.transform()
  16.                                 Method.invoke()
  17.                                     Runtime.exec()

  18.         Requires:
  19.                 commons-collections
  20. */
  21. This only works in JDK 8u76 and WITHOUT a security manager
复制代码

CC6–HashSet+LazyMap(3.1-3.2.1,jdk1.7,1.8)

CC6同样使用的是LazyMap来触发ChainedTransformer的transform,不同的是在触发LazyMap.get()时用了不同的方法

思路
CC6用了HashSet的readObject()



TiedMapEntry对象被带入put函数

在put进入hash函数,

在hashCode函数中会调用恶意的TiedMapEntry对象自身的getValue函数

getValue这里就会调用LazyMap的get函数了





然后就是熟悉的LazyMap调用链了

调用链


HashSet.readObject() 的 put–>
HashMap.put() 和hash() ,调hashCode()–>
TiedMapEntry.hashCode()和getValue(),调this.map.get(this.key) –>
LazyMap(this.factory.transform(key),即ChainedTransformer的transform()) –>
ChainedTransformer循环调用数组的transform() –>
ConstantTransformer and InvokerTransformer(RCE)
  1. /*
  2.         Gadget chain:
  3.             ObjectInputStream.readObject()
  4.             HashSet.readObject()
  5.                HashMap.put()
  6.                HashMap.hash()
  7.                     TiedMapEntry.hashCode()
  8.                     TiedMapEntry.getValue()
  9.                         LazyMap.get()
  10.                             ChainedTransformer.transform()
  11.                             InvokerTransformer.transform()
  12.                                    reflect.Method.invoke()
  13.                                 java.lang.Runtime.exec()
  14. */
复制代码

CC7–Hashtable+LazyMap(3.1-3.2.1,jdk1.7,1.8)
思路
这次是Hashtable的readObject()了

会调用readHashtable,

然后是reconstitutionPut,这里需要满足e.hash == hash ,才能到e.key.equals(key)的equals(),因此需要构造hash相同的两个lazymap强制进行比较

:因为两个lazymap的hash相同,所以hashtable放进第二个lazymap时,会把第一个lazymap的key值"yy"放到第二个lazymap中(首先lazymap.get(‘yy’)尝试从第二个lazymap中拿),此时将导致lazymap2中新添加yy->processImpl键值对,造成第二个lazymap空间大小为2,和第一个不一样,hash不同无法继续,改成lazyMap2.remove("yy")即可



equal判断的时候就会去取值,即调用get(),进入LazyMap调用链


调用链

  1. /*
  2.     Payload method chain:
  3.     java.util.Hashtable.readObject
  4.             java.util.Hashtable.reconstitutionPut
  5.                     org.apache.commons.collections.map.AbstractMapDecorator.equals
  6.                             java.util.AbstractMap.equals
  7.                                             org.apache.commons.collections.map.LazyMap.get
  8.                                                     org.apache.commons.collections.functors.ChainedTransformer.transform
  9.                                              org.apache.commons.collections.functors.InvokerTransformer.transform
  10.                                              java.lang.reflect.Method.invoke
  11.                                                     java.lang.Runtime.exec
  12. */
复制代码

参考javasec/3. apache commons-collections中的反序列化
玩转Ysoserial-CommonsCollection的七种利用方式分析
java反序列化-ysoserial-调试分析总结篇(1) - tr1ple

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2024-4-19 22:43 , Processed in 0.022409 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表