Привет, Хабр!
Меня зовут Михаил, я исследователь Центра научных исследований и перспективных разработок, в круг моих обязанностей входит разработка мобильных приложений Android и исследование безопасности разрабатываемых решений. Сегодня рассмотрим, так ли страшен «черт», как его малюют, или насколько страшен MavenGate на самом деле.
В начале этого года специалисты из компании OverSecured опубликовали статью, в которой подробно описали атаку на цепочку поставок MavenGate, поэтому сильно заострять внимание на описании атаки не стану. Если вкратце, злоумышленник может выкупить домен разработчика библиотеки и получить возможность свободно обновлять эту библиотеку от имени автора, и в теории, может добавить что-то нехорошее в эту библиотеку. А разработчики приложений, не проверив содержимое, подтянут инфицированную библиотеку к себе в проект. Ситуация неприятная.
По версии компании OverSecured этой атаке может быть подвержено 18% всех зависимостей в публичных репозиториях, таких, как MavenCentral, jCenter и jitpack. Это довольно много, особенно, если учесть, что open-source проекты, в основном, используют либо зависимости из публичных репозиториев, либо такие же open-source библиотеки из jitpack.
И здесь мне стало интересно, насколько open-source проекты мобильных приложений Android реально подвержены этой атаке, и сколько библиотек уже «инфицировано» в составе этих самых проектов.
Выбор подопытных
Самый простой вариант — это взять наиболее популярные приложения с открытым исходным кодом и проверить их. Погуглил, список получился таким:
Для исследования я не брал браузеры, так как их безопасность — отдельная тема, и по большей части браузеры написаны с использованием нативного кода, а MavenGate больше про JVM код. Еще я не брал приложения, которые на более чем 50% написаны на C/C++, потому что в этом также нет смысла. Конечно артефакты Maven репозиториев могут хранить нативный код, но такие библиотеки мне попадаются очень редко.
Идея исследования
Когда подопытные были выбраны, я задумался, что с ними со всеми делать. Атака предполагает замену библиотеки на аналогичную, но с «приветом», соответственно, хеш‑суммы у них будут разные. Это значит, что если все‑таки кто‑то сумел подложить что‑то нехорошее в библиотеку в одном репозитории, в другом репозитории хеш будет отличаться.
Однако, maven репозитории хранят не только jar файлы, но и aar, так называемые Android Archive, они могут хранить манифест приложения, и в целом, ближе к Android системе нежели jar файлы. Одну и ту же библиотеку можно собрать как jar, так и aar, внутри они будут выглядеть по‑разному, соответственно и хеши этих файлов будут разными, это стоит учесть.
Атака также предполагает выкуп домена, если автор библиотеки об этом не позаботился ранее, поэтому нужно выяснить, кому принадлежит домен, когда зарегистрирован, когда обновлен и когда истекает. Если домен выкуплен сразу после выхода этой новости, это может говорить о том, что может готовиться что‑то неладное. В этом могут помочь Whois сервисы.
Это две основные идеи для анализа зависимостей. Если пройтись по каждой библиотеке и вычислить её хеш в каждом из популярных репозиториев, перевернуть домен, отправить запрос на whois, и соединить все воедино в табличном представлении, то это позволит наглядно продемонстрировать, какие библиотеки абсолютно безопасны, а какие потенциально могут быть с «приветом».
Тогда таблица будет иметь столбцы:
Домен
Имя библиотеки
Версия
Владелец домена
Дата создания
Дата обновления
Дата конца регистрации домена
хеш jar библиотеки из репозитория 1
хеш aar библиотеки из репозитория 1
...
хеш jar библиотеки из репозитория N
хеш aar библиотеки из репозитория N
Для того, чтобы наглядно показать в таблице, какая библиотека может реально представлять опасность, а какая нет, нужно для каждой строки этой таблицы задать правило окраски:
Поясню каждый случай на этой диаграмме
Не красим — когда хеши совпадают и домен обновлен до выхода новости, все прекрасно.
Синий — хеши совпадают, но домен недавно выкуплен, что может говорить о том, что он может быть выкуплен не автором библиотеки.
Желтый — домен выкуплен раньше выхода новости, предполагается, что злоумышленник не мог подложить библиотеку с «приветом», но при этом хеши не совпадают, что очень странно. Конечно, могут быть разные причины, почему так произошло. Разработчик мог просто не заметить, что льет разные библиотеки, помеченные одной и той же версией в разные репозитории.
Красный — крайняя ситуация, в которой и домен недавно обновлен, и хеши не совпадают, очень много вопросов к этой библиотеке, нужно скачивать и смотреть, что там может быть не так, и в случае обнаружения инфицированной библиотеки, принять меры.
Получение зависимостей
Определив алгоритм действий, нужно задуматься о том, как получать зависимости. Так как речь идет о maven репозиториях и android проектах, первое, что приходит на ум — использовать для этого сборщик Gradle, он имеет функционал работы с зависимостями. Кому‑то из вас может прийти идея парсить файлы build.gradle в поисках блока dependeices. Это конечно можно сделать, но есть и транзитивные зависимости, которые тоже нужно получить и разобрать. А еще есть зависимости, которые подвозит плагин. Во всех приложениях могут быть разные плагины и они могут приносить свои зависимости. Конечно, чаще всего, они не вносят зависимости, но шанс такой есть.
Недолго думая, я воспользовался таской:
./gradlew dependencies
Но эта команда выведет список зависимостей только для корня проекта, а это не все зависимости проекта, так как могут быть и подпроекты – модули gradle, у которых могут быть свои зависимости и плагины, их тоже нужно учесть в рамках анализа зависимостей одного проекта.
./gradlew <имя модуля>:dependencies
Чтобы получить список модулей можно вызвать таск:
./gradlew projects
Как не трудно догадаться, парсил я все это добро с помощью регулярок. Далее для домена каждой библиотеки я применял Whois, чтобы выяснить информацию о домене и обращался к самым популярным Maven репозиториям (Maven Central, jCenter, jitpack, clojars — добавил на всякий случай) чтобы выяснить хеш библиотек, потом просто складывал все в pandas массив, чтобы потом перегнать все, скажем, в Excel табличку.
Почему Excel спросите вы, потому что данные в нем проще всего пост-процессить руками самому (конечно в случае необходимости), находить закономерности, ну и, в конечном итоге, обратно перегнать в pandas, чтобы уже какая-то другая утилита занималась пост-процессингом. Ну и данные можно прям в Excel отсортировать по какому-либо признаку, что очень удобно.
При этом важно пояснить что я не брал Google репозиторий, так как в Google вряд ли чего можно подвезти извне, Google не даст)
Так же я не брал в анализ библиотеки, авторство которых явно принадлежит «большому брату». Речь идет о Google, JetBrains и т. п. Эти товарищи просто не позволят, чтобы их библиотеки были скомпрометированы.
Опорной датой для whois, я посчитал 1 февраля 2024 года, несмотря на то, что новость вышла 17 января.
Результат
В результате я прогнал все приложения, о которых говорил выше, но по правилу, описанному в схеме, чаще находились синие, то есть недавно обновленный домен, в принципе, это логично, потому что разработчики начали внимательнее смотреть за своим кодом в публичных репозиториях после выхода новости о MavenGate.
Получилось что-то вроде этого:
В конце я прикреплю ссылку на все xls файлы, чтобы было проще ознакомиться.
Желтых я не нашел ни в одном приложении, что подтверждает то, что разработчики все же заливают одни и те же библиотеки в разные репозитории.
Однако же я все‑таки нашел одну «красную» библиотеку, она находилась в приложении k-9 почте, как видно на скриншоте, хеши явно отличаются в MavenCentral и jitpack, и казалось бы, вот оно, бинго! Какая-то из двух библиотек явно должна вести себя по-другому.
А вот и нет! Если вручную скачать два этих архива по ссылкам:
https://repo.maven.apache.org/maven2/io/github/detekt/sarif4k/sarif4k/0.4.0/sarif4k-0.4.0.jar
https://jitpack.io/io/github/detekt/sarif4k/sarif4k/0.4.0/sarif4k-0.4.0.jar
Можно удостовериться, что хеши реально отличаются:
Так, а в чем же отличие? Нужно посмотреть во внутрь архива и сравнить. Это можно сделать с помощью windiff:
Как видно из вывода windiff, это просто kotlin модуль, в котором отличается лишь манифест. Заглянем в этот манифест:
Если присмотреться, то видно, что строки просто поменяны местами) Это могло произойти просто из-за того, что для разных репозиториев разработчик мог собирать библиотеки в разное время или сборка была под другой конфигурацией. Но на самом деле это не важно, почему строки поменяны, по сути это не поменяет ничего во время сборки приложения с этой библиотекой. Если бы отличался исполняемый код, то это точно было бы бинго!
И того во всех приложениях у меня получилось 947 библиотек, из которых:
448 библиотек — бесцветных, то есть вполне безопасных в рамках исследования
498 библиотек — синих, обновлен домен недавно
0 библиотек — желтых самая странная категория отсутствует, к счастью
1 библиотека — красная, но мы уже убедились, она безобидна)
Выводы
Я думаю, что 947 библиотек, из которых 1 потенциально зараженная, это ничтожно малый процент для того, чтобы атаковать хоть какое-то весомое число JVM приложений, тем более беря во внимание тот факт, что эта библиотека явно с github и то, что она оказалась безобидной.
Когда Gradle подтягивает библиотеку из публичного репозитория, есть вероятность 50%, что вы попадете в библиотеку, домен которой выкуплен после 1 февраля 2024 года.
Основываясь на цифрах исследования, можно сказать, что в 99,9% случаев библиотеки, скачиваемые из разных репозиториев, одинаковы, а это значит, что порядок объявления репозиториев в build.gradle/pom.xml файлах не имеет значения.
Да, может быть и такая ситуация, что библиотеки уже скомпрометированы и уже давно содержат в себе что-то не хорошее (красное правило), но эта ситуация уже выходит за рамки исследования, так как чаще всего трудно понять, какая библиотека была оригинальной. Это нужно делать отдельное исследование с прикручиванием анализаторов кода, но какой в этом смысл, если хеши библиотек почти всегда одинаковые.
Выходит, что MavenGate не так‑то страшен, как его «малюют».
В конечном итоге никто не мешает использовать самый простой и действенный способ обеспечения безопасности своих приложений на платформе JVM — использовать зеркало пакетов в контуре компании, где работают анализаторы кода, которые смогут наверняка определить, какая библиотека «здоровая», а какая нет.
С результатами исследования можно ознакомиться по ссылке.
Если есть вопросы, пишите в комментариях. Постараюсь ответить :)
amarkevich
в случае использования Maven версия зависимости фикстированная
Maven репозитории не позволяют перезаписывать уже существующий релиз артефакта
использование инструментов вроде dependabot позволяют автоматизировать процесс обновления зависимостей