Remote-Codeausführung mit XStream

1. Übersicht

In diesem Tutorial analysieren wir einen Remote Code Execution-Angriff gegen die XStream XML-Serialisierungsbibliothek. Dieser Exploit fällt in die Kategorie der nicht vertrauenswürdigen Deserialisierungsangriffe .

Wir werden erfahren, wann XStream für diesen Angriff anfällig ist, wie der Angriff funktioniert und wie solche Angriffe verhindert werden können.

2. XStream-Grundlagen

Bevor wir den Angriff beschreiben, wollen wir einige XStream-Grundlagen überprüfen. XStream ist eine XML-Serialisierungsbibliothek, die zwischen Java-Typen und XML übersetzt. Betrachten Sie eine einfache Personenklasse :

public class Person { private String first; private String last; // standard getters and setters }

Mal sehen, wie XStream eine Personeninstanz in XML schreiben kann :

XStream xstream = new XStream(); String xml = xstream.toXML(person);

Ebenso kann XStream XML in eine Instanz von Person einlesen :

XStream xstream = new XStream(); xstream.alias("person", Person.class); String xml = "JohnSmith"; Person person = (Person) xstream.fromXML(xml);

In beiden Fällen verwendet XStream Java Reflection, um den Personentyp in und aus XML zu übersetzen . Der Angriff erfolgt beim Lesen von XML. Beim Lesen von XML instanziiert XStream Java-Klassen mithilfe von Reflection.

Die Klassen, die XStream instanziiert, werden durch die Namen der XML-Elemente bestimmt, die analysiert werden.

Da wir XStream so konfiguriert haben, dass der Personentyp bekannt ist , instanziiert XStream eine neue Person, wenn XML-Elemente mit dem Namen "Person" analysiert werden.

Zusätzlich zu benutzerdefinierten Typen wie Person erkennt XStream sofort Java-Kerntypen. Beispielsweise kann XStream eine Karte aus XML lesen :

String xml = "" + "" + " " + " foo" + " 10" + " " + ""; XStream xStream = new XStream(); Map map = (Map) xStream.fromXML(xml); 

Wir werden sehen, wie die Fähigkeit von XStream, XML zu lesen, das die wichtigsten Java-Typen darstellt, beim Exploit der Remotecodeausführung hilfreich ist.

3. Wie der Angriff funktioniert

Remote-Code-Ausführungsangriffe treten auf, wenn Angreifer Eingaben bereitstellen, die letztendlich als Code interpretiert werden. In diesem Fall nutzen Angreifer die Deserialisierungsstrategie von XStream, indem sie Angriffscode als XML bereitstellen.

Mit der richtigen Zusammensetzung der Klassen führt XStream den Angriffscode letztendlich durch Java-Reflektion aus.

Lassen Sie uns einen Beispielangriff erstellen.

3.1. Fügen Sie Angriffscode in einen ProcessBuilder ein

Unser Angriff zielt darauf ab, einen neuen Desktop-Rechner-Prozess zu starten. Unter macOS ist dies "/Applications/Calculator.app". Unter Windows ist dies "calc.exe". Dazu bringen wir XStream dazu, einen neuen Prozess mit einem ProcessBuilder auszuführen . Rufen Sie den Java-Code auf, um einen neuen Prozess zu starten:

new ProcessBuilder().command("executable-name-here").start();

Beim Lesen von XML ruft XStream nur Konstruktoren auf und legt Felder fest. Daher hat der Angreifer keine einfache Möglichkeit, die ProcessBuilder.start () -Methode aufzurufen .

Allerdings clevere Angreifer können die richtige Zusammensetzung von Klassen verwenden , um letztlich das Ausführen Process ‚s start () Methode.

Die Sicherheitsforscherin Dinis Cruz zeigt uns in ihrem Blogbeitrag, wie sie die Vergleichsschnittstelle verwenden, um den Angriffscode im Kopierkonstruktor der sortierten Sammlung TreeSet aufzurufen. Wir werden den Ansatz hier zusammenfassen.

3.2. Erstellen Sie einen vergleichbaren dynamischen Proxy

Denken Sie daran, dass der Angreifer einen ProcessBuilder erstellen und seine start () -Methode aufrufen muss . Um dies zu tun, werden wir eine Instanz erstellen Vergleichbar deren vergleichen Methode ruft die Process ‚s start () Methode.

Glücklicherweise erlauben Java Dynamische Proxies uns eine Instanz erstellen Vergleichbar dynamisch .

Darüber hinaus bietet die EventHandler- Klasse von Java dem Angreifer eine konfigurierbare InvocationHandler- Implementierung. Der Angreifer konfiguriert den Eventhandler die aufzurufen Process ‚s start () Methode.

Wenn wir diese Komponenten zusammenfügen, haben wir eine XStream-XML-Darstellung für den vergleichbaren Proxy:

