CryptoHack:Crypto on the Web wp第一部分:JWT
CryptoHack:Crypto on the Web wp第一部分:JWT
- Token Apperciation
- JSON WEB TOKENS说明
- Wp
- JWT Sessions
- JWT 与 ID Cookie
- Wp
- No way JOSE
- Wp
- JWT Secrets
- Wp
- 区别
- RSA or HMAC?
- Wp
- 漏洞产生原因
- JSON in JSON
- Wp
- RSA or HMAC? Part 2
Token Apperciation
JSON WEB TOKENS说明
我的理解: JWT是一种web数据传输的方式,基于json,常用于用户身份认证
- JWT分为三个部分:Header,Playload,Sign,即标头、负载、签名,用符号 ” . “进行分割
- JWT采用base64的编码方式,其中 +, / 被不同的特殊字符所代替,避免URL解析错误
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmbGFnIjoiY3J5cHRve2p3dF9jb250ZW50c19jYW5fYmVfZWFzaWx5X3ZpZXdlZH0iLCJ1c2VyIjoiQ3J5cHRvIE1jSGFjayIsImV4cCI6MjAwNTAzMzQ5M30.shKSmZfgGVvd2OSB2CGezzJ3N6WAULo3w9zCl_T47KQ
Wp
方式一:
在线网站: https://jwt.io/
方式二:
s = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmbGFnIjoiY3J5cHRve2p3dF9jb250ZW50c19jYW5fYmVfZWFzaWx5X3ZpZXdlZH0iLCJ1c2VyIjoiQ3J5cHRvIE1jSGFjayIsImV4cCI6MjAwNTAzMzQ5M30.shKSmZfgGVvd2OSB2CGezzJ3N6WAULo3w9zCl_T47KQ" flag = jwt.decode(s,options={"verify_signature": False}) print(flag)
tips : 关于jwt这个库, python里有俩种, 如果无法运行显示没有decode这个函数, 注意使用pyjwt库, 在安装pyjwt库后的导入仍然是 import jwt
pip install pyjwt
JWT Sessions
JWT 与 ID Cookie
当使用JWT时, 并不像ID Cookie一样需要在服务端创建一个会话对象, 而是由服务器创建JWT中的整个会话对象并发送给浏览器, 这之后只需要读取JWT中的Playload即可对用户进行鉴权。
而为了安全性问题, 服务器会首先进行签名验证, 再读取Playload。
故而很明显可以看到, JWT有利于减轻服务端的荷载.
当客户端从使用一个服务器或资源切换到另一个服务器或资源时,该客户端的会话应该仍然有效。此外,对于大型组织,可能会有数百万个会话。由于 JWT 位于客户端上,因此它们解决了这些问题:任何后端服务器都可以通过检查令牌上的签名并读取内部数据来授权用户。
Wp
JWT所使用的标志头为: Authorization
No way JOSE
说明: 于此开始为JWT的漏洞
Wp
随意输入数据, 先得到一个session(JWT):
接下来做解码, 可以参见第一个挑战, 这里我使用在线网站:
我们的目标是伪造一份JWT使得其中的admin字段为true, 进而成功获取管理员身份.
阅读题目代码发现没有对签名验证方式做限制, 那么我们就可以伪造一份alg为none的JWT:
记得修改playload. 就可以得到答案.
JWT Secrets
Wp
题目说要猜一猜密钥, 那么找到官方文档:
看到示例中使用的密钥是secret, 那么我们试试猜测本题也是secret:
可以看到签名验证通过了, 证明我们的猜想是正确的, 利用该密钥就可以随意进行playload的更改:
print(jwt.encode({"username": "123","admin": 'true'}, 'secret', algorithm='HS256'))
Tips: 这里使用在线网站似乎无法正常生成JWT, 源于密钥长度不足, 也许可以通过绕过前端来实现, 此处笔者没有尝试
另一条路线
当然, 我们可以尝试爆破密钥. 这里我使用utools自带的JWT解码功能, 可以帮助我们爆破长度比较小的密钥:
图中secret项即为密钥
区别
我们查看源代码, 可以发现这题与上一题的区别, 以及为什么本题不适用于no way的解题方法:
可以看到, 在解析JWT时, 系统默认尝试使用HS256, 避免了alg值为none的情况.
RSA or HMAC?
Wp
由于代码中 HS256 和 RS256 使用的是同一份密钥, 那么我们便可以尝试用公钥作为密钥, 使用对称密钥方式(HS256) 进行伪造.
首先获取公钥文件:
将这部分作为密钥生成JWT, 注意 : 这里不再能使用在线网站, 存在版本问题
pk = '-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAvoOtsfF5Gtkr2Swy0xzuUp5J3w8bJY5oF7TgDrkAhg1sFUEaCMlR\nYltE8jobFTyPo5cciBHD7huZVHLtRqdhkmPD4FSlKaaX2DfzqyiZaPhZZT62w7Hi\ngJlwG7M0xTUljQ6WBiIFW9By3amqYxyR2rOq8Y68ewN000VSFXy7FZjQ/CDA3wSl\nQ4KI40YEHBNeCl6QWXWxBb8AvHo4lkJ5zZyNje+uxq8St1WlZ8/5v55eavshcfD1\n0NSHaYIIilh9yic/xK4t20qvyZKe6Gpdw6vTyefw4+Hhp1gROwOrIa0X0alVepg9\nJddv6V/d/qjDRzpJIop9DSB8qcF1X23pkQIDAQAB\n-----END RSA PUBLIC KEY-----\n' print(jwt.encode({"username": "123","admin": 'true'}, pk, algorithm='HS256'))
如果要做的好看一点的话, 要注意换行符的处理, 开头没有, 结尾要有, 如下:
pk = '''-----BEGIN RSA PUBLIC KEY----- MIIBCgKCAQEAvoOtsfF5Gtkr2Swy0xzuUp5J3w8bJY5oF7TgDrkAhg1sFUEaCMlR YltE8jobFTyPo5cciBHD7huZVHLtRqdhkmPD4FSlKaaX2DfzqyiZaPhZZT62w7Hi gJlwG7M0xTUljQ6WBiIFW9By3amqYxyR2rOq8Y68ewN000VSFXy7FZjQ/CDA3wSl Q4KI40YEHBNeCl6QWXWxBb8AvHo4lkJ5zZyNje+uxq8St1WlZ8/5v55eavshcfD1 0NSHaYIIilh9yic/xK4t20qvyZKe6Gpdw6vTyefw4+Hhp1gROwOrIa0X0alVepg9 Jddv6V/d/qjDRzpJIop9DSB8qcF1X23pkQIDAQAB -----END RSA PUBLIC KEY----- ''' //不能有任何多余的换行符
这里我使用的是 2.10.1 版本的PyJwt, 需要修改的文件位于
{你的python路径}\Lib\site-packages\jwt\utils.py
将对应的部分注释掉即可
漏洞产生原因
- 没有分别对 HS256 和 RS256 作密钥区分
- 暴露了公钥
- 版本过低, 没有对密钥进行区分
JSON in JSON
Wp
构造注入参数即可:
其在str处理后, JSON变为:
{"admin": false, "username": "123", "admin":"true"}//str函数为其前后增加双引号
如此便实现了对admin的复写, 最终解出的JSON应为:
{"admin": true, "username": "123"}
RSA or HMAC? Part 2
这也是我写这篇的主要原因, 因为在网上实在难以找到这部分的wp…唯一找到的github上的一篇还是有问题的
Wp
题目情景与RSA or HMAC?是一样的, 唯一的区别是这题并没有给出公钥.
漏洞编号 : CVE-2017-11424
使用工具: jwt_forgery.py, 项目地址:https://github.com/silentsignal/rsa_sign2n
首先我们需要俩份密钥进行分析, 故而分别取不同的username得到俩份JWT:
eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJ1c2VybmFtZSI6ImFkbWluMSIsImFkbWluIjpmYWxzZX0.2tAA3re-8C2EQa0q_WM4F2uqREZR1ygH91Ga-6fCf9SbndFMQ1EDN238pIcyfpjuOWls00DXa3JFv9q29fqo-L0e_M3fqRwltoKoEh1GJ9WSDnwcNs74Dsst0N5_swC8yrYXtsEl45rzS9XnjR2m4YVi-RQqlrIwm5H1i46UzlF6VFFvTZoMCLdSjPBaiwrf9hbmz0GacNyl_xwS8yjlYEzORv7K_klp6zaL1X1PkL5B1SMe2DU9RxGzfg0hYzQPlJmzmegGKojF_FoqnThLqQaBVXMJjMor1eHr1tFJ4VncGuppWu6C0rWwqLsDi04AfLnqO65ZMrPbT9nI_FA5ag
eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJ1c2VybmFtZSI6IjEyMyIsImFkbWluIjpmYWxzZX0.yyvxMNFOqqk9h3UY6NUNV8DXYFEkw9pidonLEWEuHMqtg8bS-VLJ0TQ5hIqiAjDEb_0u1x7PZ8Yuuk7biUHDLb4Pz-Dl_5ApCt4KTZtLTGk8h7AyVuCHNL60iRtPTvO2LU23-wwyb2blM6Lk03mwycN23pKBA4PNVly0yrKQQoxKPzec1KS9_WNLIKskJYGFVegowEdDgMGWbyQZmubeCBgNp_GOxlYW-T2qA55XoXkAMNDrWBgxpR1C0N7tTl3_Gd2mrL2VdMx7uxXeRhvxqvkFLXkyCDY1idy4BL0Qe0vETXBXvxwKMUNRqsxir6Tji9E7HvGyKqcGTMz-QFaECg
命令格式: python jwt_forgery.py {JWT_1} {JWT_2}
注意运行的目录位置
通过上述操作我们就得到了俩个公钥文件, 这里用的是 RS256 的加密方式, 所以我们取尾缀为pkcs1.pem的文件:
-----BEGIN RSA PUBLIC KEY----- MIIBCgKCAQEA7pgbuP6X8XHyFr47ybSr1yT+m++FNn/fUnnKP113bYbcp/Jn4nl5 kiRoK4cgXWv6yYaTxVMSyvLcdDkPsmZ0J4DfBXZSpUKY2QK+oR76nb5fZ+VNUEi0 Iu15GBJdIPj8yEA81OviDNcnXRyDpLBWtXZ1gNJyvoiOj/3DgRcIWz9yJNkze8ln utMOzxobg/o2i9oewa2MJk+MHKUZOOCxoaVfmdczTqDIXdowxnWCTEgWb4SBN+MH Gu+8pWgGqXCioGDPALHRR98CWopHC0z7Via/UXkLNGCjJfdNZiJFnLHG8tATDvDS CDQSg46MARk5Ein4ekVPaNKnjc6INnO01QIDAQAB -----END RSA PUBLIC KEY-----
带入之前那题的脚本就可以解出来了.
另外, 如果想要一步到位, 可以对jwt_forgery进行更改:
找到forge_mac函数:
修改这一句代码即可, 这样在之前运行的脚本中就可以直接得到本题可以使用的JWT:
更多内容可以参考这篇文章: 技术分享:如何在没有公钥的情况下实现JWT密钥滥用
如有错误, 请师傅们在评论区指出, 万分感谢