Summary
The json-web-token library is vulnerable to a JWT algorithm confusion attack.
Details
On line 86 of the 'index.js' file, the algorithm to use for verifying the signature of the JWT token is taken from the JWT token, which at that point is still unverified and thus shouldn't be trusted. To exploit this vulnerability, an attacker needs to craft a malicious JWT token containing the HS256 algorithm, signed with the public RSA key of the victim application. This attack will only work against this library is the RS256 algorithm is in use, however it is a best practice to use that algorithm.
PoC
Take a server running the following code:
const express = require('express');
const jwt = require('json-web-token');
const fs = require('fs');
const path = require('path');
const app = express();
const port = 3000;
// Load the keys from the file
const publicKeyPath = path.join(__dirname, 'public-key.pem');
const publicKey = fs.readFileSync(publicKeyPath, 'utf8');
const privateKeyPath = path.join(__dirname, 'private-key.pem');
const privateKey = fs.readFileSync(privateKeyPath, 'utf8');
app.use(express.json());
// Endpoint to generate a JWT token with admin: False
app.get('/generateToken', async (req, res) => {
const payload = { admin: false, name: req.query.name };
const token = await jwt.encode(privateKey, payload, 'RS256', function (err, token) {
res.json({ token });
});
});
// Middleware to verify the JWT token
function verifyToken(req, res, next) {
const token = req.query.token;
jwt.decode(publicKey, token, (err, decoded) => {
if (err) {
console.log(err)
return res.status(401).json({ message: 'Token authentication failed' });
}
req.decoded = decoded;
next();
});
}
// Endpoint to check if you are the admin or not
app.get('/checkAdmin', verifyToken, (req, res) => {
res.json(req.decoded);
});
app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});
Public key recovery
First, an attacker needs to recover the public key from the server in any way possible. It is possible to extract this from just two JWT tokens as shown below.
Grab two different JWT tokens and utilize the following tool: https://github.com/silentsignal/rsa_sign2n/blob/release/standalone/jwt_forgery.py