freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

PortSwigger JWT 安全问题
2022-07-23 12:45:25
所属地 浙江省

博客地址 https://drun1baby.github.io/

最近看到 PortSwigger 上新出了一个靶场,主题是 JWT 安全。

0x01 前言

JWT 全称 JSON Web Token,是一种标准化格式,用于在系统之间发送加密签名的 JSON 数据。

原始的 Token 只是一个 uuid,没有任何意义。

JWT 包含了部分业务信息,减少了 Token 验证等交互操作,效率更高。JWT 作为现如今高度分布式网站的首选 Cookie 之一,今天我们来谈论一下 JWT 的安全性问题。

0x02 JWT 是什么

  • 要针对网站的 Cookie 下手,首先我们要知道它是什么。

JWT 由三部分组成,分别为 Header ———— 头部;Payload ———— 负载,Signature ———— 签名。它们之间由三个 **.**分隔,由 Base64 加密而来。

Signature 可用的 JWT 也被称作为 JWS。

我们以下面这一个 JWT 作为例子进行说明

eyJraWQiOiI5MTM2ZGRiMy1jYjBhLTRhMTktYTA3ZS1lYWRmNWE0NGM4YjUiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsImV4cCI6MTY0ODAzNzE2NCwibmFtZSI6IkNhcmxvcyBNb250b3lhIiwic3ViIjoiY2FybG9zIiwicm8sZSI6ImJsb2dfYXV0aG9yIiwiZW1haWwiOiJjYXJsb3NAY2FybG9zLW1vbnRveWEubmV0IiwiaWF0IjoxNTE2MjM5MDIyfQ.SYZBPIBg2CRjXAJ8vCER0LA_ENjII1JakvNQoP-Hw6GG1zfl4JyngsZReIfqRvIAEi5L4HV0q7_9qGhQZvy9ZdxEJbwTxRs_6Lb-fZTDpW6lKYNdMyjw45_alSCZ1fypsMWz_2mTpQzil0lOtps5Ei_z7mM7M8gCwe_AGpI53JxduQOaB5HkT5gVrv9cKu9CsW5MS6ZbqYXpGyOG5ehoxqm8DL5tFYaW3lB50ELxi0KsuTKEbD0t5BCl0aCR2MBJWAbN-xeLwEenaqBiwPVvKixYleeDQiBEIylFdNNIMviKRgXiYuAvMziVPbwSgkZVHeEdF5MQP1Oe2Spac-6IfA

Header 部分

Header 部分就像是货车的标志,告诉了我们这辆车上面装了什么易爆品啊,还是易燃品。

在 JWT 中 Header 部分存储的是 Token 类型和加密算法,它长成这样

{
  "alg": "HS256",   // 也可以是 HS512, RS256 等等
  "typ": "JWT"
}

Payload 部分

Payload 这个词中文意思为负载,也就是我们大货车上面的货物。Payload 里面存具体的业务数据比如用户 id 等等。

  • Payload 部分长这样

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

Signature 部分

Signature 部分意为签名,这批货送到了,需要对方签收一下,这就是签名,我个人也喜欢把它理解成 Secret,它是经历这样一个算法所生成的。

alg 对应的加密算法(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  我们输入的 Secret
) secret base64 encoded

Signature 是具体的数字签名密文信息,这部分的密文信息是手动设置的,一般在 Java 开发的配置文件当中设置。像这样

image

我们可以在 jwt.io中体验一下 Signature 的使用。

JWT 与 JWT,JWE

  • "JWT vs JWS vs JWE"

  • 这个概念了解就好了,从原理上来说是非常相似的。

简单来说,现在网上大多数介绍JWT的文章实际介绍的都是 JWS(JSON Web Signature),也往往导致了人们对于 JWT 的误解,但是 JWT 并不等于 JWS,JWS 只是 JWT 的一种实现,除了 JWS 外,JWE(JSON Web Encryption) 也是 JWT 的一种实现。

