jwt伪造

本文最后更新于:2024年4月6日 下午

这次周末打的downunderctf,发现了点新奇的题。

image-20220928083455787

拿到手看到只有登录注册功能,扫了一下路径也没有发现源码,于是我当场根据经验认定为这是个二次注入,在那注了半天全转译了。

后面看了writeup才了解到这是个jwt伪造。随意注册个账号登录查看cookie,发现如下形式

1
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmcmVzaCI6ZmFsc2UsImlhdCI6MTY2NDMyNTQwNywianRpIjoiMzA3MDM4OWYtNzEzNS00YjEyLTk3NDctY2I4MWNkYzQxMjIxIiwidHlwZSI6ImFjY2VzcyIsInN1YiI6NDAsIm5iZiI6MTY2NDMyNTQwNywiZXhwIjoxNjY0MzI2MzA3fQ.0lTpWgZnpD65gXYaCygCsROp9-DHh_7hsCk0IrLuMhE

jwt一般由三段字符串组合而成,中间以.连接

第一部分称为header,第二部分是payload,第三部分是签名,这些数据是base64过后的,在知道secret后也能伪造签名。

可以通过jwt.io来解析jwt,如上面的jwt就像这样

image-20220929141106670

可以看到header里说加密方式是hs256,payload里的其他信息是jwt规范中建议加上的,真正有用的信息是sub:40大概就是说我们的用户id是40,也就是说如果能伪造签名我们就能实现任意用户登录。

这里提到一个工具https://github.com/brendan-rius/c-jwt-cracker这个工具可以用来爆破secret,一般来说太长的就可以不做尝试了,可以看看题目里有没有什么提示,这个工具也能指定字典,如果有明显提示就可以尝试一下。

比如本题的就可以尝试onepiece,最后爆破结果得到secret是onepiece,直接写脚本伪造jwt用sub:1登录就可以得到flag了

1
2
3
4
5
6
7
from requests import get
import jwt
import re

encoded_jwt = jwt.encode({'sub': 1}, 'onepiece', algorithm='HS256')
#第一个参数是payload,第二个是secret,第三个是加密方式
print(encoded_jwt)

这题到这里就结束了

在这题之外

  • 如果发现加密方式为none可以直接去掉签名
  • 修改算法为none
  • 修改算法从RS256到HS256
  • 信息泄漏 密钥泄漏
  • 爆破密钥

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!