 java.lang.Comparable    open /Applications/Calculator.app   start  

3.3. Erzwingen Sie einen Vergleich mit dem vergleichbaren dynamischen Proxy

Um einen Vergleich mit unserem vergleichbaren Proxy zu erzwingen , erstellen wir eine sortierte Sammlung. Erstellen wir eine TreeSet- Auflistung, die zwei vergleichbare Instanzen vergleicht : einen String und unseren Proxy.

Wir werden den Kopierkonstruktor von TreeSet verwenden, um diese Sammlung zu erstellen. Schließlich haben wir die XStream-XML-Darstellung für ein neues TreeSet, das unseren Proxy und einen String enthält :

 foo  java.lang.Comparable    open /Applications/Calculator.app   start    

Letztendlich tritt der Angriff auf, wenn XStream dieses XML liest. Während der Entwickler erwartet, dass XStream eine Person liest , führt er stattdessen den Angriff aus:

String sortedSortAttack = // XML from above XStream xstream = new XStream(); Person person = (Person) xstream.fromXML(sortedSortAttack);

3.4. Angriffszusammenfassung

Lassen Sie uns die reflektierenden Aufrufe zusammenfassen, die XStream beim Deserialisieren dieses XML ausführt

  1. XStream ruft den TreeSet- Kopierkonstruktor mit einer Sammlung auf, die einen String "foo" und unseren vergleichbaren Proxy enthält.
  2. Der TreeSet Konstruktor ruft unsere Vergleichbare Proxy des compareTo Methode, um die Reihenfolge der Elemente in der sortierten Satz zu bestimmen.
  3. Unser vergleichbarer dynamischer Proxy delegiert alle Methodenaufrufe an den EventHandler .
  4. The EventHandler is configured to invoke the start() method of the ProcessBuilder it composes.
  5. The ProcessBuilder forks a new process running the command the attacker wishes to execute.

4. When Is XStream Vulnerable?

XStream can be vulnerable to this remote code execution attack when the attacker controls the XML it reads.

For instance, consider a REST API that accepts XML input. If this REST API uses XStream to read XML request bodies, then it may be vulnerable to a remote code execution attack because attackers control the content of the XML sent to the API.

On the other hand, an application that only uses XStream to read trusted XML has a much smaller attack surface.

For example, consider an application that only uses XStream to read XML configuration files set by an application administrator. This application is not exposed to XStream remote code execution because attackers are not in control of the XML the application reads (the admin is).

5. Hardening XStream Against Remote Code Execution Attacks

Fortunately, XStream introduced a security framework in version 1.4.7. We can use the security framework to harden our example against remote code execution attacks. The security framework allows us to configure XStream with a whitelist of types it is allowed to instantiate.

This list will only include basic types and our Person class:

XStream xstream = new XStream(); xstream.addPermission(NoTypePermission.NONE); xstream.addPermission(NullPermission.NULL); xstream.addPermission(PrimitiveTypePermission.PRIMITIVES); xstream.allowTypes(new Class[] { Person.class });

Darüber hinaus können XStream-Benutzer in Betracht ziehen, ihre Systeme mithilfe eines RASP-Agenten (Runtime Application Self-Protection) zu härten. RASP-Agenten verwenden zur Laufzeit die Bytecode-Instrumentierung, um Angriffe automatisch zu erkennen und zu blockieren. Diese Technik ist weniger fehleranfällig als das manuelle Erstellen einer Whitelist von Typen.

6. Fazit

In diesem Artikel haben wir gelernt, wie ein Remotecodeausführungsangriff auf eine Anwendung ausgeführt wird, die XStream zum Lesen von XML verwendet. Da solche Angriffe existieren, muss XStream gehärtet werden, wenn es zum Lesen von XML aus nicht vertrauenswürdigen Quellen verwendet wird.

Der Exploit ist vorhanden, weil XStream mithilfe von Reflection Java-Klassen instanziiert, die durch das XML des Angreifers identifiziert werden.

Wie immer finden Sie den Code für die Beispiele auf GitHub.