环境搭建

使用docker容器搭建webgoat环境

什么是JWT

Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准.该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(单点登录SSO:在多个应用系统中,只需要登录一次,就可以访问其他相互信任的应用系统。)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。

JWT的构成

JWT是由三段信息构成的,将这三段信息文本用.链接一起就构成了Jwt字符串。第一部分我们称它为头部(header),第二部分我们称其为载荷(payload, 类似于飞机上承载的物品),第三部分是签证(signature)。

header

jwt的头部承载两部分信息:

声明类型,这里是jwt

声明加密的算法 通常直接使用 HMAC SHA256

完整的头部就像下面这样的JSON:

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

然后将头部进行base64加密(该加密是可以对称解密的),构成了第一部分。

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9

playload

载荷就是存放有效信息的地方。这个名字像是特指飞机上承载的货品,这些有效信息包含三个部分

标准中注册的声明

公共的声明

私有的声明

标准中注册的声明 (建议但不强制使用) :

iss: jwt签发者

sub: jwt所面向的用户

aud: 接收jwt的一方

exp: jwt的过期时间,这个过期时间必须要大于签发时间

nbf: 定义在什么时间之前,该jwt都是不可用的.

iat: jwt的签发时间

jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。

公共的声明 : 公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密.

私有的声明 : 私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息。

定义一个payload:

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

然后将其进行base64加密,得到JWT的第二部分。

eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9

signature

JWT的第三部分是一个签证信息,这个签证信息由三部分组成:

header (base64后的)

payload (base64后的)

secret

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

// javascript

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

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

// TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

将这三部分用.连接成一个完整的字符串,构成了最终的jwt:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

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

webgoat关卡

第3关

本次给出了一串JWT token,让我解码来获取其中的用户

其实就是让我们熟悉JWT token的构成

使用webwoof中的JWT模块

可以看到用户名为user

第5关


这一关要求修改我收到的令牌,通过修改令牌实现越权成为管理员

首先先获得一个普通用户身份

之后抓取重置过程的包

可以看到有token

放到webwoof中看一下内容是啥

看到admin项为false,修改一下admin的值,再改一下alg为none,不加密

把得到的token粘贴到包中,再在结尾加上一个”.”重新发包

之后票数就被重置了

第8关

本关给了一个token,让根据这个token得到密钥,再自己做一个账户名为webgoat的token再重新加密后提交

需要爆破密钥

这里使用密码破解工具:hashcat

-m 是哈希类型 jwt的代号是16500

Jwt.txt是存放题目提供的原token

-a 3 掩码暴力破解

-w 3 指定工作模式 3为high,对系统资源占用比较大了

20k 指常用的20000个单词

--force 忽略警告

爆破出结果是available

在token中还设置了token的生效时间,只有1分钟,所以在爆出available之后,可能时间就不够了,这时候手动修改一下这里的数字(该处使用的是unix时间戳,可以百度看现在的时间,然后修改)让现在的时间处于两者之间,再提交就能通过了

提示成功

第10关

这一关是提供了TOM 的很久以前过期的token,要求我们使用过期的token来达到冒充TOM的账号来付款的操作。

又是时间戳,看了一下,有效期是24小时,修改一下,改成今天

抓包,替换token

 

成功

第11关

这一关是jerry想删除tom的账号

尝试把jerry的token替换成tom的,再取消加密,然后执行删除操作

但是显示不成功

百度后发现,此处需要配合一个SQL注入漏洞,如下

kid这个参数直接拼接到了SQL语句中,而且将查询的结果作为解码后作为secret进行token解析

构造SQL语句,将密码设为1

重放包

关于JWT的安全隐患

1.在alg字段设为none

如果开发人员没有在生产环境中关闭该功能,那么签名将被无效化,任何签名都是有效的。

2.暴力破解密钥

如果签名算法,加密后的载荷和最终得到的加密字符串都已知,且密钥不够复杂的话,使用暴力破解便有可能将密钥解出。

3.密钥泄露

若是密钥泄露,那么任何token都能被签名成功。

4.kid字段被控制

因为kid字段可以被用户控制,如果kid字段存在薄弱点,那么就容易被攻击者利用,从而造成危害。比如:目录遍历,SQL注入攻击

5操纵头部参数

除KID外,JWT标准还能让开发人员通过URL指定密钥。

(1).JKU头部参数

JKU全称是“JWKSet URL”,它是头部的一个可选字段,用于指定链接到一组加密token密钥的URL。若允许使用该字段且不设置限定条件,攻击者就能托管自己的密钥文件,并指定应用程序,用它来认证token。

jku URL -> file containing JWK set -> JWK used to verify the token

(2) JWK头部参数

头部可选参数JWK(JSON Web Key)使得攻击者能将认证的密钥直接嵌入token中。

(3)操纵X5U,X5C URL

同JKU或JWK头部类似,X5U和X5C头部参数让攻击者能够指定认证的公钥证书或证书链。X5U以URI形式指定信息,而X5C允许将证书值嵌入token中。

6.信息泄漏

由于JWT用于访问控制,因此它通常包含用户的相关信息。

如果不加密token,则任何人都能通过base64解码并读取token上的载荷信息。因此,如果token包含敏感信息,它便可能成为信息泄漏的来源。正常的token签名仅保障数据完整性,而非机密性。

7.命令注入

有时,将KID参数直接传到不安全的文件读取操作可能会让一些命令注入代码流中。

一些函数就能给此类型攻击可乘之机,比如Ruby open()。攻击者只需在输入的KID文件名后面添加命令,即可执行系统命令:

“key_file” | whoami;

类似情况还有很多,这只是其中一个例子。理论上,每当应用程序将未审查的头部文件参数传递给类似system(),exec()的函数时,都会产生此种漏洞。

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