Fjernudførelse af kode med XStream

1. Oversigt

I denne vejledning dissekerer vi et Remote Code Execution-angreb mod XStream XML-serialiseringsbiblioteket. Denne udnyttelse falder ind i utroværdig deserialisering kategori af angreb.

Vi lærer, hvornår XStream er sårbar over for dette angreb, hvordan angrebet fungerer, og hvordan man forhindrer sådanne angreb.

2. Grundlæggende om XStream

Før vi beskriver angrebet, lad os gennemgå nogle af de grundlæggende XStream-forhold. XStream er et XML-serialiseringsbibliotek, der oversættes mellem Java-typer og XML. Overvej et simpelt Person klasse:

offentlig klasse person {privat streng først; privat streng sidste; // standard getters og setter}

Lad os se, hvordan XStream kan skrive nogle Person forekomst til XML:

XStream xstream = ny XStream (); Streng xml = xstream.toXML (person);

Ligeledes kan XStream læse XML i en forekomst af Person:

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

I begge tilfælde bruger XStream Java-refleksion til at oversætte Person skriv til og fra XML. Angrebet finder sted under læsning af XML. Når XML læser XML, iværksætter XStream Java-klasser ved hjælp af refleksion.

Klasserne XStream instantiates bestemmes af navnene på de XML-elementer, den analyserer.

Fordi vi konfigurerede XStream til at være opmærksom på Person type, XStream instantierer en ny Person når det analyserer XML-elementer med navnet “person”.

Ud over brugerdefinerede typer som Person, XStream genkender centrale Java-typer ud af kassen. For eksempel kan XStream læse en Kort fra XML:

String xml = "" + "" + "" + "foo" + "10" + "" + ""; XStream xStream = ny XStream (); Kortkort = (kort) xStream.fromXML (xml); 

Vi ser, hvordan XStreams evne til at læse XML, der repræsenterer centrale Java-typer, vil være nyttigt i udnyttelsen af ​​fjernkodekørsel.

3. Sådan fungerer angrebet

Fjernkodeudførelsesangreb opstår, når angribere leverer input, som i sidste ende tolkes som kode. I dette tilfælde udnytter angribere XStreams deserialiseringsstrategi ved at angive angrebskode som XML.

Med den rigtige sammensætning af klasser kører XStream i sidste ende angrebskoden gennem Java-refleksion.

Lad os bygge et eksempelangreb.

3.1. Inkluder angrebskode i en ProcessBuilder

Vores angreb sigter mod at starte en ny desktop-lommeregnerproces. På macOS er dette “/Applications/Calculator.app”. I Windows er dette “calc.exe”. For at gøre det vil vi narre XStream til at køre en ny proces ved hjælp af en ProcessBuilder. Husk Java-koden for at starte en ny proces:

ny ProcessBuilder (). kommando ("eksekverbart navn-her"). start ();

Når du læser XML, påkalder XStream kun konstruktører og sætter felter. Derfor har angriberen ikke en ligetil måde at påberåbe sig ProcessBuilder.start () metode.

Dog kan kloge angribere bruge den rigtige sammensætning af klasser til i sidste ende at udføre ProcessBuilder'S Start() metode.

Sikkerhedsforsker Dinis Cruz viser os i deres blogindlæg, hvordan de bruger Sammenlignelig interface til at påkalde angrebskoden i kopibyggeren til den sorterede samling TreeSet. Vi opsummerer fremgangsmåden her.

3.2. Lave en Sammenlignelig Dynamisk proxy

Husk at angriberen skal oprette en ProcessBuilder og påberåbe sig dens Start() metode. For at gøre det opretter vi en forekomst af Sammenlignelig hvis sammenligne metode påberåber sig ProcessBuilder'S Start() metode.

Heldigvis tillader Java Dynamic Proxies os at oprette en forekomst af Sammenlignelig dynamisk.

Desuden Java EventHandler klasse giver angriberen en konfigurerbar InvocationHandler implementering. Angriberen konfigurerer EventHandler at påberåbe sig ProcessBuilder'S Start() metode.

Når vi sætter disse komponenter sammen, har vi en XStream XML-repræsentation til Sammenlignelig fuldmagt:

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

3.3. Tving en sammenligning ved hjælp af Sammenlignelig Dynamisk proxy

For at tvinge en sammenligning med vores Sammenlignelig proxy, vi bygger en sorteret samling. Lad os bygge en TreeSet samling, der sammenligner to Sammenlignelig tilfælde: a Snor og vores fuldmægtig.

Vi bruger TreeSet'S kopikonstruktør til at bygge denne samling. Endelig har vi XStream XML-repræsentationen for en ny TreeSet der indeholder vores fuldmagt og en Snor:

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

I sidste ende sker angrebet, når XStream læser denne XML. Mens udvikleren forventer, at XStream læser en Person, det udfører i stedet angrebet:

String sortedSortAttack = // XML ovenfra XStream xstream = ny XStream (); Person person = (Person) xstream.fromXML (sortedSortAttack);

3.4. Oversigt over angreb

Lad os opsummere de reflekterende opkald, som XStream foretager, når den deserialiserer denne XML

  1. XStream påberåber sig TreeSet kopi konstruktør med en Kollektion indeholdende en Snor “Foo” og vores Sammenlignelig fuldmagt.
  2. Det TreeSet konstruktør kalder vores Sammenlignelig proxy's sammenligne med metode for at bestemme rækkefølgen af ​​varerne i det sorterede sæt.
  3. Vores Sammenlignelig dynamisk proxy delegerer alle metodekald til EventHandler.
  4. Det EventHandler er konfigureret til at påberåbe sig Start() metode til ProcessBuilder det komponerer.
  5. Det ProcessBuilder gafler en ny proces, der kører den kommando, som angriberen ønsker at udføre.

4. Hvornår er XStream sårbar?

XStream kan være sårbar over for dette eksterne kodeudførelsesangreb, når angriberen styrer den XML, den læser.

Overvej f.eks. En REST API, der accepterer XML-input. Hvis denne REST API bruger XStream til at læse XML-anmodningsorganer, kan det være sårbart over for et eksternt kodeudførelsesangreb, fordi angribere styrer indholdet af XML sendt til API'en.

På den anden side har et program, der kun bruger XStream til at læse betroet XML, en meget mindre angrebsflade.

Overvej f.eks. Et program, der kun bruger XStream til at læse XML-konfigurationsfiler, der er indstillet af en applikationsadministrator. Denne applikation udsættes ikke for XStream fjernudførelse af kode, fordi angribere ikke har kontrol over den XML, som applikationen læser (administratoren er).

5. Hærdning af XStream mod eksterne kodeudførelsesangreb

Heldigvis introducerede XStream en sikkerhedsramme i version 1.4.7. Vi kan bruge sikkerhedsrammen til at hærde vores eksempel mod angreb på fjernudførelse af kode. Sikkerhedsrammen giver os mulighed for at konfigurere XStream med en hvidliste af typer, som det er tilladt at instantiere.

Denne liste inkluderer kun grundlæggende typer og vores Person klasse:

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

Derudover kan XStream-brugere overveje at hærde deres systemer ved hjælp af en RASP-agent (Runtime Application Self-Protection). RASP-agenter bruger bytecode-instrumentering på kørselstidspunktet til automatisk at opdage og blokere angreb. Denne teknik er mindre udsat for fejl end manuelt at oprette en hvidliste af typer.

6. Konklusion

I denne artikel lærte vi, hvordan man udfører et angreb på fjernudførelse af kode på et program, der bruger XStream til at læse XML. Fordi der findes angreb som dette, skal XStream hærdes, når det bruges til at læse XML fra kilder, der ikke er tillid til.

Udnyttelsen eksisterer, fordi XStream bruger refleksion til at instantiere Java-klasser identificeret af angriberens XML.

Som altid kan koden til eksemplerne findes på GitHub.