JWT 会造成的危害

JWT 的漏洞经常与提权类漏洞的危害放在一起说,所以提权类的漏洞能造成什么样的危害,JWT 产生的安全问题也能产生危害。

0x03 JWT 基础安全问题

JWT 所产生的安全问题,是设计上与逻辑上不恰当的设计所造成的。

其中,如果服务器未对签名进行校验,则可以任意修改数据,若进行了校验,则无法修改,需要我们通过其他手段进行辅助攻击。

1. 未对签名进行验证

  • 前文我们说到,JWT 需要开发者提供一个 Signature ———— 签名,如果我们不对签名进行验证,极有可能产生如下的越权情况。

假设选择存在一个 JWT 如下

{ 
	"username": "carlos", 
 	"isAdmin": false 
}

如果没有对签名进行认证,我们可以修改isAdmin属性的值为true,即可造成简单的越权。

Lab: JWT authentication bypass via unverified signature
JWT 未对签名进行验证

  • 靶场要求,利用 admin 的越权账户删除 carlos 账户。

我们去到 "My Accout" 界面下,登录进去,抓包,认识一下 JWT

image

接着,在 Burpsuite 里面,我们查看这串 JWT。

image

{
	"iss":"portswigger",
 	"sub":"wiener",
 	"exp":1656054440
}

在 Burpsuite 当中对其进行修改并发包。

image

  • 此时我们已经是 admin 的账户了,然后去完成我们的删除 user 的操作

image

成功 ~!

2. 未对加密算法进行强验证

  • 这一种情况,也就是未对 Header 部分的 **"alg"**进行验证

我们可以设置 alg 为 none,绕过验证,从而实现攻击。

Lab: JWT authentication bypass via flawed signature verification
未对加密算法进行强验证

  • 靶场要求,利用 admin 的越权账户删除 carlos 账户。

登录,抓包,在 /my-accout 接口处,查看 JWT 的内容,修改接口为 /admin并发包,现在是 401 Unauthorized 的状态码。

image

如图所示修改 sub 属性为 administrator,修改 alg 为 none。这里还需要将我们的 Signature 删除掉,因为 Signature 是通过 alg 算法生成的,但要保留"分割点",让其成为 JWT 的形式。

image

image

  • 此时我们已经是 admin 的账户了,然后去完成我们的删除 user 的操作。

image

3. 爆破 Secret 进行越权操作

这里用到一个叫做 hashcat的工具,kali 机一般是自带的,如果没有安装的话可以通过如下命令进行安装。

sudo apt-get update
sudo apt-get install hashcat

爆破需要字典,这里是我在本道靶场中使用的 字典

通过如下命令可以进行 JWT 的 Secret 的爆破

hashcat -a 0 -m 16500 你的jwt /path/to/jwt.secrets.list
  • 获得 Secret 之后我们又能做什么呢?Secret 是用来生成 Signature 的一个未知的部分,所以知道它了之后便可以进行越权操作,我们通过这一道靶场感受一下。

Lab: JWT authentication bypass via weak signing key
爆破 Secret 进行越权

  • 靶场要求,利用 admin 的越权账户删除 carlos 账户。

先抓包,获取到 jwt 之后,我们使用 hashcat 进行 Secret 的爆破。

image

这里获取到了 Secret 为 "secret1",接着我们去到 jwt.io 下伪造身份。

image

现在我们成功伪造了一个 admin 权限的用户,再进行越权操作。Success ~!

image

0x04 JWT 标头注入

  • 这里的注入分为两种情况,当连接到数据库进行验证的时候,可能会存在 SQL 注入;若未连接至数据库时,我们可以进行越权设计。

  • 在 JWT Header 当中会存在名为 "JWK" 的属性之一,JWK 这一属性是可选的,而非向 alg属性一样,是必填的。

