Externe code-uitvoering met XStream

1. Overzicht

In deze zelfstudie ontleden we een Remote Code Execution-aanval op de XStream XML-serialisatiebibliotheek. Deze exploit valt in de niet-vertrouwde deserialisatie categorie aanvallen.

We zullen leren wanneer XStream kwetsbaar is voor deze aanval, hoe de aanval werkt en hoe dergelijke aanvallen kunnen worden voorkomen.

2. XStream Basics

Laten we, voordat we de aanval beschrijven, enkele basisprincipes van XStream bekijken. XStream is een XML-serialiseringsbibliotheek die zich vertaalt tussen Java-typen en XML. Overweeg een simpele Persoon klasse:

openbare klasse Persoon {privé String eerst; private String laatste; // standaard getters en setters}

Laten we eens kijken hoe XStream wat kan schrijven Persoon instantie naar XML:

XStream xstream = nieuwe XStream (); String xml = xstream.toXML (persoon);

Evenzo kan XStream XML inlezen in een instantie van Persoon:

XStream xstream = nieuwe XStream (); xstream.alias ("persoon", Persoon.klasse); String xml = "JohnSmith"; Persoon person = (Persoon) xstream.fromXML (xml);

In beide gevallen gebruikt XStream Java-reflectie om het Persoon typ van en naar XML. De aanval vindt plaats tijdens het lezen van XML. Bij het lezen van XML instantieert XStream Java-klassen met behulp van reflectie.

De klassen die XStream instantieert, worden bepaald door de namen van de XML-elementen die het parseert.

Omdat we XStream hebben geconfigureerd om op de hoogte te zijn van de Persoon type, start XStream een ​​nieuw Persoon wanneer het XML-elementen met de naam "persoon" parseert.

Naast door de gebruiker gedefinieerde typen zoals Persoon, XStream herkent standaard Java-typen. XStream kan bijvoorbeeld een Kaart van XML:

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

We zullen zien hoe het vermogen van XStream om XML te lezen die de belangrijkste Java-typen vertegenwoordigt, nuttig zal zijn bij het misbruik van externe code.

3. Hoe de aanval werkt

Aanvallen met het uitvoeren van externe code vinden plaats wanneer aanvallers invoer leveren die uiteindelijk als code wordt geïnterpreteerd. In dit geval maken aanvallers gebruik van de deserialisatiestrategie van XStream door aanvalscode als XML te leveren.

Met de juiste samenstelling van klassen voert XStream uiteindelijk de aanvalscode uit via Java-reflectie.

Laten we een voorbeeldaanval bouwen.

3.1. Neem aanvalscode op in een ProcessBuilder

Onze aanval is bedoeld om een ​​nieuw rekenmachineproces op de desktop te starten. Op macOS is dit "/Applications/Calculator.app". Bij Windows is dit “calc.exe”. Om dit te doen, zullen we XStream misleiden om een ​​nieuw proces uit te voeren met behulp van een ProcessBuilder. Roep de Java-code op om een ​​nieuw proces te starten:

nieuw ProcessBuilder (). commando ("executable-name-here"). start ();

Bij het lezen van XML roept XStream alleen constructors aan en stelt velden in. Daarom heeft de aanvaller geen eenvoudige manier om het ProcessBuilder.start () methode.

Slimme aanvallers kunnen echter de juiste samenstelling van klassen gebruiken om uiteindelijk het ProcessBuilder‘S begin() methode.

Beveiligingsonderzoeker Dinis Cruz laat ons in hun blogpost zien hoe ze de Vergelijkbaar interface om de aanvalscode in de kopieerconstructor van de gesorteerde verzameling aan te roepen TreeSet. We vatten de aanpak hier samen.

3.2. Maak een Vergelijkbaar Dynamische proxy

Bedenk dat de aanvaller een ProcessBuilder en roep zijn begin() methode. Om dit te doen, maken we een instantie van Vergelijkbaar van wie vergelijken methode roept de ProcessBuilder‘S begin() methode.

Gelukkig stellen Java Dynamic Proxies ons in staat om een ​​instantie van Vergelijkbaar dynamisch.

Bovendien, Java's EventHandler class biedt de aanvaller een configureerbaar InvocationHandler implementatie. De aanvaller configureert het EventHandler om het ProcessBuilder‘S begin() methode.

Als we deze componenten samenvoegen, hebben we een XStream XML-weergave voor de Vergelijkbaar volmacht:

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

3.3. Forceer een vergelijking met behulp van de Vergelijkbaar Dynamische proxy

Om een ​​vergelijking met onze te forceren Vergelijkbaar proxy, we bouwen een gesorteerde verzameling. Laten we een TreeSet verzameling die twee vergelijkt Vergelijkbaar gevallen: a Draad en onze gevolmachtigde.

We zullen gebruiken TreeSet‘S copy constructor om deze collectie samen te stellen. Ten slotte hebben we de XStream XML-weergave voor een nieuwe TreeSet met onze proxy en een Draad:

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

Uiteindelijk vindt de aanval plaats wanneer XStream deze XML leest. Terwijl de ontwikkelaar verwacht dat XStream een Persoon, voert het in plaats daarvan de aanval uit:

String gesorteerdSortAttack = // XML van boven XStream xstream = nieuwe XStream (); Persoon person = (Persoon) xstream.fromXML (gesorteerdSortAttack);

3.4. Aanvalsoverzicht

Laten we de reflecterende oproepen samenvatten die XStream maakt wanneer het deze XML deserialiseert

  1. XStream roept de TreeSet copy constructor met een Verzameling met een Draad "Foo" en onze Vergelijkbaar proxy.
  2. De TreeSet constructor noemt onze Vergelijkbaar proxy's vergelijk met methode om de volgorde van de items in de gesorteerde set te bepalen.
  3. Onze Vergelijkbaar dynamische proxy delegeert alle methodeaanroepen naar de EventHandler.
  4. De EventHandler is geconfigureerd om het begin() methode van de ProcessBuilder het componeert.
  5. De ProcessBuilder forkt een nieuw proces waarbij het commando wordt uitgevoerd dat de aanvaller wenst uit te voeren.

4. Wanneer is XStream kwetsbaar?

XStream kan kwetsbaar zijn voor deze externe code-uitvoeringsaanval wanneer de aanvaller de XML beheert die het leest.

Overweeg bijvoorbeeld een REST API die XML-invoer accepteert. Als deze REST API XStream gebruikt om XML-aanvraagbody's te lezen, kan deze kwetsbaar zijn voor een externe code-uitvoeringsaanval omdat aanvallers de inhoud van de XML die naar de API wordt verzonden, controleren.

Aan de andere kant heeft een applicatie die alleen XStream gebruikt om vertrouwde XML te lezen een veel kleiner aanvalsoppervlak.

Overweeg bijvoorbeeld een toepassing die alleen XStream gebruikt om XML-configuratiebestanden te lezen die zijn ingesteld door een toepassingsbeheerder. Deze applicatie is niet blootgesteld aan XStream-uitvoering van externe code omdat aanvallers geen controle hebben over de XML die de applicatie leest (de admin is).

5. XStream harden tegen aanvallen op het uitvoeren van externe code

Gelukkig introduceerde XStream een ​​beveiligingsraamwerk in versie 1.4.7. We kunnen het beveiligingsraamwerk gebruiken om ons voorbeeld te beschermen tegen aanvallen op het uitvoeren van externe code. Het beveiligingsraamwerk stelt ons in staat om XStream te configureren met een witte lijst van typen die het mag instantiëren.

Deze lijst bevat alleen basistypen en onze Persoon klasse:

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

Bovendien kunnen XStream-gebruikers overwegen om hun systemen te versterken met behulp van een Runtime Application Self-Protection (RASP) -agent. RASP-agenten gebruiken bytecode-instrumentatie tijdens runtime om aanvallen automatisch te detecteren en te blokkeren. Deze techniek is minder foutgevoelig dan het handmatig samenstellen van een witte lijst van typen.

6. Conclusie

In dit artikel hebben we geleerd hoe we een externe code-uitvoeringsaanval kunnen uitvoeren op een toepassing die XStream gebruikt om XML te lezen. Omdat aanvallen zoals deze bestaan, moet XStream worden gehard wanneer het wordt gebruikt om XML uit niet-vertrouwde bronnen te lezen.

De exploit bestaat omdat XStream reflectie gebruikt om Java-klassen te instantiëren die worden geïdentificeerd door de XML van de aanvaller.

Zoals altijd is de code voor de voorbeelden te vinden op GitHub.