Pull to refresh
79.6
Swordfish Security
Информационная безопасность, DevSecOps, SSDL

Разбираемся с MavenGate, новой атакой на цепочку поставок для Java и Android-приложений

Reading time11 min
Views5K

Всем привет!

Сегодня с вами Юрий Шабалин, генеральный директор «Стингрей Технолоджиз», и я хотел бы разобрать в этой статье новый тип атаки на цепочку поставок под названием «MavenGate».

А что в ней, собственно, такого? Ну хотя бы то, что ей подвержены более 18% всех Java-библиотек, соответственно, для Android это применимо в полной мере. Совершить эту атаку безумно просто. Суть ее основана на логике работы сборщиков Java/Android-проектов и разрозненности репозиториев, которые хранят эти библиотеки.

Если стало интересно и хочется проверить, уязвимы ли вы, добро пожаловать под кат!

Введение

17 января 2024 компания Oversecured выпустила в своем блоге детальный отчет по новому типу атаки на цепочку поставок, которая затрагивает просто неимоверное количество зависимостей во многих Java-репозиториях, таких как Maven Central, Jcenter, Jitpack и многих других.

Но на самом деле, эта проблема касается не столько самих репозиториев, которые просто публикуют библиотеки и дают их загружать (хотя и к ним есть вопросы), сколько настройки и логики работы систем сборки Maven и Gradle.

Здесь мы не будем дословно пересказывать оригинальное исследование (хотя и без отсылок к нему не обойтись). Мы обсудим его, дополним немного и поясним, что все это значит и чем может обернуться. Данная атака мне показалась весьма перспективной и, к сожалению, мало освещенной. Мы провели свой собственный эксперимент, чтобы прояснить возникшие вопросы и наглядно показать, как она работает и как устроена логика различных систем сборки. Так как наша компания занимается анализом защищенности мобильных приложений, то все примеры и эксперименты относятся к Android-приложениям. Но для всех Java-проектов процесс аналогичен.

Поехали!

Суть атаки

Для начала разберем, в чем заключается суть этой атаки, почему она стала возможной и почему она так опасна?

