过程
打开题目链接,是一个登录框,不加验证码,且在注册用户名admin时提示该用户名已被注册,因此爆破也是一种思路。不过根据题目名字中的提示,jwt,且拥有注册入口,注册一个用户先。
注册完用户,通过getFlag按钮,得到提示不是admin用户,在忘记密码的页面,找到flask的secretKey,因此考虑flask的session伪造。
jwt
简要介绍一下jwt。JSON Web Token, JWT。jwt是一种协议,用来解决用户认证的问题。我们熟知的认证方式是session-cookie的形式,客户端向服务端发送用户名和密码,服务端验证后会在当前的会话(session)中保存用户的相关信息(例如:当前用户的权限),并向客户端返回一个session_id,也即客户端的cookie。下次请求时,客户端将通过Cookie传送该session_id,服务端接收到之后,通过session_id来查询对应session,从而获取与用户相关的信息。
这种机制的问题时需要服务端保存session,且session查询需要消耗资源。
因而引入jwt,jwt的设计比较简明,一个三段式的字符串,如下:
使用base64编码,以.
分隔为了三个部分:Header、Payload、Signature。
- Header
header部分用于标识生成Signature的算法。例如图中的,也是默认的HS256:HMAC-SHA256。 - Payload
payload部分存放需要传递的数据,有7个官方字段,当然也可以添加自己的字段,但是因为该部分内容是base64编码的,与明文传输并无二致,避免传输私密信息:
iss (issuer):签发人
exp (expiration time):过期时间
sub (subject):主题
aud (audience):受众
nbf (Not Before):生效时间
iat (Issued At):签发时间
jti (JWT ID):编号
- Signature
使用一个客户端与服务端都知道的密钥对前两部分进行加密,作为一个签名附加在jwt的第三部分。
故而当服务端签发jwt返回给客户端时,客户端可以使用相同的密钥和指定算法,验证前两部分的有效性;反过来,服务端也可以通过此种方法来验证客户端的jwt是否有效*。
flask session伪造
有了密钥,使用https://github.com/noraj/flask-session-cookie-manager/tree/master对session进行伪造。
首先解密可以得到:{'_fresh': True, '_id': '0212d692b0824b514d6eab354c932359356d94eff6bd2dbabe3236c8f95e3081a881f9f142deb7505132b14c04ab8a813bfc3d319348345395132f1947daf8cc', '_user_id': '2'}
,改_user_id为1,重新加密后,替换原有session获得flag。
acquisition
- 学习了flask的session伪造的解法
- 重温了一遍jwt
reference
https://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html
https://github.com/noraj/flask-session-cookie-manager/tree/master
https://blog.csdn.net/Leaf_initial/article/details/131560501
后记
* 这里想到一个问题:对称加密下,客户端也保存有相同的密钥,那么客户端自己也是可以伪造签名的,例如一个非admin的用户,将自己的权限改为了admin,即payload部分内容发生改变,同时他将新的签名进行更新,不就可以伪造jwt了?当然这个过程也有许多细节问题可以用来规避,例如1)如果使用非对称的加密算法,既然客户端只是验证,客户端保存一个公钥即可,jwt的签发由服务端保存的私钥进行2)payload中避免使用如“admin=0”,“admin=1”此类的身份验证策略,也可以避免此种伪造。3)还是把用户的权限控制放在服务端,验证完jwt后通过查询ACL再来决定用户的真正权限。可能对于具体的使用场景、使用流程的认识还不到位,因而有诸如此类的疑惑,后续搞明白了再写一写