前言
随着web应用的日渐复杂化,某些场景下,仅使用Cookie、Session等常见的身份鉴别方式无法满足业务的需要,JWT也就应运而生,JWT可以有效的解决分布式场景下的身份鉴别问题,并且会规避掉一些安全问题,如CORS跨域漏洞,CSRF漏洞等。
JWT简介
JWT即Json Web Token的缩写,顾名思义,是Token的一种。它常被用来在向服务器发起请求时用作身份认证。使用JWT作为身份认证的优势在于:它不需要在服务端去保留用户的认证信息。仅需要对该Token正确性进行校验即可,这就意味着基于token认证机制的应用,不需要去考虑用户在哪一台服务器登录了,为应用的扩展提供了便利。
新技术带来便利的同时也会带来新的安全问题,如果JWT本身安全存在问题,那么整个身份认证机制就会得不到保障。
JWT由三部分组成,类似于xxx.yyy.zzz
,前两部分是base64编码的内容,第三部分是加密的签名部分
-
第一部分被称为header,会说明字符串的类型以及加密方式,当然还会包含其他一些字段,在介绍JWT安全问题时会涉及到部分。
-
第二部分被称为payload,包含用户的身份id,是否是管理权限等字段,这部分中的相关字段可以根据实际情况自行定义。
-
第三部分是加密部分,对前面的“xxx.yyy”用头部中声明的加密方法进行加密,保证JWT的完整性。
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
JWT常见安全问题
JWT相关CVE漏洞
下面是JWT相关的一些CVE漏洞
(CVE-2015-2951) alg=none签名绕过漏洞
(CVE-2016-10555) RS/HS256公钥不匹配漏洞
(CVE-2018-0114)密钥注入漏洞
(CVE-2019-20933/CVE-2020-28637)空白密码漏洞
(CVE-2020-28042)空签名漏洞
弱签名算法(无算法)
更改头部中声明的加密算法
例如将下面的HS256更改为none,可以只篡改前两部分,而不改变第三部分签名的情况下进行绕过,或者删除第三部分,但是记得保留“.” 这个符号,以保证符合JWT的格式
存在这种漏洞一般是第三方JWT库存在问题,或者是代码编写错误,生成JWT的代码中使用了none,如下所示
弱密码
如果JWT使用HS系列(对称加密)的加密方法,而且使用较为简单的Key进行加密,那么使用工具就极有可能能够得到key,之后就可以随意的篡改JWT了。
常用的工具
1.jwt_tool
2.hashcat
使用工具破解下面的JWT
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.dQknU80__AHPVDmZPFavWdML1XKrVYPLeOXNMo
利用kid标头
在JWT的头部中,除了存在typ 以及alg之外,还可以存在其他内容。KID 标头是一个密钥标识符。kid是RFC标准中用来表示密钥存储位置的地方,服务端可以根据这个提示来寻找解密密钥。
根据代码的编写情况,可能存在以下问题
- SQL注入
- 目录遍历
- 命令执行
SQL注入
这种情况是密钥存储在数据库中,使用kid字段查询相关内容,代码如下所示
目录遍历
kid的值表示的是密钥存储的文件,通过构造payload,就可能会造成目录遍历漏洞
命令执行
kid的值会被带入到要执行的命令之后,代码如下所示
利用JKU标头
jku头是用来获取jwks(json web keys)文件的,这是一个json文件,用来指定获取密钥。我们可以简单地托管我们自己的 JWKS 文件并将他的 jku 指向我们的服务器。
出现此问题的主要原因是应用程序必须解码令牌并使用它来获取 jwks,以便它可以构建公钥并验证令牌。
因此,如果应用程序在没有任何验证的情况下简单地信任 jku,那么这可能会被滥用。
jwks 文件的格式
利用步骤如下
- 创建公私密钥对
openssl genrsa -out private.pem 2048
openssl rsa -in private.pem -outform PEM -pubout -out public.pem
- 创建我的jwk 文件,通过在线工具 https://russelldavies.github.io/jwk-creator/ ,
public Key Use 选择 sigining,
Algorithm 选择JWT头部中声明的算法
Key id与 头部中声明的一致
然后将生成的公钥的值粘贴框中
- 修改并保存JWKS文件,根据原来JWKS文件中的kid的值,替换对应的部分,用生成jwk替换掉原来的部分,然后就可以篡改JKU头指向我们托管的服务器,以及payload中对应的值,然后进行利用公钥签名,向服务器发送我们构造的JWT即可,可以使用在线工具jwt.io,或者python以及java代码利用私钥生成篡改的JWT。
易受攻击代码示例
利用 X5C 标头
x5c 标头包含可用于验证JWT的 x509certificate/x509certificate 链。正常流程是提取证书中的公钥,然后验证JWT,如果信任x5c中的值,而不进行验证,那么我们可以生成自己的证书和然后用私钥签名篡改后的内容即可。
攻击流程
- 创建证书并提取公私密钥
openssl req -x509 -nodes -days -newkey rsa:2048 -keyout attacker.key -out attacker.crt
openssl x509 -pubkey -noout -in attacker.crt > publicKey.pem
2.篡改令牌并用您的 x509 证书代替 x5c 声明。
3.修改token,用私钥签名。
易受攻击代码示例
利用X5U标头
x5u标头的问题与juk的问题一致,只不过是地址指向的是存储证书的位置,它用于从 Web 服务器获取 x5c 证书,该证书将用于从中创建公钥并验证令牌。
利用流程
- 创建证书和公私 rsa 密钥对
openssl req -x509 -nodes -days -newkey rsa:2048 -keyout attacker.key -out attacker.crt
openssl x509 -pubkey -noout -in attacker.crt > publicKey.pem
2.托管证书
3.篡改token并用私钥签名
易受攻击代码示例
其他
exp标头
除了上面的问题,如果头部中没有exp标头声明JWT的过期时间,那也是存在风险的,一旦获得该令牌,那么就可以重复使用。
跨服务中继攻击
三个组件,下面两个可以会向用户发送JWT令牌,然后令牌服务在签发令牌时不进行必要的验证,那么可能两个应用在具有相同的用户名时,签发相同的令牌,那么就可以替代其他用户登录到另外一个应用中。
实验部分
https://portswigger.net/web-security/all-labs
未验证签名
成功登录后,可以看到获得的Cookie内容就是JWT格式的,
eyJraWQiOiJhZThiOTNhMC01ZWJjLTQzZWYtOWY3My04ODVmMzY1NzdjZWEiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6IndpZW5lciIsImV4cCI6MTY3NjU1NjMxOX0.LM-PYrwNfxoSYvh13Fx4iWP46dYNPhNrq_X1PbVH4JBdtAwolKEIu5ICe_XLUI668CKgwTrlcLzQq8g7gsVvyAttwJKpdfICI-Mncn1CRx6vkxBPxrYhOniRR9G3b_dSwWuuyrpHPV2Zncl8_6wrRO1lB5Yd0zO4TZhATv5hq60iO8F75kRSyD8WnllvNOJVUtTPatmdkq-G8R-bJ0PPlhsXzCXRuWKOlkKl5nyE_aF-tiCAryK6cN_u5pQlc4fYtPds6fnxrPtp7fKPFHCoUSUG7HaA48WqGF603uiKdtLbtia0eEwtSjfT_Wk15Blau4wcGwzCnHeF4Tp2SxWSpA
因为该问题是程序不会对签名部分进行任何验证,因此直接修改payload部分,然后任意输入私钥生成JWT即可
再次构造payload
访问成功,之后可以发现删除用的URL,成功删除用户。
无算法漏洞
- 登录后获得Cookie中的JWT
- 然后将头部中的alg更改 none,payload中的用户名替换为administrator,base64编码后替换原来的部分,然后删除签名部分,记得保留点号,保证格式正确,然后就按照第一个实验中的步骤删除用户即可。
使用弱密钥加密
-
成功登录获得到JWT后进行解码查看,可以看到JWT使用的HS256,这个是对称加密算法,存在使用所密钥的该路,可以使用jwt_tool,等工具尝试对密钥进行猜解。
-
破解的密钥结果为secret1,那我们就可以对JWT进行篡改了。
-
使用jwt.io网站生成JWT,然后按照上面两个实验的步骤删除用户即可。
JWK标头注入
安装bp扩展 JWT Editor,然后使用JWT Editor Keys生成RSA密钥对,点击确认
在JSON Web Token选项卡 的底部,单击Attack,然后选择Embedded JWK。出现提示时,选择您新生成的 RSA 密钥并单击确定。
jku标头注入
-
使用扩展生成RSA密钥并保存,然后选择Copy Public Key as JWK,并按照JWKS文件的格式保存到攻击者的服务器。
-
添加jku头,值为对应jwks文件对应的url
-
将生成的kid替换原来的kid,用来标识用户的用户名,然后选择对应的密钥进行签名,之后不走与上面的实验一致。
利用kid标头绕过
上面的知识部分有讲过kid是用来帮助寻找密钥的,有一种情况就是保存在指定路径的某个文件中,文件名是kid的值。
攻击这个靶场时,我用windows上面的burp怎么样都没办法进行签名,怀疑时burp版本问题,换了个版本也不行,打开虚拟用kali里面的burpSuit,就成功了。
- 首先修改kid的值,为…/…/…/…/…/…/dev/null,然后将sub修改为administrator。
2.生成密钥,然后保存,记得将k的值修改为AA==,其实也就是空,
混淆算法
这个漏洞应该是在程序中没有指明算法导致的问题。
1.首先访问jwks.json文件,将keys的值,也就是框起来的部分,粘贴出来
2.然后使用JWT Editor的插件,生成RSA密钥,选择生成JWT的,生成之后,将上面的值替换掉里面的内容,然后点击确定,保存
3.然后对保存的密钥右键,copy as public pem,去进行base64的加密。然后选择生对称加密的密钥,将加密后的值替换到k中,然后保存
4.然后,将加密算法更改为HS256,用户名标识改为administrator,然后就有管理员权限了,之后的步骤就是删除用户,没什么好写的。
参考
https://www.freebuf.com/articles/web/303200.html
https://www.jianshu.com/p/576dbf44b2ae
https://zhuanlan.zhihu.com/p/86937325
https://medium.facilelogin.com/jwt-jws-and-jwe-for-not-so-dummies-b63310d201a3
https://systemweakness.com/hacking-jwt-d29f39e202d5
https://lazyhacker.medium.com/jwt-vulnerabilities-list-simple-explanation-134c4dcc5e61