JWT以及python反序列化
JWT
JWT,全称JSON Web Token,它是作为JSON对象在各方之间安全传输信息,该信息可以被验证和信任
JWT的使用场景
授权(Authorization):每个请求都会带有JWT,让其用户有允许访问特殊的路由、服务和资源的令牌
信息交换(Information Exchange):对于安全的在各方之间传输信息而言,JWT是很好的选择,因为JWT是有使用公钥/私钥签名的,而这个签名主要针对使用头和负载计算机,可以验证内容没有篡改
JWT构成
JWT由三部分构成,分别是header、payload、signature
1 | header.payload.signature |
header部分
header由两部分构成
1 | token的类型,如:"typ":"JWT" |
然后用base64来进行加密,得到JWT的第一部分
payload部分
payloay包含声明,声明是用户和其它数据的声明,有三种类型
1 | registered claims:预定义声明,不是强制的 |
实例
1 | { |
然后对payload进行base64加密,成为JWT的第二部分
Signature
Signature是一个签名,用来验证消息在传递过程中是否有篡改,并且,对于使用私钥签名的token,它还可以验证JWT的发送方是否为它所称的发送方
它的加密原理
1 | HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret) |
其中的secret是私钥,一般会在注册登录的时候获得JWT,作为一个用户凭证
JWT的工作过程
当用户要访问受保护的资源的时候,则需要注册和登录,而就在这个过程中,用户会向授权服务器发出请求,授权服务器发出请求后,会返回一个JWT作为用户的凭证来访问受保护的资源,而JWT中的签名是用私钥加密的,提高了安全性
因此,如果我们伪造jwt的话,需要知道授权服务器的私钥,所以我们可以使用工具对私钥进行爆破,使用c-jwt-cracker,建议在ubuntu进行编译
1 | c-jwt-cracker:https://github.com/brendan-rius/c-jwt-cracker |
然后修改JWT的内容,再用获得的私钥进行加密,可以使用这个网站(https://jwt.io/),也可以使用脚本
1 | import jwt |
JWT详细可看:https://www.cnblogs.com/cjsblog/p/9277677.html
python反序列化
python反序列化使用pickle库以及函数来进行序列化,和php的serialize和unserialize作用一样
Pickle库以及函数
pickle库让python可以实现序列化和反序列化,而它会将二进制形式的序列化保存在文件中,后缀为.pkl,但是文件不能直接打开预览,其中反序列化和序列化的函数有
1 | dumps:对象序列化为bytes对象 |
dump/load
实例
1 | import pickle |
看这串代码,我们可以知道dump函数以及load()函数的流程,就是用dump()函数将数据进行序列化,然后将序列化后得到的数据写入到一个文件中,然后load()函数读取文件,并将里面的数据进行反序列化
dumps/loads
实例
1 | import pickle |
我们可以知道dumps/loads的操作流程,即dumps()函数将数据进行序列化,并返回一个bytes对象出来,然后loads()函数直接从bytes对象中读取序列化的信息,然后进行反序列化即可,无需写入读取文件
PVM
对于python而言,python运行源码,是python解释器将源码编译成字节码,然后将字节码转发到python虚拟机中执行,即PVM就是用来解释字节码的引擎
PVM执行流程
首先PVM将源码编译成字节码,然后PVM再将字节码转发到python虚拟机中执行,不过两个阶段PVM不同,第一个是python解释器,第二个是python虚拟机
PVM与Pickle库的关系
Pickle是一门基于栈的编程语言,本质是一个轻量的PVM,它由三部分构成
指令处理器
1 | 从数据流中读取操作码和参数,并对其进行解释处理,在循环执行过程中会不改变stack和memo区域的值,直到遇到.这个结束符号 |
栈区(stack)
1 | 作为流数据处理过程中的暂存区,在不断进出栈过程中完成对数据流的反序列化操作,并最终在栈顶生成反序列化的结果 |
标签区(medo)
1 | 可以看作是数据索引或者标记,即将反序列化完成的数据以key-value的形式存储在medo中 |
指令处理器的操作码
1 | c:读取本行的内容作为模块名,读取下一行的内容作为对象名object,然后将module.object作为可调用对象压入到栈中 |
Pickle/CPickle反序列化漏洞
python反序列化漏洞一般出现在__reduce__()函数中,它会在dumps/dump()函数进行序列化时,自动调用这个__reduce__()函数,
1 | __reduce__() |
其实,R操作码就是__reduce__()函数的底层实现,在序列化快结束时,python进程会自动调用__reduce__()方法,所以如果我们可以控制被调用函数的参数,就可以执行恶意代码
反序列化漏洞出现位置
解析认证token、session的时候
将对象Pickle后存储在磁盘文件
将对象Pickle后在网络中传输
参数传递给程序
执行恶意代码的实例
1 | #模仿Epicccal师傅的例子 |
实例
但我们打开页面时,发现是一个购物的界面,下面有一个提示就是找到lv6,所以我们可以写一个脚本,来找到有lv6的页面
1 | import requests |
发现lv6后,打开购买界面,发现它的购买金额已经大于我们的钱袋里的钱,但是有优惠金额,所以我们可以抓包,然后修改优惠金额,让我们可以买到,修改后却说不是admin用户用不了,然后看到包里有JWT这个验证信息
1 | JWT=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InRlc3QifQ.l0qG4XbJbemqJXsaITaT8g78fkJ-boRvU2H7H1CY644 |
因此,我是用c-jwt-cracker爆破出密钥
1 | 1Kun |
然后修改JWT中payload的值
1 | {"username":"admin"} |
然后用https://jwt.io/这个网站伪造一个JWT,放进包里,进行一个欺骗,然后进入到一个一键成为大会员的界面。之后打开页面源码,发现有源码泄露www.zip
得到源码后,由于我们当前的页面是admin的,所以我们打开y源码文件中的Admin.py
1 | import tornado.web |
可以看见一处python反序列化漏洞
1 | try: |
而self.get_argument(‘become’)是获取become参数的值,我们只要点击一键成为大会员,然后抓包就可以看见这个become参数。根据代码
1 | p = pickle.loads(urllib.unquote(become)) |
我们要输入一串序列化的字符串进行,让它进行读取,前面提到了__reduce__()方法,可以执行恶意代码,而这里也没有什么过滤,所以可以直接构造序列化信息,exp
1 | # coding=utf8 |
commands.getoutput()
1 | 可以获取程序执行后的返回值和输出 |
eval()
1 | 在php中是将参数当作php代码执行,而在python是将参数当作是表达式执行,并返回结果 |
详细可参考:https://blog.csdn.net/qq_43431158/article/details/108919605