Java反序列化:CommonsCollections3调试分析

基础知识

1. Java 反射

1.1 getConstructor

getConstructor 是Java反射API中的一个方法,用于获取类的公共构造方法的引用。构造方法是一种特殊的方法,用于创建类的实例(对象),并且通常在对象创建时进行初始化。

getConstructor的函数原型:

public Constructor<?> getConstructor(Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException
  • parameterTypes:一个可变参数,用于指定要获取的构造方法的参数类型列表。
  • NoSuchMethodException:如果没有找到匹配指定参数类型的公共构造方法,则会抛出此异常。
  • SecurityException:如果存在安全管理器且其 checkMemberAccess 方法拒绝访问构造方法,则会抛出此异常。

1.2 newInstance

newInstance是Java反射API中的一个方法,用于创建类的实例并返回该实例的引用。

newInstance的函数原型:

public T newInstance() throws InstantiationException, IllegalAccessException
  • T表示要创建的类型

2. InstantiateTransformer

InstantiateTransformer 是Apache Commons Collections库中的一个类,用于创建对象的转换器。具体来说,它用于通过类名实例化一个新对象,并将其作为转换结果返回。

看看源码:

public class InstantiateTransformer<T> implements Transformer<Class<? extends T>, T>, Serializable {
    private static final long serialVersionUID = 3786388740793356347L;
    private static final Transformer NO_ARG_INSTANCE = new InstantiateTransformer();
    private final Class<?>[] iParamTypes;
    private final Object[] iArgs;

    public static <T> Transformer<Class<? extends T>, T> instantiateTransformer() {
        return NO_ARG_INSTANCE;
    }

    public static <T> Transformer<Class<? extends T>, T> instantiateTransformer(Class<?>[] paramTypes, Object[] args) {
        if (paramTypes == null && args != null || paramTypes != null && args == null || paramTypes != null && args != null && paramTypes.length != args.length) {
            throw new IllegalArgumentException("Parameter types must match the arguments");
        } else {
            return paramTypes != null && paramTypes.length != 0 ? new InstantiateTransformer(paramTypes, args) : new InstantiateTransformer();
        }
    }

    private InstantiateTransformer() {
        this.iParamTypes = null;
        this.iArgs = null;
    }

    public InstantiateTransformer(Class<?>[] paramTypes, Object[] args) {
        this.iParamTypes = paramTypes != null ? (Class[])paramTypes.clone() : null;
        this.iArgs = args != null ? (Object[])args.clone() : null;
    }

    public T transform(Class<? extends T> input) {			
        try {
            if (input == null) {
                throw new FunctorException("InstantiateTransformer: Input object was not an instanceof Class, it was a null object");
            } else {
                Constructor<? extends T> con = input.getConstructor(this.iParamTypes);	// 获取公开构造器
                return con.newInstance(this.iArgs);				// 创建对象实例
            }
        } catch (NoSuchMethodException var3) {
            throw new FunctorException("InstantiateTransformer: The constructor must exist and be public ");
        } catch (InstantiationException var4) {
            throw new FunctorException("InstantiateTransformer: InstantiationException", var4);
        } catch (IllegalAccessException var5) {
            throw new FunctorException("InstantiateTransformer: Constructor must be public", var5);
        } catch (InvocationTargetException var6) {
            throw new FunctorException("InstantiateTransformer: Constructor threw an exception", var6);
        }
    }
}

3. TrAXFilter

在其构造方法中具有

public class TrAXFilter extends XMLFilterImpl {
    private Templates              _templates;
    private TransformerImpl        _transformer;
    private TransformerHandlerImpl _transformerHandler;
    private boolean _useServicesMechanism = true;

    public TrAXFilter(Templates templates)  throws
        TransformerConfigurationException
    {
        _templates = templates;
        _transformer = (TransformerImpl) templates.newTransformer();
        _transformerHandler = new TransformerHandlerImpl(_transformer);
        _useServicesMechanism = _transformer.useServicesMechnism();
    }

    public Transformer getTransformer() {
        return _transformer;
    }

    private void createParent() throws SAXException {
        XMLReader parent = null;
        try {
            SAXParserFactory pfactory = SAXParserFactory.newInstance();
            pfactory.setNamespaceAware(true);

            if (_transformer.isSecureProcessing()) {
                try {
                    pfactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
                }
                catch (SAXException e) {}
            }

            SAXParser saxparser = pfactory.newSAXParser();
            parent = saxparser.getXMLReader();
        }
        catch (ParserConfigurationException e) {
            throw new SAXException(e);
        }
        catch (FactoryConfigurationError e) {
            throw new SAXException(e.toString());
        }

        if (parent == null) {
            parent = XMLReaderFactory.createXMLReader();
        }

        // make this XMLReader the parent of this filter
        setParent(parent);
    }

    public void parse (InputSource input) throws SAXException, IOException
    {
        XMLReader managedReader = null;

        try {
            if (getParent() == null) {
                try {
                    managedReader = XMLReaderManager.getInstance(_useServicesMechanism)
                                                    .getXMLReader();
                    setParent(managedReader);
                } catch (SAXException  e) {
                    throw new SAXException(e.toString());
                }
            }

            // call parse on the parent
            getParent().parse(input);
        } finally {
            if (managedReader != null) {
                XMLReaderManager.getInstance(_useServicesMechanism).releaseXMLReader(managedReader);
            }
        }
    }

    public void parse (String systemId) throws SAXException, IOException
    {
        parse(new InputSource(systemId));
    }

    public void setContentHandler (ContentHandler handler)
    {
        _transformerHandler.setResult(new SAXResult(handler));
        if (getParent() == null) {
                try {
                    createParent();
                }
                catch (SAXException  e) {
                   return;
                }
        }
        getParent().setContentHandler(_transformerHandler);
    }

    public void setErrorListener (ErrorListener handler) { }
}

4. TemplatesImpl动态加载字节码

  • Templates接口

  • TemplatesImpl动态加载字节码过程

/*
	TemplatesImpl.newTransformer()
		TemplatesImpl.getTransletInstance()					
			TemplatesImpl.defineTransletClasses()			
				ClassLoader.defineClass()		=> 加载字节码
*/

5.AbstractMapDecorator类

这个类是java.util.Map接口的装饰器(Decorator),它允许你通过包装另一个Map实现来添加额外的功能或修改Map的行为,而无需修改原始Map实现的代码。

public abstract class AbstractMapDecorator implements Map {
    protected transient Map map;

    protected AbstractMapDecorator() {
    }

    public AbstractMapDecorator(Map map) {
        if (map == null) {
            throw new IllegalArgumentException("Map must not be null");
        } else {
            this.map = map;
        }
    }

    protected Map getMap() {
        return this.map;
    }

    public void clear() {
        this.map.clear();
    }

    public boolean containsKey(Object key) {
        return this.map.containsKey(key);
    }

    public boolean containsValue(Object value) {
        return this.map.containsValue(value);
    }

    public Set entrySet() {
        return this.map.entrySet();
    }

    public Object get(Object key) {
        return this.map.get(key);
    }

    public boolean isEmpty() {
        return this.map.isEmpty();
    }

    public Set keySet() {
        return this.map.keySet();
    }

    public Object put(Object key, Object value) {
        return this.map.put(key, value);
    }

    public void putAll(Map mapToCopy) {
        this.map.putAll(mapToCopy);
    }

    public Object remove(Object key) {
        return this.map.remove(key);
    }

    public int size() {
        return this.map.size();
    }

    public Collection values() {
        return this.map.values();
    }

    public boolean equals(Object object) {
        return object == this ? true : this.map.equals(object);
    }

    public int hashCode() {
        return this.map.hashCode();
    }

    public String toString() {
        return this.map.toString();
    }
}

调试分析

基本过程和CC1链一致,主要是将最后的恶意代码触发由InvokerTransformer改装成InstantiateTransformer,然后利用TrAXFilter来调用TemplatesImpl来加载Java字节码

Gadget Chain

/* Gadget Chaain*/
ObjectInputStream.readObject()
	AnnotationInvocationHandler.readObject()
    	LazyMap.readObject()
    		Map(Proxy).entrySet()
    			LazyMap.get()
    				InstantiateTransformer.transform()
    					TrAXFilter.TrAXFilter()
    						TemplatesImpl.newTransformer()
    							TemplatesImpl.getTransletInstance()

调试过程

  • 看调用栈,几乎就是CC1那条路,不赘述了吧

image-20230912211645657

posted @ 2023-09-12 21:40  Icfh  阅读(30)  评论(0编辑  收藏  举报