Hackergame2020 普通的身份认证器 Write-up
关于 JWT
毕竟是面向搜索引擎的比赛嘛 多搜搜
下面的链接给出了介绍了 JWT 与给出了几种 JWT 的攻击方法
回到题目
打开题目页面只有两个按钮,按 F12 打开开发者面板,切到网络
选项页
点击以 Guest 登陆
,观察网络请求
将Hg-Token: 比赛 Token
加入Headers
,POST
到 /token
来进行登陆
得到access_token
和token_type
用base64
解码登陆后的返回的 JWT,可知此 JWT 使用 RS256 加密
根据上文所提到的攻击方法,对待 RS256 加密,只有
- 将加密降级为 none
- 将加密降级为 HS256
手动或者使用 jwt_tool 来进行构造一个新的headers
和payload
进行第一种加密的攻击,失败
然后呢
既然第一种方法不行 那么就只剩下将算法改为 HS256,以公钥作为密钥进行降级攻击了
但是很明显手上所拥有的信息中没有公钥
这时候读一下页面的源码,看看有没有什么收获
发现此网站由 FastAPI 做后端,于是开始搜索 FastAPI 与 JWT 的相关内容
无有用信息
于是只搜索 FastAPI 的相关攻击 于是搜到这样一篇
发现里面提到了 FastAPI 有交互式的 API 页面,于是马上回到比赛页面,进入/docs
点开/debug
,execute
后出现了PUBLIC_KEY
这时候就可以开始构造攻击脚本了
构造攻击脚本
首先要通过登陆来获得没有过期的 token(其实不必须
1 | REQUEST = requests.Session() |
对其access_token
进行base64
解码,将payload
部分解析成字典,记得要补全等号才能正常解析
1 | payload_base64 = bytes(access_token.split('.')[1], encoding='utf8') |
然后将其payload
里的sub
的值改成 admin
1 | payload['sub'] = 'admin' |
同时别忘了要获取公钥
1 | def get_public_key(): |
最后就是将这套构造后的payload
使用 HS256 算法,公钥作为密钥来进行加密
1 | jwt_token = str(jwt.encode(payload, key=public_key,algorithm='HS256'), encoding='utf8') |
将这个JWT
加入到headers
里进行请求
1 | headers['Authorization'] = f'{token_type} {jwt_token}' |
等等,报错了
如无意外,直接运行会报错
1 | jwt.exceptions.InvalidKeyError: The specified key is an asymmetric key or x509 certificate and should not be used as an HMAC secret. |
因为一般版本的 PyJWT
库会对密钥进行检验
会过滤掉以-----BEGIN RSA PUBLIC KEY-----
此类开头的的密钥
结合第一篇文给的建议和题目给的提示
1 | pip3 install pyjwt==0.4.3 |
装好这个版本的PyJWT
库后再跑一遍就可以得到 flag 了
后续
其实根据FastAPI JWT
搜索出来的 FastAPI 的文档里有个小细节
完整的攻击脚本
1 | import jwt |