HITB CTF 2017-Pasty-writeup
Task
|
|
Solution
先看看基本功能。
注册:
登陆:
写paste,保存:
可以对保存的paste进行下载:
可以看到注册后返回了一大串的字符串,之后写paste保存时即以这个字符串作为身份认证。
刚接触时不知道是啥,就拿去用base64解了一下,虽然解出来后后面出现了一大堆的乱码,不过前面解出来入下:
然后就开始查找关于JWT的资料了。在 https://jwt.io/ 上可以对字符串进行debugger。
JWT由三部分组成。第一部分是头部,原始信息是json格式,包含使用的算法(alg),类型(typ),可选的kids字段,将头部原始信息进行base64编码后得到了第一部分字段:
第二部分是payload,原始信息是json格式,包含一些重要信息,比如本题中是"sub": "chybeta"
,这对应着账号的用户,将这部分原始信息进行base64编码后得到第二部分字段:
第三部分,将第一部分和第二部分字段用点号.
连接后根据第一部分里的alg算法进行签名。在本题中采用的是RS256算法:
在服务器返回给我们的头部信息中,有一个是:
关于kid字段(key ID),它指定了用哪一个key来加密。可以看看这里:What’s the meaning of the “kid” claim in a JWT token?
访问:
可以发现里面有许多pem公钥,我们字段相符合即为3c3c2ea1c3f113f649dc9389dd71b851.pem。
结合头部的alg信息,我们知道它采用的是SH256即sha256签名,这种方式下,发送方(即我们)要用私钥对签名进行加密,接收方(即服务器端)用公钥进行解密,而从头部kid字段知道,公钥的地址实际上是我们可以指定的。所以攻击流程如下:
- 我们先生成公钥私钥对。
- 用公钥私钥在 jwt.io 上进行签名伪造jwt,其中kid字段指明我们公钥的地址。
- 服务器端收到jwt后,用kid字段地址中的公钥进行解密,由于公钥和私钥是我们伪造的,所以解密是能成功的。
- 拿flag。
利用openssl生成公钥私钥对。
公钥:
私钥:
用之前注册的账号写一个paste,内容为公钥
利用下载功能获取到公钥的保存地址,这里为 /api/paste/fcb82912-1c84-4ce6-9713-755d5f142066?raw。
我们想要让服务器端用我们的公钥来加密,所以在伪造jwt的头部的kid字段应为:
但是由前面观察keys文件夹知道,服务器端会把传进来的url后面加上.pem
。即"kid":"keys/3c3c2ea1c3f113f649dc9389dd71b851"
会变成去访问keys/3c3c2ea1c3f113f649dc9389dd71b851.pem
可以在?raw
后面加上一个&
,这样加上去后的.pem
不会变成后缀而是被当成一个参数。访问 http://47.74.147.52:20012/api/paste/fcb82912-1c84-4ce6-9713-755d5f142066?raw&.pem 即访问 http://47.74.147.52:20012/api/paste/fcb82912-1c84-4ce6-9713-755d5f142066?raw ,即我们paste/公钥的保存地址。
接下来用 https://jwt.io/ 来生成伪造的jwt。由于要伪造admin,所以sub字段要修改为admin。在公钥和私钥部分填上我们生成的公钥和私钥,在头部kid字段填上公钥的地址。
得到:
之后在每次操作中,要记得把Authorization字段替换成对应的伪造jwt。
flag: