Недавно мы опубликовали статью о выявлении вредоносных пакетов в Python Package Index и с тех пор активно используем разработанный нами сервис для анализа проектов. Сегодня хотим поделиться с вами интересным наблюдением, связанным с накруткой репутационной статистики в проектах. Как нечаянно улучшить репутацию своего проекта, насколько распространена эта проблема и как в автоматическом режиме обнаружить подобные манипуляции — читайте под катом.

Это вторая часть цикла, посвященного PT PyAnalysis — сервису для выявления подозрительных и вредоносных Python-пакетов. Перед прочтением рекомендуем ознакомиться с нашей предыдущей статьей «(Не)безопасная разработка: как выявить вредоносный Python-пакет в открытом ПО»????

Индикаторы популярности проекта в репозитории PyPI

Изобретать свои собственные велосипеды — это увлекательное и очень важное занятие в жизни каждого разработчика. Но иногда то ли вдохновения не хватает, то ли сроки поджимают, и человек отправляется на поиски готового проекта, который решает поставленные задачи. Например, хочется вам найти библиотеку, которая будет работать как requests, но с кэшированием запросов и поддержкой TTL. Стоит только воспользоваться поисковиком репозитория Python Package Index, с которого вы по умолчанию качаете пакеты методом «pip install …», и по запросу requests cache находятся сразу несколько пакетов на выбор:

  • django-in-request-cache

  • django-request-cache

  • requests-cache

  • requests-cache-latest

  • requests-etag-cache

  • requests-filecache

  • requests-filecache

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

Рисунок 1. Пример популярного пакета
Рисунок 1. Пример популярного пакета

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

Статистика проекта на GitHub — один из наиболее популярных и доступных способов оценить качество проекта и доверие к нему со стороны сообщества. Однако это работает только в случае добросовестных разработчиков пакетов. Исследуя вредоносные пакеты, мы часто замечаем, что злоумышленники иногда «подкручивают» статистику своих проектов в PyPI, благодаря чему они становятся более привлекательными в глазах будущих жертв. Мы решили узнать, насколько серьезна эта проблема.

Взгляд со стороны публикующего пакеты

Администраторы Python Package Index постарались сделать так, чтобы добавление пакета на PyPI.org не вызывало затруднений: вам нужно всего лишь создать аккаунт в сервисе, после чего загрузить релиз пакета через консольную утилиту twine. Для дополнительного удобства есть подробная инструкция, которая разъясняет все возможные подводные камни в этом процессе: Python Packaging User Guide: Packaging Python Projects.

При изучении туториала по созданию пакета на PyPI.org в разделе «Configuring metadata» необходимо создать pyproject.toml со следующим содержанием:

[project]
name = "example_package_YOUR_USERNAME_HERE"
version = "0.0.1"
authors = [
  { name="Example Author", email="author@example.com" },
]
description = "A small example package"
readme = "README.md"
requires-python = ">=3.7"
classifiers = [
    "Programming Language :: Python :: 3",
    "License :: OSI Approved :: MIT License",
    "Operating System :: OS Independent",
]

[project.urls]
"Homepage" = "https://github.com/pypa/sampleproject"
"Bug Tracker" = "https://github.com/pypa/sampleproject/issues"

В этом разделе вам предлагается изменить только название проекта, сохранив остальную информацию.

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

Рисунок 2. Привет, Хабр!
Рисунок 2. Привет, Хабр!

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

Рисунок 3. Пример вредоносного пакета
Рисунок 3. Пример вредоносного пакета
Рисунок 4. Пакет устанавливает майнер
Рисунок 4. Пакет устанавливает майнер

Статистика

Наш отдел исследования угроз ИБ начал наблюдение за PyPI.org год назад, в марте 2022. Мы собрали информацию не только по всем существующим проектам на PyPI, но и по тем, которые были удалены в течение этого года. В анализе постараемся не прибегать к аудиту самого кода, руководствуясь только метаинформацией, которую можно получить на странице проекта: вредоносная начинка может быть разной в зависимости от задач злоумышленника (о задачах мы писали в прошлой статье), а нам интересно посмотреть именно на данные, не зависящие от логики проекта.

Рисунок 5. Рейтинг репозиториев GitHub по использованию в качестве домашней страницы в проектах PyPI
Рисунок 5. Рейтинг репозиториев GitHub по использованию в качестве домашней страницы в проектах PyPI

Как видно на диаграмме выше, самым популярным репозиторием является sampleproject. Это неудивительно: он принадлежит Python Packaging Authority и используется в вышеупомянутой инструкции по созданию пакетов.

