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

New JWT weaknesses #392

Merged
merged 14 commits into from Oct 12, 2017

Conversation

2 participants
@tghosth
Contributor

tghosth commented Oct 10, 2017

(relates to issue #364)

Introduction

This is a small commit but it creates two new vulnerabilities by switching to an old version of express-jwt and switching to an asymmetric JWT signing algorithm.

The first is that you can submit a JWT using the "none" algorithm and the application will accept it.

The second is that if you have the public key of the private key used to asymmetrically sign the JWT, you can make the server think that the JWT was actually signed using a symmetric key and therefore generate valid, signed JWTs based on the public key.

Both these vulnerabilities are explained far better here.

Note that it invalidates the existing JWT vulnerability.

Steps to reproduce these vulnerabilities:

"None" algorithm

  1. Login and generate a JWT.

  2. Copy the JWT from the request into the tool at jwt.io

  3. Change the start of the JWT text from "eyJhbGciOiJS" to "eyJhbGciOiJI" to work around a bug in the jwt.io site. The JWT algorithm has now been changed to HS256 instead of RS256.

  4. Change the value of the "alg" parameter on the right hand side from "HS256" to "none".

  5. Make any changes to attributes in the JWT on the right hand side which you want, e.g. change user.

  6. Remove the signature part of the JWT on the left hand side so that the final character is a dot.

  7. Submit the modified JWT. The app should accept the request and act accordingly.

"Asymmetric to Symmetric" bug

This is harder to exploit, mostly because it is very sensitive to the newline characters in the public key. In order to allow exploitation, the public key will need to be available to the end user. Maybe it could be left in the ftp directory. I would strongly recommend leaving it in the code fragment form so to help avoid the newline issues.

  1. Login and generate a JWT.

  2. Copy the JWT from the request into the tool at jwt.io

  3. Change the start of the JWT text from "eyJhbGciOiJS" to "eyJhbGciOiJI" to work around a bug in the jwt.io site. The JWT algorithm has now been changed to HS256 instead of RS256.

  4. Make any changes to attributes in the JWT on the right hand side which you want, e.g. change user.

  5. In the jwt.io window where you are modifying the JWT, go to the browser developer tools (F12 in chrome) and open the js/jwt.js file.

  6. Put a breakpoint on line 77 of this file (key = window.CryptoJS.enc.Latin1.parse(key).toString();)

  7. Make a final change to the JWT attributes on the right hand side which should trigger the breakpoint.

  8. Whilst execution is paused, enter the following into the Chrome Developer Console key = '-----BEGIN RSA PUBLIC KEY-----\r\nMIGJAoGBAM3CosR73CBNcJsLv5E90NsFt6qN1uziQ484gbOoule8leXHFbyIzPQRozgEpSpiwhr6d2/c0CfZHEJ3m5tV0klxfjfM7oqjRMURnH/rmBjcETQ7qzIISZQ/iptJ3p7Gi78X5ZMhLNtDkUFU9WaGdiEb+SnC39wjErmJSfmGb7i1AgMBAAE=\r\n-----END RSA PUBLIC KEY-----'
    (Doing it in this way avoids the newline issues, this took me a really really long time to figure out :( )

  9. Restart execution. A signature should have been added to the JWT text on the left hand side.

  10. Submit the modified JWT. The app should accept the request and act accordingly.

tghosth added some commits Aug 6, 2017

Update from upstream (#1)
* Remove reference of node.js-version specific Docker images

* Update REFERENCES.md

Added my talk at BSidesTLV

* Fixed broken label into LABEL_RECYCLE_QUANTITY

* Remove juice reference (#362)
and reformat markdown files

* Change donation/merchandise label for customized apps
(solves #362)

* Add video, code and scan results
(of @Soluto's integration with ZAP and Webdriver/Selenium)

* Add missing exclamation point

* Add more API backend tests
- app config
- password reset

* Remove unused config variable

* Add "Jawa Script T-Shirt" product

* Update test dependencies

* Fall back to Protractor 4

* Migrate first batch of API tests to frisby v2

* Migrate file serving API tests to frisby2

* Add X-XSS-Protection header absence test

* Migrate complaint API tests to frisby2

* Use actually existing Complaint resource

* Move HTTP request logging to top of router chain

* Adapt to frisby2 URI encoding behavior

* Adapt to JSON non-strict expectations
(see vlucas/frisby#365)

* Set `application/json` content header for POST requests

* Migrate recycle API tests to frisby2

* Migrate feedback API tests to frisby2

* Migrate but disable file upload tests for frisby2
(see vlucas/frisby#372)

* Migrate redirect tests to frisby2

* Make test work by preventing URL encoding
(vlucas/frisby@4842632)

* Migrate challenge API tests to frisby2

* Migrate security question tests to frisby2

* Align `describe` contexts
(now consequently use /rest or /api prefix)

* Migrate security answer tests to frisby2

* Migrate basket item tests to frisby2

* Add test for forward-slash error expectation

* Migrate product tests to frisby2

* Migrate user tests to frisby2

* Mark all disabled tests

* Add disabled tests for password reset use cases

* Enable file upload tests
(see vlucas/frisby#374)

* Drop node.js 4.x support due to rejection by Heroku

* Drop node.js 4 support

* Attempt to fix Heroku semver issues
(https://kb.heroku.com/why-is-my-node-js-build-failing-because-of-an-invalid-semver-requirement)

* Specify node engines as range for Heroku
(this lets 5.x and 7.x appear as supported)

* Specify node engines as range for Heroku
(this lets 5.x and 7.x appear as supported)

* Block tampering attempts on password reset form
(and complete tests of API route)

* Use frisby latest 2.x release

* Migrate to Jest as runner for frisby.js tests

* Avoid using Basket id=1 in API tests
(as it might concurrently be checked out)

* Use Basket id=3 in basket item tests

* Add bower_components to modules for Jest

* Specify testPathIgnorePatterns for Jest

* Fix formatting

* Drop node.js 4.x support
(for seemingly being on bad terms with Jest)

* Drop node.js 4.x from CI jobs

* Remove node.js 4.x from compatibility table

* Soften expectation dependency of UNION SQLi and CSRF tests

* Remove unneeded dependencies

* Remove password check during UNION SQLi for Bender

* Fix styleguide violations

* Add back js-yaml dependency
(needed by Heroku)

* Remove illegal comment

* Add back dottie and fix js-yaml version

@tghosth tghosth referenced this pull request Oct 10, 2017

Closed

JWT alg=none vulnerability #364

@bkimminich bkimminich changed the base branch from develop to jwt_challenges Oct 12, 2017

@bkimminich bkimminich merged commit ecd2f2a into bkimminich:jwt_challenges Oct 12, 2017

2 of 3 checks passed

Node Security 7 vulnerabilities found
Details
continuous-integration/appveyor/pr AppVeyor build succeeded
Details
continuous-integration/travis-ci/pr The Travis CI build passed
Details
@bkimminich

This comment has been minimized.

Owner

bkimminich commented Oct 22, 2017

Hi @tghosth, I just tried to write unit tests for the challenge verification with pre-built tokens from jwt.io built exactly like you specified them here. For Asymmetric to Symmetric" bug I do not get a valid signature from setting the key via console. Could you retry please if it still works for you?

See 0ac15ff#diff-b23f26e8b487351a9699003c329e0890R248 for the token I made that does not work. The test is disabled at the moment with xit.

bkimminich added a commit that referenced this pull request Oct 22, 2017

@tghosth

This comment has been minimized.

Contributor

tghosth commented Oct 22, 2017

I will try and take a look at some point in the next few days...

bkimminich added a commit that referenced this pull request Oct 29, 2017

@bkimminich

This comment has been minimized.

Owner

bkimminich commented Oct 29, 2017

@tghosth, I found the mistake. I checked for RS256 in the challenge verification where I needed to check for HS256. Now it works! Thanks again for those two awesome challenges!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment