Преди време писах за един проблем със сигурността, който намерих в библиотеката. Тази публикация описва друга малка уязвимост в Apache Olingo. Проблемът е коригиран и във версия 4.7.0.

Между другото, Apache Olingo е Java библиотека, която внедрява Open Data Protocol (OData). Този протокол позволява създаването и потреблението на подлежащи на запитване и оперативно съвместими RESTful API по лесен начин.

(Първоначално публикувано на https://blog.gypsyengineer.com)

Проблемът

Apache Olingo има клас AbstractService, който е част от публичния API. Според Javadoc, класът е входна точка за прокси режима. Той дава достъп до екземпляри на контейнери на обекти. Конструкторът на класа взема компресирани метаданни. По-точно, конструкторът очаква сериализиран XMLMetadata обект, който е кодиран с Base64 и компресиран с GZIP. Кой механизъм за сериализация използва? Това е сериализацията на Java по подразбиране, за която е известно, че е уязвима на атаки за десериализация. Ето как конструкторът обработва компресираните метаданни:

protected AbstractService(final String compressedMetadata, final String metadataETag,
      final ODataServiceVersion version, final String serviceRoot, final boolean transactional) {
ByteArrayInputStream bais = null;
    GZIPInputStream gzis = null;
    ObjectInputStream ois = null;
    XMLMetadata metadata = null;
    try {
      // use commons codec's Base64 in this fashion to stay compatible with Android
      bais = new ByteArrayInputStream(new Base64().decode(compressedMetadata.getBytes("UTF-8")));
      gzis = new GZIPInputStream(bais);
      ois = new ObjectInputStream(gzis);
      metadata = (XMLMetadata) ois.readObject();

Първо, той използва метода Base64.decode() за декодиране на входните данни. След това преобразува декодираните данни в байтов масив и обвива масива в ByteArrayInputStream екземпляр. След това входният поток се обвива в GZIPInputStream обект за декомпресиране на данните. И накрая, входният поток GZIP е обвит в ObjectInputStream за десериализация на метаданните.

По подразбиране ObjectInputStream не прилага никаква проверка, когато десериализира данни. Това означава, че ако атакуващият е в състояние да подаде злонамерени метаданни към клас, който разширява AbstractService, тогава в по-лошия случай това може да доведе до изпълнение на кода на атакуващия.

Сериозността на проблема силно зависи от това как дадено приложение използва класа AbstractService и откъде идват метаданните.

Ако приложение зареди метаданните от защитен ресурс, нападателят трябва да намери начин как да запише злонамерени метаданни в защитения ресурс. Може да не е лесно. Например, ако метаданните се зареждат от правилно конфигурирана база данни, тогава атакуващият трябва да намери SQL инжекция или друг начин, който би му позволил да инжектира злонамерени данни в базата данни. Или, ако приложението зареди метаданните от файл, който има правилни права за достъп, тогава атакуващият трябва да намери начин как да пише във файла.

От друга страна, ако дадено приложение получи метаданните през мрежата, може да е много по-лесно за атакуващия да накара приложението да зареди неговите злонамерени данни.

Независимо от това, поддържащите проекта се съгласиха да направят класа AbstractService малко по-безопасен за използване.

Решението

Проблемът е смекчен чрез прилагане на бял списък за класове, които са разрешени за десериализация. Това може да бъде направено, например, чрез разширяване на класа ObjectInputStream и прилагане на белия списък в отменения метод resolveClass(). Но не беше необходимо, защото Olingo използва Apache IO, който предлага класа ValidatingObjectInputStream за прилагане на такъв бял списък. Друг вариант може да са филтрите, които са добавени в JEP 290, но този начин ще изисква използването на по-нови версии на Java.

„Поправката“ беше доста проста:

  • Добавен е фабричен метод createObjectInputStream(), който създава ValidatingObjectInputStream екземпляр, конфигуриран с бял списък от класове, които са разрешени за десериализация.
  • Конструкторът на класа AbstractService извиква метода createObjectInputStream(), за да получи десериализатор за метаданни.
  • По подразбиране белият списък съдържа само класове от пакета org.apache.olingo.
  • Ако потребител иска да разшири белия списък по подразбиране, той може да замени метода getAllowedClasses(), за да го накара да върне списък с разрешени класове.

Корекцията е пусната в Apache Olingo 4.7.0.

Приложението ми уязвимо ли е?

Определено не всяко приложение, което използва по-стари версии на Apache Olingo, е уязвимо. Има поне две неща, които трябва да бъдат проверени в приложение, което използва Apache Olingo:

  1. Използва ли класа AbstractService?
  2. Има ли начин как някой може да подаде злонамерени данни на класа?

Отговорите на въпросите по-горе зависят от конкретното приложение. Ако и двата отговора са „да“, тогава е вероятно приложението да е уязвимо.

Заключение

Този проблем е друг пример за уязвимост на десериализация, когато дадено приложение използва механизма за десериализация на Java по подразбиране. Не всички приложения, които използват Apache Olingo, са уязвими по подразбиране. Но може би е по-добре да останете на сигурно място и да актуализирате библиотеката.

Препратки

Първоначално публикувано в https://blog.gypsyengineer.com на 21 декември 2019 г.

Следвайте Infosec Write-ups за още такива страхотни описания.