Среди проектов, которые ссылаются на sampleproject, каждый пятый имеет в названии «test», «hello» или «example». Это свидетельствует о том, что они, вероятно, были размещены пользователями, которые учились публиковать пакеты. Встречаются пакеты, названия которых были заняты по принципу, аналогичному киберсквоттингу (регистрации доменов, имя которых похоже на название существующей компании или торговой марки, к которой лицо, регистрирующее домен, не имеет отношения): gptcloud, scancer, onelibrary, onetrees, scfl. Кроме того, бывают случаи явного тайпсквоттинга (использования названий пакетов, схожих с оригинальным, в надежде «протроянить» опечатавшегося разработчика): pandass, google.com, source_s3. Попадаются и привлекательные предложения: cash_app_money_generator_2022_updated_free_cash_app_hack_no_verification_1_6yd3wc, google_play_gift_card_redeem_code_generator. В последнем случае примечательно, что такие фишинговые аккаунты легко выявить не только по количеству разделителей, но и по переиспользованию полей метаинформации (например, везде указан один и тот же электронный адрес для связи или одна и та же домашняя страница):

Рисунок 6. Креативность создателя пакетов поражает воображение
Рисунок 6. Креативность создателя пакетов поражает воображение

По результатам наблюдений в течение года десятая часть пакетов, созданных с домашней страницей sampleproject, была удалена. Среди них есть как легитимные пакеты, так и вредоносные. В качестве примера вредоносного пакета можно привести tiktok8 (троян, скачивающий полезную нагрузку), gardenscapes_hack_coins_free_working_2022 (adware, открывающий сайт посредством selenium), kfaction (стилер учетных записей Discord).

Выделим также репозитории GitHub, в которых велико количество удаленных пакетов PyPI:

  • gmyrianthous/example-publish-pypi (45 из 110 пакетов — 41%). Еще одна инструкция по созданию пакетов.

  • psf/requests (74 из 80 пакетов — 93%). Официальный репозиторий проекта requests. Здесь есть интересная деталь: почти все удаленные пакеты являются вредоносными и имеют названия, мимикрирующие под название искомого: requst, requestts, reeuests, request и т. д. Это проявление ранее упомянутой техники typosquatting.

  • kotko/bravado-decorators (40 из 41 пакета — 98%). Ситуация тоже примечательная: это часть активности исследователя безопасности kotko. В некоторых его пакетах происходит сбор информации о системе вместе с внешним IP-адресом с последующей отправкой на сервер. А это уже нежелательная активность для пользователя.

Как PT PyAnalysis обнаруживает переиспользование метаинформации

Рисунок 7. Статистика по связям между проектами на PyPI и репозиториями GitHub
Рисунок 7. Статистика по связям между проектами на PyPI и репозиториями GitHub

Выявление случаев StarJacking — задача, которую можно решать шаг за шагом. Сперва выделим среди чуть менее чем 450 тысяч пакетов те, для которых не указана домашняя страница: так отсеется треть пакетов. Еще у 4% от общего числа пакетов домашняя страница показывает ошибку 404, поэтому технически StarJacking не работает: звезды не импортируются, обмана практически не происходит (подробнее об этом в разделе «Другие интересные наблюдения»).

Далее сложнее. Проект указывает на рабочую ссылку на GitHub. Для того чтобы подтвердить связь, она должна быть двусторонней. Можно считать, что репозиторий GitHub «признал» пакет PyPI, если произойдет одна из двух ситуаций:

  1. В установочных файлах Python-проекта в репозитории (setup.py, setup.cfg, pyproject.toml или Pipfile) есть явное указание имени пакета — PyPI.

  2. В файлах документации репозитория указано, что проект можно установить с помощью команды «pip install название_проекта_на_pypi».

Мы не можем полагаться на одинаковые никнеймы разработчика (сложно напрямую подтвердить, что аккаунты на PyPI и GitHub принадлежат одному и тому же человеку; подробнее в разделе «Другие интересные наблюдения») или названия проектов, так как злоумышленник относительно легко может их скопировать.

Получается, если пакет на PyPI ссылается на репозиторий, не являющийся проектом на Python (например, пакет является API-клиентом к сервису на другом языке программирования), то проверить связь без участия человека невозможно. По нашей статистике, доля таких пакетов — 1%. Об их опасности мы можем лишь аккуратно предупреждать пользователей, так как большая часть из них легитимная. Между тем, никто не мешает злоумышленнику ссылаться на них, притворившись, скажем, SDK. Поэтому за такими пакетами мы пристально наблюдаем.

Рисунок 8. Даже если этот разработчик является автором пакета «broken», репозиторий об этом не знает. Злоумышленник может создать оба пакета без особых усилий
Рисунок 8. Даже если этот разработчик является автором пакета «broken», репозиторий об этом не знает. Злоумышленник может создать оба пакета без особых усилий

Часть пакетов, которые ссылаются на «не свои» репозитории, можно обелить с помощью транзитивной связи пакетов через одного и того же разработчика:

Рисунок 9. Отслеживание связности пакетов через общего разработчика
Рисунок 9. Отслеживание связности пакетов через общего разработчика

Здесь «dream-sdk» обеляется через подтвержденный пакет «dream-core», который связан с им общим разработчиком.