JWK 英文全称为 JSON Web Key,是一个JSON对象,表示一个加密的密钥。JWK 中的字段表示密钥的属性。

因为是对象,所以 JWK 当中有一些参数如下。

  • jku(JSON Web Key URL) - 提供一个 URL,服务器可以从中获取一组包含正确密钥的密钥。

  • kid(Key ID) - 提供一个 ID,服务器可以使用该 ID 在有多个密钥可供选择的情况下,根据密钥的格式,标识正确的密钥。

一个完整的 JWT Header 长成这样,其中便含有 jwk 这一属性。

{ 
	"kid": "ed2Nf8sb-sD6ng0-scs5390g-fFD8sfxG", 
	"typ": "JWT", 
	"alg": "RS256", 
	"jwk": 
	{ 
		"kty": "RSA", 
	 	"e": "AQAB", 
		"kid": "ed2Nf8sb-sD6ng0-scs5390g-fFD8sfxG", 
"n":"yy1wpYmffgXBxhAUJzHHocCuJolwDqql75ZWuCQ_cb33K2vh9m" 
	} 
}

JWT Header 的注入也分很多种,下面我们一一举例

1. 通过 jwk 注入 JWT,形成 JWS

理想情况下,服务器应当使用有限的公钥白名单来验证 JWT 签名。如果使用的公钥白名单中配置不恰当,。例如允许 jwk 的任意公钥,则会导致安全问题。

  • 我们以下面一道靶场尝试攻击方式。需要先安装 Burpsuite 的 JWT Editor Keys插件。

Lab: JWT authentication bypass via jwk header injection
通过 jwk 注入 JWT

  • 靶场要求,利用 admin 的越权账户删除 carlos 账户。

同样,登录,抓包。我们将 /my-accout 接口的包单独拿出来,选中 JSON Web Token 的界面。将 sub 的值从 "wiener" 切换为 "administrator"

image

切换完之后,点击我们安装的 JWT Editor Keys插件中的 New RSA Key,生成 2048 长度的 JWK 私钥。

image

这样子一来,我们的私钥就成功创建了,因为服务器后台不比对 JWK 的私钥是否合规,我们可以直接绕过,并进行越权认证。

image

现在我们成功认证为 administrator权限,发送删除请求即可 ~

image

2. 通过 jku 注入 JWT,形成 JWS

jku 通常在 JWT 的 Header 部分,它长这样。

image

以这一泄露为例,该 JWT 使用 RSASHA256 进行加密,而 RSA 的加密需要 n 与 e。我给大家先画幅流程图理一下攻击思路。

image

  • 前三步是 Burpsuite 的插件 JWT Editor Keys 可以完成的,后续操作只需要我们将 jku 指向 JWK 文件即可进行越权操作。

下面这道靶场就是通过 kid 的值匹配进行攻击

Lab: JWT authentication bypass via jku header injection
通过 jku 注入 JWT

  • 靶场要求,利用 admin 的越权账户删除 carlos 账户。

在明确了攻击方式之后,我们先利用 Burpsuite 的插件 JWT Editor Keys 生成公私钥对,上一道题目生成的公私钥对也可以拿来使用。

进入到 Expolit Server 中,在 Body 部分复制我们的 JWK 公钥。

image

直接拿出来的公钥是没有 "keys" 表头的,我们需要加上此标头,类似这样

{
    "keys": [
{
    "kty": "RSA",
    "e": "AQAB",
    "kid": "691edd2a-f7bf-4e14-b5c4-c5ec79c3dffb",
    "n": "oCBNZNRji19rBXUxFRI9toMCRwzxSZ1ckkCvLnuKc8r_ZvRhIHNHo7Flr8_1efAyaIwx_YG7zJZG41hZcpnDE3BfZym09dAM8T65BVLTmFolqpPcZIoqO_CCIK8JU1T0oRgDaW-PCobwC0axi0-LAgb5YbODuTxmwre6bjOwCGftEL9FNgjS5TR9yB0ZxLEJTGGtk7tq5q_voRT93vmSXmjZvUnFmMXSUEIHshfXjh12XUOLs0Mjgn0gfAWM7RFbkXvF-MLvj_p26gdV3j4zHUhPbeFoepzxcBGfVQXajWjj7xfisVga1Okjqp3S53ouNMpY9OEy-UmFz-QGNxKCKw"
}
    ]
}
  • 前半部分,我们在本地生成一个 JWK,并将其存放到自己的服务器上面,已经完成了,接下来是修改 jku,使得 jku 指向 服务器。

image

再点下面的 "Sign",选中生成的 Sign Key,并且选择 Don't modify header模式,Sign 即可。

image

  • 现在我们已经提权完毕,执行删除操作 ~

image

3. 通过 kid 注入 JWT,与目录遍历攻击相结合

JWS 规范没有针对 kid 进行严格设置,比如必须是 uuid 的格式或者是其他的,它只是开发人员选择的任意字符串。

那么我们可以通过将 kid 指向数据库中的特定条目,或者是指向文件名,作为验证密钥。

当 JWT 使用的是对称加密算法的时候,极有可能存在目录遍历的漏洞,我们能够强制服务器使用其文件系统中的任意文件作为验证密钥。

  • 当目录遍历与 kid 标头注入 JWT 结合在一起时,我们可以先尝试读取dev/null这一文件,dev/null这一文件默认为空文件,返回为 null,我们可以在 Symmetric Key 中,将 k 值修改为AA==也就是 null,进行攻击。

我们来看 Port 这里的靶场

Lab: JWT authentication bypass via kid header path traversal
通过 kid 注入 JWT,与目录遍历攻击相结合

  • 靶场要求,利用 admin 的越权账户删除 carlos 账户。

这里我们先要生成一个 Symmetric Key,也就是对称密钥,并将 k 的值修改为AA==,如图。

image

接着,我们在抓到的包中修改 kid 值,尝试用目录遍历读取dev/null此文件。并将 sub 修改为 administrator

image

点击下面的 Sign,使用 OCT8 的密钥攻击。

image

成功越权,再进行删除操作即可 ~

4. 其他的 JWT 标头攻击

  • cty 标头,意义为 Content Type,有时用于为 JWT 负载中的内容声明媒体类型,一般情况下是省略的。

我们可以尝试修改 cty 标头为text/xml或者是application/x-java-serialized-object,这也可能导致 XXE 注入与反序列化漏洞。

  • x5c (X.509 Certificate Chain);有时用于传递用于对 JWT 进行数字签名的密钥的 X.509 公钥证书或证书链。此标头参数可用于注入自签名证书,类似于上面讨论的 jwk 标头注入攻击。由于 X.509 格式及其扩展的复杂性,解析这些证书也会引入漏洞。

类似的 CVE 有 CVE-2017-2800CVE-2018-2633

0x05 Web 手的 Crypto 式攻击 ———— 算法混淆漏洞

一些基础知识,对称与非对称加密

可以使用一系列不同的算法对 JWT 进行签名。其中一些,如HS256(HMAC + SHA-256)使用“对称”密钥。这意味着服务器使用单个密钥对 Token 进行签名和验证。显然,这需要保密,就像密码一样。

image

其他算法,如RS256(RSA + SHA-256)使用“非对称”密钥对。这包括一个私钥(服务器用于对令牌进行签名)和一个可用于验证签名的数学上相关的公钥。

image

顾名思义,私钥必须保密,但公钥通常是共享的,以便任何人都可以验证服务器颁发的令牌的签名。

算法混淆漏洞的产生原因

  • 根本原因:JWT 库的原生安全问题。

尽管实际的验证过程因所使用的算法而异,但许多库都提供了一种与算法无关的单一方法来验证签名。也就是说,我不论是对称加密方式 RS256,还是非对称加密方式 HS256,它们产生 token 的算法是与提供的加密算法无关的。

当时学的时候我也愣了一下,后面看懂了。
我们可以根据下面的伪代码加以理解

在 JWT 库中定义了verify()方法

function verify(token, secretOrPublicKey){ 
	algorithm = token.getAlgHeader(); 
	if(algorithm == "RS256")
	{ 
		// Use the provided key as an RSA public key 
	} 
		else if (algorithm == "HS256")
		{ 
			// Use the provided key as an HMAC secret key 
		} 
}
  • 当我们使用 RS256 这种非对称加密的时候,他们可能总是将一个固定的公钥传递给该方法。

publicKey = <public-key-of-server>; 
token = request.getCookie("session"); 
verify(token, publicKey);

因为publicKey是一样的,所以会导致在使用对称加密的时候,造成publicKey的泄露,容易造成一系列的安全问题。

执行算法混淆攻击

  • 还是画个流程图,清楚一点

image

/jwks.json泄露的算法混淆攻击

以下面这道靶场感受一下算法混淆攻击

Lab: JWT authentication bypass via algorithm confusion
执行算法混淆攻击

  • 靶场要求,利用 admin 的越权账户删除 carlos 账户。

先进行第一步,获取它的服务器公钥,访问/jwks.json接口,前文我们提到过这个接口是也可能存在信息泄露的可能的。将这个 JWK 复制下来,作为我们第二部的 RSA Key。

image

接着,进行第二步,到 Burpsuite 的 JWT Editor Keys 去。生成新的 RSA Key,这里用之前泄露的 JWK。

image

然后把这串东西选中 "Copy Public Key as PEM",将其进行 base64 编码操作,保存一下得到的字符串。这里编码要注意,上下的一串-----END PUBLIC KEY-----不要删掉。

保存完后,在 JWT Editor Keys 处,生成新的对称加密 Key,用之前保存的 base64 编码去替换 k 的值。

修改 alg 为 HS256,修改 sub 为 administrator。再进行 Sign 操作,最后发包即可。

image

/jwks.json未泄露的算法混淆攻击

  • 在这种情况下,基于我们登录之后产生一个 JWT,在 logout 之后重新登录,会产生一个不同的 JWT,我们可以将两个 JWT 相比对,得到公钥。

Lab: JWT authentication bypass via algorithm confusion with no exposed key
/jwks.json 未泄露的算法混淆攻击

  • 靶场要求,利用 admin 的越权账户删除 carlos 账户。

按照我们之前说的方式,登录登出,再登录,获取两个 JWT,将其放到 Port 提供的 docker 工具里面运行。

运行的命令如下

docker run --rm -it portswigger/sig2n <token1> <token2>

image

  • 此脚本会输出一系列 token 的存在情况值,

image

这里我们尝试每一个 Tempered JWT,Port 这里给了提示说是 X.509 形式的,所以我们只需要将 X.509 形式的 JWT 进行验证即可。

当 Response 回应 200 时,代表 token 是有效的,若为 302 则代表了重定向。下图是一个成功的案例。

image

将这一 JWT 的 Base64 编码拿过来,先放到记事本里面暂存。去到 Burpsuite 的 JWT Editor Keys,点击 New Symmetric Key,将上面的 Base64 编码拿过来替换此对称密钥的 k 值。

生成对称密钥之后,进行和之前攻击一致的 Sign 操作

image

此时已经成功越权,执行删除操作即可。

0x06 JWT 安全问题的防护

  • 使用最新的 JWT 库,虽然最新版本的稳定性有待商榷,但是安全性都是较高的。

  • 对 jku 标头进行严格的白名单设置。

  • 确保 kid 标头不容易受到通过 header 参数进行目录遍历或 SQL 注入的攻击。

# web安全 # JWT # JAVA安全 # PortSwigger
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录