CVE-2024-27198: JetBrains TeamCity Authentication Bypass

D2

Администратор
Регистрация
19 Фев 2025
Сообщения
4,380
Реакции
0
Источник


CVE-2024-27198 - уязвимость затрагивающая аутентификаю в CI/CD от JetBrains - TeamCity, получила балл CVSS 9,8(критический).
Уязвимость нашла компания Rapid7 в феврале 2024 года.
Обзор
Принцип эксплуатации уязвимости, заключается в создании URL определённым образом.

Проблема заключается в классе
Код: Скопировать в буфер обмена
jetbrains.buildServer.controllers.BaseController
, который обрабатывает пользовательские запросы, а точнее в том, как он это делает.

Ниже представлена часть кода метода handleRequestInternal из класса BaseController, который осуществляет обработку.
Java: Скопировать в буфер обмена
Код:
public abstract class BaseController extends AbstractController {
  
  
  
    public final ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
        try {
            ModelAndView modelAndView = this.doHandle(request, response);
            if (modelAndView != null) {
                if (modelAndView.getView() instanceof RedirectView) {
                    modelAndView.getModel().clear();
                } else {
                    this.updateViewIfRequestHasJspParameter(request, modelAndView);
                }
            }

Мы видим, что если результат запроса не перенаправляет куда-либо, то будеть вызван метод
Код: Скопировать в буфер обмена
updateViewIfRequestHasJspParamete
, описанный ниже
Java: Скопировать в буфер обмена
Код:
private void updateViewIfRequestHasJspParameter(@NotNull HttpServletRequest request, @NotNull ModelAndView modelAndView) {

    boolean isControllerRequestWithViewName = modelAndView.getViewName() != null && !request.getServletPath().endsWith(".jsp");
      
    String jspFromRequest = this.getJspFromRequest(request);
      
    if (isControllerRequestWithViewName && StringUtil.isNotEmpty(jspFromRequest) && !modelAndView.getViewName().equals(jspFromRequest)) {
        modelAndView.setViewName(jspFromRequest);
    }
}

В данном методе мы можем увидеть, что переменная isControllerRequestWithViewName будет иметь значение true, если и у текущего ModelAndView есть имя, и servlet путь текущего запроса не заканчивается на .jsp.
Злоумышленник может удовлетворить это, запросив URI с сервера, который сгенерирует ответ HTTP 404. Такой запрос сгенерирует servlet путь /404.html, что заканчивается на .html, а не .jsp, следовательно isControllerRequestWithViewName будет иметь значение true.
Далее мы видим, как вызывается метод getJspFromRequest, чей результат записывается в переменную jspFromRequest.
Важно отметить, что getJspFromRequest принимает в качестве аргумента изначальный запрос пользователя, что дает злоумышленнику возможность контролировать содержимое переменной jspFromRequest.

Рассмотрим код метода:
Java: Скопировать в буфер обмена
Код:
protected String getJspFromRequest(@NotNull HttpServletRequest request) {
    String jspFromRequest = request.getParameter("jsp");
      
    return jspFromRequest == null || jspFromRequest.endsWith(".jsp") && !jspFromRequest.contains("admin/") ? jspFromRequest : null;
}

Мы видим, что метод - берёт значение запроса пользователя, оканчивающееся на .jsp и возвращает ответ, если оно не содержит пути /admin.
В конце проверок значение из переменной jspFromRequest передаётся в метод ModelAndView.setViewName. И так как злоумышленник может контролировать аргумент этого метода, результат его выполнения позволяет изменить URL-адрес, обрабатываемый DispatcherServlet, что позволяет злоумышленнику вызывать произвольный эндпоинт.

Хулиганим
Из начала статьи мы можем понять, что:
  • Ведёт на страницу, которой нет(ответ - 404)
  • URI - ведёт на страницу, доступную только аутентифицированным пользователям.
  • Если в запросе не содержится /admin и он заканчивается на .jsp.
Следуя приведённым выше правилам мы можем создать такой URI:
/huligan?jsp=/app/rest/server;.jsp
Тестировать его я буду на специально построенном уязвимом окружении версии 2023.11.3, которое я взял отсюда.

Запускаем контейнер и BurpSuite, после чего захожу по адресу localhost:8111 и настраиваю его(тут сами, это легко).
Видим, что я создал одного юзера с правами админа:
1727189491215.png


Чтож, пора начинать хулиганить.

Для начала тестируем первое условие:
1727189689069.png


Отлично! Первое условие соблюдено, теперь пробуем второе и третье вместе:
1727189779621.png


Здорово! У нас всё получилось и мы смогли забайпасить аутентификацию, но как мы можем развить атаку? Мы можем создать нового юзера с административными правами. Делается это так - мы берём эндпоинт /app/rest/users, соблюдая все вышеизученные правила и указываем специальные headers:
1727190237397.png


BurpSuite говоритт, что всё получилось, чтож проверим:
1727190292998.png


И вправду, я могу увидеть что у меня есть 2 юзера с админскими правами, а также могу залогиниться под каждым.

Итог
В этой статье мы изучили CVE-2024-27198 уязвимость обхода аутентификации в CI/CD от JetBrains, это довольно интересная уязвимость, от которой можно раскрутиться и пробраться внутрь сети, если хорошо знать сам TeamCity.
Надеюсь вам было интересно почитать, данную статью, с вами был Эрмано, всем хорошего времени суток.
 
Сверху Снизу