Считая, что присутствие автора в обоих пакетах является признаком, по которому можно обелить пакет, в котором мы сомневаемся, нам удалось вывести из-под подозрения почти 20 тысяч пакетов (5% от общего числа).

Что представляют собой 4% подтвержденных пакетов с использованием StarJacking

Мы выявили причины, по которым пакеты следует отнести к категории не вызывающих доверия. К счастью, большая часть из них попала в эту категорию по ошибке самого разработчика, а не из злого умысла.

Небольшая доля пакетов с использованием StarJacking (5,9%, 1153 случая) представляет собой копирование метаинформации из популярных Python-пакетов. Среди «жертв популярности» прослеживается pyscaffold (с явно подозрительными подражателями: ceritfi, pycparsre, reqjests), poetry (juwpyter), certifi (bettercolor, virtualenvy, reqyests). Но в большинстве случаев пакеты выглядят как форки и просто тесты возможностей.

С оставшимися случаями StarJacking все тоже не радужно: они представлены либо уже известными нам результатами прохождения туториала по sampleproject (который, как бы неожиданно это ни звучало, не входит в список популярных пакетов, скачиваемых с PyPI.org), либо автоматически созданными пакетами-пустышками, не имеющими никакой подозрительной активности. К таким результатам автоматизации можно отнести, например, пакеты пользователя alexjxd:

Рисунок 10. Завидую его продуктивности :)
Рисунок 10. Завидую его продуктивности :)

Другие интересные наблюдения

Аномальное число «битых» страниц на GitHub

Мы заметили, что среди множества пакетов PyPI с несуществующими репозиториями GitHub заметная часть принадлежит одному-единственному аккаунту:

Рисунок 11. Доли среди наиболее продуктивных разработчиков с «битыми» названиями
Рисунок 11. Доли среди наиболее продуктивных разработчиков с «битыми» названиями

Разработчик wizardforcel очень продуктивен: с 2020 года он участвовал в 13 000 проектов, часто создавая по несколько десятков пакетов в час. Название подавляющей части из них не несет никакого смысла, но некоторые имеют свойство вводить в заблуждение: cdndrive, re_for_beginners (передаем привет автору книги), kubernetes_aws_shouce.

Такое поведение нельзя оставлять без внимания. Мы уведомили об этом пользователе администраторов Python Package Index, и они удалили его и все его достижения :)

Но ответ не заставил себя долго ждать! У wizardforcel появился преемник — apachecn. Он пересоздал 9 проектов wizardforcel и не показывает признаков спам-активности. Полагаем, что разработчик проводил понятный только ему эксперимент, но удаление его наследия подтолкнуло его к тому, чтобы прекратить свою деятельность, оставив только нужные пакеты.

Рисунок 12. Наши ощущения в ходе исследования wizardforcel / apachecn
Рисунок 12. Наши ощущения в ходе исследования wizardforcel / apachecn

Непоказательность email-адреса для сопоставления разработчиков из разных сервисов

В рамках регистрации GitHub и PyPI требуется подтверждения почты. Однако в карточке проекта берется email не из профиля разработчика, а тот, который пользователь указал в метаинформации. С одной стороны, это логично: если разработка ведется от имени компании или у продукта есть отдельный ящик электронной почты, то предпочтительнее указать его. В профиле пользователя email также не отображается. Таким образом, мы не можем доверять email, указанному в проекте PyPI, так как его может скопировать злоумышленник.

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

Заключение

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

В этой статье мы рассказали о проблеме StarJacking в PyPI.org — злоупотреблении импортируемой статистикой о репутации проекта для создания более привлекательного образа и введения пользователя в заблуждение.

Копирование солидного количества звезд на пакеты с непонятными названиями не несет никакого логического смысла, так как пользователь вряд ли установит пакет «gggggghghghghghghghfyrtfyuhgjuh» или «h8shdf89d» (настоящие пакеты, старджекинг на requests), даже если у него более сорока тысяч звезд. Но, комбинируя эту технику c typosquatting или придумывая более убедительные названия, можно добиться более привлекательных результатов: периодически выходят новости о появлении пакетов с названиями вида «requeste», «fast_http», «websocket_cli». Но в нашем сердце навсегда останется пакет «rickquests», павший жертвой борьбы с StarJacking:

Рисунок 13. Не судите книгу по обложке ;)
Рисунок 13. Не судите книгу по обложке ;)

По результатам исследования мы добавили в сервис PT PyAnalysis предупреждение об использовании разработчиком техники заимствования звезд из репозитория, который по факту ему не принадлежит. Это должно послужить мотивацией для наших пользователей на всякий случай усомниться, стоит ли доверять используемому проекту. Злоупотребление статистикой проекта должно привлекать повышенное внимание к проекту, даже если это сделано не нарочно: невнимательность разработчика к метаинформации проекта может являться индикатором низкой культуры кода.

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


Станислав Раковский

Старший специалист отдела исследования угроз ИБ, Positive Technologies

Комментарии (0)