定义

JWT(JSON Web Token)是一种用来在不同系统之间安全传递信息的标准。

组成

  • Header:是一个JSON,声明令牌类型和签名算法(比如是盖“公章”还是“私章”,用什么算法盖)。

例如:{"alg": "HS256", "typ": "JWT"}

  • Payload:是你真正的信息,也是一个JSON。比如用户ID、用户名、权限角色、过期时间等。

例如:{"sub": "123", "name": "zhangsan", "admin": true}

  • Signature:是一把神奇的锁。它把Base64编码后的Header、Base64编码后的Payload和一個只有服务器知道的密钥(Secret) 混合在一起,通过Header里指定的算法(如HS256)计算出一个签名哈希。

最终形态:一个JWT看起来就是这样,由三部分用点(.)连接起来:
hhhhhh.pppppp.ssssss(分别是Base64(Header).Base64(Payload).Signature)
核心思想:服务器不保存JWT,只靠验证最后的Signature来判断JWT是否合法、是否被篡改。签名离不开密钥。

分类

这里围绕做的ctfshow来进行分类讲解(但是这点类别肯定是不全的)

None算法绕过

JWT标准支持一种叫做 none 的算法。意思是“这个JWT没有签名,你们直接相信我里面的内容就好了”。这本来是用于调试的,绝对不应该在生产环境中使用。
但如果开发人员配置错误,没有在服务器端明确禁用 none 算法,攻击者就可以进行如下操作:
截获一个正常的JWT:hhhhhh.pppppp.ssssss
把头部(Header)里的算法 "alg": "HS256" 改成 "alg": "none"。
把Payload部分改成他想要的内容,比如把 "name": "zhangsan" 改成 "name": "admin",把 "admin": false 改成 "admin": true。
因为算法是none,所以签名部分(Signature)可以直接删掉,或者留空(但通常要把原签名部分去掉,只留hhhhhh.pppppp.)。
把这个修改后的JWT发给服务器。服务器看到算法是none,错误地配置导致它没有验证签名,直接相信了Payload里的内容,攻击者就成功地伪装成了管理员。
如何修复:服务器端必须严格校验算法,明确拒绝任何 none 算法的JWT。

弱口令密钥/密钥爆破

原理:
对于HS256这类对称加密算法,签名和验证用的是同一把密钥(Secret)因此,必须足够复杂,不能被猜出来。
但如果开发人员偷懒,使用了弱密钥,比如:
secret
123456
password
qwerty
攻击者就可以进行“爆破”(穷举攻击)。
他先拿到一个合法的JWT:hhhhhh.pppppp.ssssss。
他知道前两部分(Header和Payload)的内容和签名算法(HS256)。
他编写一个程序,用一个巨大的弱口令字典(包含成千上万常见弱密码),不断地尝试:用字典里的每一个词作为密钥,对已知的hhhhhh.pppppp进行HS256签名计算,看得到的结果是否等于ssssss。
一旦匹配成功,他就猜到了服务器的密钥!这下他就拥有了“刻章机器”,可以伪造任何用户的任何权限的JWT了,为所欲为。
如何修复:使用足够长、足够随机(如通过加密学安全方法生成)的密钥,让爆破在计算上变得不可行。
这里有个工具来着 --> https://github.com/ticarpi/jwt_tool

公/私钥泄露(提权)

对于RS256等非对称加密算法,签名验证使用两把钥匙:一把私钥(Private Key) 由服务器秘密保管,用来盖章(签名);一把公钥(Public Key) 可以发给任何人,用来验章(验证签名)。公钥无法推导出私钥。
这本来更安全,但前提是私钥必须绝对保密。
如果发生以下情况:
开发人员误把私钥上传到了公开的代码仓库(如Github)。
服务器配置错误,导致私钥文件被下载。
公司内部人员泄露。
攻击者一旦拿到了私钥,游戏就彻底结束了。他可以用这个私钥,为他想要的任何Header和Payload生成一个合法的、任何公钥都无法验证通过的签名。他可以伪造“万能通行证”,服务器会完全信任它,因为签名是用它自己的私钥签的。
如何修复:私钥是最关键的敏感信息,必须通过安全的密钥管理服务(KMS)或 vault 来保管,严格限制访问权限,绝不能硬编码在代码里或存放在web目录下。

一些想写的题目

听学长说jwt还有一种打法 就是去年王鼎的一道题目 来看看叭

王鼎杯web1

web的附件找了好久都没找到 这里就复盘思路
访问网站抓包发现jwt外露 简单尝试了一下弱密钥爆破 发现行不通
接着尝试去伪造jwt 这就需要公私钥了
于是我们需要使用工具jwt_forgery 逻辑是先利用RSA签名来恢复模数 一旦n被成功恢复,结合常用的公钥指数e,攻击者就可能得到公钥 在这一体中是可以得到的
项目地址 -->> https://github.com/silentsignal/rsa_sign2n
python jwt_forgery.py [jwt]
得到了公钥就可以反向取私钥
项目地址 -->> https://github.com/RsaCtfTool/RsaCtfTool
python RsaCtfTool.py --publickey publickey.pem --private
有了公私钥就可以顺利伪造jwt提权了 后面套了点musc 但是整体利用jwt提权的过程已经结束了

all in all

看完这题感觉可以放到上面公/私钥泄露这一类 然后王鼎的这题是根据DownUnderCTF 2021 jwt改编的 看了一下思路差不多 但是这两种情况都适用于弱公钥的情况下 有点看运气