Для этого обратимся к первоисточнику и посмотрим, из чего состоит типичный Gradle-проект и как работает механизм управления зависимостями.

  1. Каждый Gradle-проект содержит в себе список репозиториев, откуда необходимо выгружать зависимости. Они могут быть разными, но наиболее популярные проекты – это MavenCentral, JCentral и JitPack с Google (для Android). Репозитории разделяются на приватные площадки, где публиковать компоненты могут только их правообладатели (например, Google-репозиторий) и публичные репозитории, где каждый может размещать что угодно (среди них как раз MavenCentral, JCenter и т.д.

    Указание перечня репозиториев в файле сборки gradle
    Указание перечня репозиториев в файле сборки gradle
  2. Список зависимостей имеет формат groupId:artifactId:version, например, com.google.code.gson:gson:2.10.1

    Описание зависимостей в файле сборки
    Описание зависимостей в файле сборки

    Сборщик идет по репозиториям сверху вниз и скачивает указанную версию из первого репозитория, в котором она будет найдена

Основным моментом здесь является тот факт, что большинство разработчиков библиотек публикует их только в одном репозитории, максимум в двух. А во всех остальных ее нет.

Но как опубликовать свою библиотеку, что для этого нужно? Как правило, процесс очень похож во многих репозиториях. Чтобы выложить библиотеку, необходимо владеть доменом, который относится к ее group.id. То есть, чтобы выложить пакет с именем ru.stingraymobile, необходимо быть владельцем домена stingraymobile.ru. И чтобы это подтвердить, на момент регистрации необходимо добавить TXT-запись с определенным значением в ваш домен.

А теперь представим, что разработчик библиотеки, который опубликовал ее несколько лет назад, купил домен, периодически обновляет ее и развивает, выложил ее в репозиторий JitPack. Все хорошо, библиотекой пользуются, она удобная, фанаты ставят звездочки на Github, но про домен владелец забыл (ведь подтверждение необходимо только один раз). И спустя год он поступил в свободную продажу. Что мешает злоумышленнику купить этот домен, зарегистрироваться во всех остальных репозиториях и загрузить туда модифицированную версию этой библиотеки? Ответ прост – абсолютно ничего.

Именно на этой особенности и построена рассматриваемая атака: библиотеки, домены которых доступны для свободной регистрации, уязвимы и могут быть подменены злоумышленниками в процессе сборки приложения. Более того, многие из репозиториев позволяют использовать GitHub-аккаунты в качестве подтверждения авторства. Но если автор изменит свой аккаунт на Github, его имя пользователя станет доступно для регистрации. Это даже проще, чем купить домен- просто зарегистрировать нужный тебе Github-аккаунт.

Ну и немного статистики от автора:

Репозиторий

Всего доменов, шт.

Из них уязвимо, шт.

Из них уязвимо, %

Maven Central

26 163

3 710

14.18

GitHub

7 523

291

3.86

Суммарно, включая приватные репозитории

33 938

6 170

18.18

Статистика получается крайне интересная, а ведь это только MavenCentral. А ведь сколько библиотек в JCenter, которых нет в MavenCentral, и сколько их в других репозиториях? И сколько проектов их использует? Если посчитать все, итоговые цифры могут получиться очень впечатляющими.

Но наибольшая опасность, как обычно, кроется в транзитивных зависимостях, ведь мы не знаем, на какие еще библиотеки ссылается пакет, который был включен в проект напрямую. А транзитивные зависимости транзитивных зависимостей? Ох уж этот прекрасный Java-мир, сколько еще открытий ты нам преподнесешь.

Но давайте посмотрим на реакцию общественности и самих репозиториев на сложившуюся ситуацию. 19 января 2024 года компания Sonatype выпустила ответ, в котором рассказала о мерах, предпринятых для митигации этой атаки:

  • По их заявлению, подтверждение при помощи DNS производится только при первой публикации, а в дальнейшем при попытке восстановления контроля над аккаунтом требуется обращение в техническую поддержку. Это конечно, хорошо и понятно, но не отменяет того факта, что данную библиотеку можно свободно загрузить в любые другие репозитории;

  • Все аккаунты с доменами, свободными для продажи, были отключены, и их восстановление будет проходить через техническую поддержку. Что же, это логичный шаг, очень хорошо, что его сделали, но он абсолютно не отменяет пример из предыдущего пункта;

  • Все последующие комментарии в духе «используйте наши продукты и будет вам счастье». Да, но нет - у многих компаний свои или альтернативные решения, и платить Sonatype им вроде как ни к чему, так что тут по большей части просто реклама.

От остальных владельцев репозиториев комментариев не поступало. А Sonatype предпринял максимум усилий для улучшения ситуации. Они единственные отреагировали, поставили на мониторинг проблемные проекты, за что им большое спасибо. Но в общем это не сделало ситуацию лучше.

Вот какова ситуация прямо сейчас (на момент написания статьи):

  1. До сих пор любой желающий может зарегистрировать домен или взять имя пользователя GitHub и залить во все репозитории, кроме того, где опубликована официальная библиотека, версию с закладкой;

  2. Это действие не требует каких-либо специфических знаний, достаточно обладать базовыми скиллами по созданию скриптов и уметь читать;

  3. Уязвимы могут быть не только прямые зависимости, но и транзитивные, а через них и ваш проект;

  4. Действия одной компании-владельца репозитория (Sonatype) практически никак не повлияли на общую ситуацию.

Таким образом, несколько вопросов до сих пор не закрыты. Главное, что важно знать пользователям, как работают сборщики, как влияет порядок репозиториев внутри файла сборки и, главное, как от этого можно защититься? Ниже разберемся с ними.

Эксперименты

Для ответа на вопрос о том, как работают сборщики проектов и как они определяют, какие именно зависимости откуда выкачивать, мы решили не теоретизировать, а провести ряд экспериментов и посмотреть, как дела обстоят на самом деле. Нам важно было понять, как будут вести себя инструменты сборки, если конкретная версия не указана, и будут ли выдавать подсказки по обновлению в IDE в зависимости от расположения репозиториев.

Мы осуществили проверку ряда гипотез:

  1. Версия библиотеки в двух репозиториях одинаковая. В файле сборки выставлена конкретная версия. Что произойдет, если:

    1. Первым стоит MavenCentral

    2. Первым стоит JitPack

  2. Версия библиотеки различается в MavenCentral (1.0) и в JitPack (1.0 и 1.1). Что произойдет, если:

    1. Версия библиотеки не указана конкретно и первым стоит Maven

    2. Версия библиотеки не указана конкретно и первым стоит JitPack

    3. Версия библиотеки указана конкретно (1.0) и первым стоит Maven

    4. Версия библиотеки указана конкретно (1.1) и первым стоит Maven

    5. Версия библиотеки указана конкретно (1.0) и первым стоит JitPack

    6. Версия библиотеки указана конкретно (1.1) и первым стоит JitPack

Итоги нашего исследования я приведу ниже, а также соберу в обобщенную таблицу в конце этой главы.

Гипотеза 1

Итак, проверка гипотезы номер 1: версия библиотеки в разных репозиториях одинаковая. Что будет происходить при сборке, в зависимости от порядка перечисления репозиториев.

Как и следовало ожидать, все оказалось очень логично. При указании конкретной версии, которая есть в обоих репозиториях, библиотека загружается из первого найденного. То есть если первым в файле сборки указан JitPack, загружается из него, если указан Maven, то из него.
Ничего странного, но заставляет сделать вывод, что очередность указания репозиториев в файле сборки на самом деле важна. Но кто об этом задумывается? По моему опыту, это беспокоит крайне мало людей. Поправьте меня в комментариях, если я не прав, пожалуйста.

Гипотеза 2

Вторая гипотеза гораздо интереснее, поскольку действительно непонятно, что будет происходить с зависимостями, если не выставлена конкретная версия, и откуда она будет загружаться. Давайте это детально проверим и посмотрим.

В последних четырех случаях все так же логично: версия библиотеки в обоих репозиториях одинакова и указана конкретно в файле сборки. И здесь из репозитория, который стоит первым на очереди, загружается библиотека. То есть проблема, обозначенная при проверке первой гипотезы, по-прежнему актуальна.

И вот интересно, что произойдет, если версия библиотеки разная в двух репозиториях (причем в файле сборки это не указано). Подразумевается, что должна быть загружена последняя версия, но откуда, это вопрос хороший. Мы не стали проходить весь тернистый путь по публикации библиотеки в MavenCentral до конца (сейчас мы где-то посередине до публикации), но провели данный эксперимент локально на внутреннем Nexus.

Итак, при объявлении версии библиотеки, как самой свежей (это делается через :+ в указании версии), загружается самая свежая версия библиотеки, которая была найдена во всех указанных в файле сборки репозиториях. То есть, если злоумышленник выложит версию библиотеки выше и в файле сборки будет указана самая свежая версия библиотеки, сборщик скачает именно версию злоумышленника.

Указание версии библиотеки как самой новой
Указание версии библиотеки как самой новой

Что интересно, IDE подсвечивает наличие новой версии библиотеки вне зависимости от того, в каком порядке расположены репозитории в системе сборки, и предлагает их обновить. Таким образом, обновление нашей уязвимой библиотеки увидят все, кто ее использует, как прямую зависимость. И IDE любезно предложит ее обновить (спасибо, конечно!)

Итоговая таблица для наглядности:

Версия библиотеки в репозиториях

Версия библиотеки в системе сборки

Первый указанный в системе сборки репозиторий

Результат

Одинаковая версия в Maven и JitPack

Указана конкретная версия (1.0)

Первый Maven

Библиотека загружена из Maven

Первый JitPack

Библиотека загружена из JitPack

Версия в Maven - 1.0
Версии в JitPack 1.0, 1.1

Версия библиотеки не указана

Первый Maven

Библиотека загружена из JitPack

Первый JitPack

Указана конкретная версия (1.0)

Первый Maven

Библиотека загружена из Maven

Первый JitPack

Библиотека загружена из JitPack

Указана конкретная версия (1.1)

Первый Maven

Библиотека загружена из JitPack

Первый JitPack

Таким образом, мы понимаем, что безумно важно правильно указывать порядок репозиториев и конкретные версии, с которыми работает приложение, чтобы избежать «сюрпризов» с репозиторием, из которого будет загружена библиотека.

Простой пример

Для того, чтобы понять, как все это дело можно провернуть, разберем пример. Возьмем библиотеку org.xmlmatchers, первую, которая попалась под руку. По данным Maven, она используется в 62 проектах и опубликована только на MavenCentral. Домен xmlmatchers.org свободен для регистрации и стоит 9.99$ в год.

Вот какой может быть последовательность действий злоумышленника:

  1. Покупает домен xmlmatchers.org за 10 долларов

  2. Регистрируется во всех публичных репозиториях, где первичная валидация происходит по домену

  3. Загружает в эти репозитории библиотеку, которая содержит полезную нагрузку в дополнению к основному функционалу с той же версией, что и в MavenCentral. Дополнительно загружает на одну минорную версию выше

  4. Все пользователи, применяющие библиотеку напрямую (и у кого установлен первым не MavenCentral-репозиторий), получат версию злоумышленника

  5. Все пользователи, которые подключают ее напрямую, получат сообщение от IDE, что библиотеку можно обновить

  6. Все проекты, использующие эту зависимость (и у кого установлен первым не MavenCentral-репозиторий), получат версию злоумышленника

Повторяем это действие для всех найденных библиотек со свободными доменами.

Есть и второй вектор атаки. Для его реализации нужно просканировать все репозитории, кроме Central, на библиотеки со свободными доменами. По полученному списку проверить, каких из них нет в основном Maven-репозитории.
Далее путь очень похожий:

  1. Покупается домен

  2. Дорабатывается и заливается библиотека в MavenCentral

  3. Все проекты и пользователи, у которых первым в списке стоит Central-репозиторий, получают библиотеку с полезной нагрузкой

Весьма несложный путь для атак на достаточно большое количество проектов. Скорее всего, злоумышленник не сможет предсказать, в какой из них попадет его библиотека, так что переживать за целенаправленную атаку конкретно на ваш проект смысла нет.

Стоит сказать, что за прошедшую неделю многие из свободных ранее доменов были куплены. И далеко не факт, что приобрели их владельцы библиотек. К примеру, домен ini4j.org куплен 23 января, а домен jdesktop.org зарегистрирован 19 января. Кто и для чего покупает домены, пока непонятно. Это могут быть как владельцы библиотек, так и злоумышленники, которые нацелены на реализацию данной атаки.

Как защититься

Есть несколько вариантов, как защититься от этого типа атаки. Лучше делать все это вместе, однако, времени это может занять много, если, конечно, не автоматизировать изменение файла сборки. Итак, смотрите рекомендации ниже.

Первый способ: собрать SBOM-файл для своих Java и Android-приложений и посмотреть, что именно они используют, все прямые и транзитивные зависимости. Провести анализ и выявить библиотеки, домены которых свободны для продажи или куплены за прошедшие несколько недель, и исключить эти зависимости из проекта, если это возможно. Дополнительно обязательно проверить, нет ли в ваших файлах сборки пакетов с версией, которая ссылается на самую свежую без прямого указания. В случае обнаружения подобных зависимостей необходимо явно зафиксировать версию библиотеки.

Второй способ: проверить контрольную сумму пакета (целостность), или хэш от загруженной библиотеки. Это потребует достаточно много усилий по первоначальному заполнению файла сборки и по поддержанию его в актуальном состоянии.

Третий способ: это проверка подписи загружаемых библиотек, но, как показывает практика, мало кто из разработчиков публикует открытые ключи. Тем не менее, это один из самых эффективных способов защиты от данной атаки.

Стоит сказать и о разработке в закрытом контуре. Компании, которые используют внутренние репозитории в режиме зеркалирования (библиотека, перед тем как попасть к разработчику или в систему сборки, сначала загружается во внутренний репозиторий и при последующих обращениях к ней отдаёт сохраненный ранее файл), менее подвержены этому типу атаки, чем те, кто загружает библиотеки из публичных репозиториев напрямую. Тем не менее, рекомендуется проверить все библиотеки, которые содержатся в проксирующих репозиториях на предмет возможности захвата домена, посмотреть, какие источники для загрузки Java-библиотек разрешены и какие версии были загружены с начала года. И более детально изучить их, выяснить, а те ли они, за кого себя выдают, или кто-то уже успел загрузить «правильную» версию.

Выводы

Выводы на самом деле неоднозначны. С одной стороны, описанный ваше сценарий является простым способом атаки на цепочку поставок. Единственная его техническая сложность заключается в добавлении «полезной нагрузки» к коду библиотек. И проблема усугубляется тем, что многие компоненты используются как транзитивные, и нет понимания, что они действительно применяются в системе. Уязвимая библиотека может присутствовать в одном из проектов, используемом в другом проекте, и т.д. И получается, что при удачном стечении обстоятельств уязвимы и подвержены данной атаке будут все.

С другой стороны, все очень зависит от настроек сборки проектов: какие репозитории используются в первую очередь, а какие после. Многие проекты окажутся не подвержены этой атаке как раз из-за порядка определения репозиториев.

Но пример простоты и масштабности этой атаки заставляет задуматься о последствиях и предпринять меры по обнаружению подобных зависимостей в ваших проектах или во внутреннем контуре уже сейчас, чтоб быть уверенными в том, что эта проблема вас не коснется.

Со своей стороны, команда Стингрей Технолоджиз разработала функциональность по проверке SBOM-файлов мобильных приложений Android на подверженность данной атаке. Мы уверены, что это поможет нашим клиентам обрести уверенность в том, что их приложения не подвержены данной проблеме. Если вы заинтересованы в том, чтобы проверить свое приложение на уязвимости и на подверженность данной атаке, только напишите.

Также хочу напомнить, что если вам интересна безопасность мобильных приложений, я рад пригласить вас в свой телеграмм-канал Mobile AppSec World, где регулярно публикуются новости, обзоры инструментов, разбор уязвимостей и остальные крайне полезные материалы.

На этом сегодня всё, берегите свои репозитории!

Tags:
Hubs:
Total votes 22: ↑22 and ↓0+22
Comments16

Articles

Information

Website
swordfish-security.ru
Registered
Founded
Employees
101–200 employees
Location
Россия
Representative
SwordfishTeam