Отдалечено изпълнение на код с XStream

1. Общ преглед

В този урок ще разчленим атака за отдалечено изпълнение на код срещу библиотеката за сериализация XStream XML. Този експлойт попада в категорията на ненадеждните десериализиращи атаки.

Ще научим кога XStream е уязвим за тази атака, как работи атаката и как да предотвратим подобни атаки.

2. Основи на XStream

Преди да опишем атаката, нека прегледаме някои основи на XStream. XStream е XML библиотека за сериализация, която превежда между типовете Java и XML. Помислете за прост клас Person :

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

Нека да видим как XStream може да напише някакъв екземпляр Person в XML:

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

По същия начин XStream може да чете XML в екземпляр на Person :

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

И в двата случая XStream използва отражение на Java, за да преведе типа Person към и от XML. Атаката се извършва по време на четене на XML. Когато чете XML, XStream създава инстанции на Java класове, използвайки отражение.

Класовете XStream създават екземпляри се определят от имената на XML елементите, които той анализира.

Тъй като конфигурирахме XStream да знае за типа Person , XStream създава инстанция за нов Person, когато анализира XML елементи с име „person“.

В допълнение към дефинираните от потребителя типове като Person , XStream разпознава основните типове Java извън кутията. Например XStream може да чете карта от XML:

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

Ще видим как способността на XStream да чете XML, представящ основните типове Java, ще бъде полезна при експлоатацията на отдалечено изпълнение на код.

3. Как работи Атаката

Атаките за дистанционно изпълнение на код се случват, когато нападателите предоставят вход, който в крайна сметка се интерпретира като код. В този случай атакуващите използват десериализиращата стратегия на XStream, като предоставят код за атака като XML.

С правилния състав на класовете, XStream в крайна сметка изпълнява кода за атака чрез отражение на Java.

Нека да изградим примерна атака.

3.1. Включете код за атака в ProcessBuilder

Нашата атака има за цел да стартира нов процес на настолен калкулатор. В macOS това е „/Applications/Calculator.app“. В Windows това е „calc.exe“. За целта ще подмамим XStream да стартира нов процес с помощта на ProcessBuilder. Припомнете кода на Java, за да стартирате нов процес:

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

Когато чете XML, XStream извиква само конструктори и задава полета. Следователно нападателят няма ясен начин да извика метода ProcessBuilder.start () .

Въпреки това, на умелите нападатели могат да използват дясната състава на класове в крайна сметка да изпълни ProcessBuilder е старт () метод.

Изследователят по сигурността Динис Круз ни показва в публикацията си в блога как те използват сравнимия интерфейс за извикване на код за атака в конструктора за копиране на сортираната колекция TreeSet. Тук ще обобщим подхода.

3.2. Създайте сравним динамичен прокси

Спомнете си, че нападателят трябва да създаде ProcessBuilder и да извика неговия метод start () . За да направите това, ще създадем инстанция на Сравними чиито сравняват метод извиква ProcessBuilder е старт () метод.

За щастие, Java Dynamic Proxies ни позволяват да създадем динамично екземпляр на Comparable .

Освен това, Java- класът EventHandler предоставя на нападателя конфигурируемо изпълнение на InvocationHandler . Нападателят конфигурира EventHandler да се позове на ProcessBuilder е старт () метод.

Събирайки тези компоненти заедно, имаме XStream XML представяне за сравнимия прокси:

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

3.3. Принудително сравнение с помощта на сравнимия динамичен прокси

За да накарате едно сравнение с нашата Сравними пълномощник, ще се изгради сортиран колекция. Нека да изградим колекция TreeSet, която сравнява два сравними екземпляра: String и нашия прокси.

За изграждането на тази колекция ще използваме конструктора за копиране на TreeSet . И накрая, имаме XStream XML представяне за нов TreeSet, съдържащ нашия прокси и String :

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

В крайна сметка атаката възниква, когато XStream чете този XML. Докато разработчикът очаква XStream да прочете човек , той вместо това изпълнява атаката:

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

3.4. Обобщение на атаките

Нека обобщим отразяващите обаждания, които XStream прави, когато десериализира този XML

  1. XStream извиква конструктора за копиране на TreeSet с колекция, съдържаща String “foo” и нашия сравним прокси.
  2. Конструкторът TreeSet извиква метода compareTo на нашия сравним прокси, за да определи реда на елементите в сортирания набор.
  3. Нашият сравним динамичен прокси делегира всички извиквания на методи към 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 });

Освен това потребителите на XStream могат да обмислят укрепване на своите системи с помощта на агент за самозащита на Runtime Application (RASP). Агентите на RASP използват байтови инструменти по време на изпълнение за автоматично откриване и блокиране на атаки. Тази техника е по-малко податлива на грешки, отколкото ръчно изграждане на бял списък с типове.

6. Заключение

В тази статия научихме как да извършим атака за отдалечено изпълнение на код на приложение, което използва XStream за четене на XML. Тъй като атаки като тази съществуват, XStream трябва да бъде втвърден, когато се използва за четене на XML от ненадеждни източници.

Експлойтът съществува, защото XStream използва отражение за създаване на Java класове, идентифицирани от XML на атакуващия.

Както винаги, кодът за примерите може да бъде намерен в GitHub.