不在乎比恨更高级欸
什么是jwt???
JSON Web Token(JSON Web令牌)是一个开放标准(rfc7519),它定义了一种紧凑的、自包含的方式,用于在各方之间以JSON对象安全地传输信息。此信息可以验证和信任,因为它是数字签名的。jwt可以使用秘密〈使用HNAC算法)或使用RSA或ECDSA的公钥/私钥对进行签名,通过JSON形式作为Web应用中的令牌,用于在各方之间安全地将信息作为JSON对象传输。在数据传输过程中还可以完成数据加密、签名等相关处理。
一般用于用户认证(前后分离/微信小程序/app开发)。
虽然可以对 JWT 进行加密,以便在各方之间提供保密性,但是我们将关注已签名的Token。签名Token可以验证其中包含的声明的完整性,而加密Token可以向其他方隐藏这些声明。当使用公钥/私钥对对令牌进行签名时,该签名还证明只有持有私钥的一方才是对其进行签名的一方(
签名技术是保证传输的信息不被篡改,并不能保证信息传输的安全
)。
jwt结构:
在其紧凑的形式中,JWT由以点(.)分隔的三个部分组成,它们是:
- 标头(Header)
- 有效载荷(Payload)
- 签名(Signature)
token格式: 如:xxxxx.yyyy.zzzz
eyJhbGciOiJOb25lIiwidHlwIjoiand0In0.W3siaXNzIjoiYWRtaW4iLCJpYXQiOjE3MTE0Mzc2NDAsImV4cCI6MTcxMTQ0NDg0MCwibmJmIjoxNzExNDM3NjQwLCJzdWIiOiJ1c2VyIiwianRpIjoiNGQzMjYxMmYwMmU2MDc1YjE3MThmMTIyZjBhNTBhOGEifV0
然后用一个网站解码:
Header:有令牌的类型和所使用的签名算法,如HMAC、SHA256、RSA;使用Base64编码组成;
{ "alg":"Hs256",none #默认alg是未加密的,加上none后不加密 "typ":"JwT" }
Payload :有效负载,包含声明;声明是有关实体(通常是用户)和其他数据的声明,不放用户敏感的信息,如密码。同样使用Base64编码 ,这些有效信息包含三个部分
标准中注册的声明
公共的声明
私有的声明
标准中注册的声明(建议但不强制使用):
iss:jwt签发者
sub:jwt所面向的用户
aud:接收jwt的一方
exp:jwt的过期时间,这个过期时间必须要大于签发时间
nbf:定义在什么时间之前,该jwt都是不可用的.
iat:jwt的签发时间
jti:jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
web345
查看源码后
抓包看看,发现了jwt
解密:
可以发现alg是None算法,无签名认证
我可以继续再JWT.io里构造JWT
将sub改为admin再加密。
把alg改为HS256,sub为admin
取到1d. 然后在发送,
web346
法一:
直接访问/admin/发现访问不了
访问/ 发现了JWT
解一下
发现alg为HS256加密算法,但是我们可以把改为none,把sub改为admin。
然后发送
法二:用jwtcrack爆破
web347
提示弱口令,开爆!!!!
hashcat -a 0 -m 16500 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhZG1pbiIsImlhdCI6MTcxMTQ0NTQ5MCwiZXhwIjoxNzExNDUyNjkwLCJuYmYiOjE3MTE0NDU0OTAsInN1YiI6InVzZXIiLCJqdGkiOiJmZDJhYWJiODI5NzkwOGQ2NThlY2NkMGZiMzVmMDEwYSJ9.Au9Gjx4WjL8OXAorx6SUNS4fvWeUfVhrIV8UPOa9RDI ../jwt.secrets.list
再把sub改为admin 密钥输入123456。
web348
题目说要爆破!!!
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部分
当用户访问主页时,服务器读取当前工作目录下的
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",表示用户无权访问此标志。
访问呢一下得到了公钥和私钥。
服务器利用私钥生成jwt,利用公钥解密jwt,所以我们只要有私钥然后自己重新生成就可以
上脚本!!!
import jwt
public = open('private.key', 'r').read()
payload={"user":"admin"}
print(jwt.encode(payload, key=public, algorithm='RS256'))
post提交就好了
web350
给了一个源码,
我们发现了公钥key,
参考JWT漏洞(ctfshow345-350)_yii 使用jwt key泄漏-CSDN博客,用这个网站
https://lightly.teamcode.com运行脚本
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)
然后上传!!!
这里我们用到将RS256算法改为HS256(非对称密码算法=>对称密码算法)
HS256算法使用密钥为所有消息进行签名和验证。
而RS256算法则使用私钥对消息进行签名并使用公钥进行身份验证。
如果将算法从RS256改为HS256,则后端代码将使用公钥作为密钥,然后使用HS256算法验证签名。
由于攻击者有时可以获取公钥,因此,攻击者可以将头部中的算法修改为HS256,然后使用RSA公钥对数据进行签名。
这样的话,后端代码使用RSA公钥+HS256算法进行签名验证