重生之我是JWT
前不久研究websocket时发现port-swigger出了新的靶场,一看,发现是关于jwt安全的,刚好来总结回忆一下
他定义了一种紧凑且自包含的方式,用于在各方之间安全地传输信息作为 JSON 对象,特别适用于分布式站点的单点登录(SSO)场景
JWT由Header、Payload、Signature组成
Header.Payload.Signature
{"alg":"加密算法","typ":"JWT"}
iss: The issuer of the token
sub: The subject of the token
aud: The audience of the token
exp: JWT expiration time defined in Unix time
nbf: "Not before" time that identifies the time before which the JWT must not be accepted for processing
iat: "Issued at" time, in Unix time, at which the token was issued
jti: JWT ID claim provides a unique identifier for the JWT
//可以自定义其它字段
Signature = HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload),"secret")
secret保存在后端,就是来解析确定验证的key
未对签名进行验证
我们前面说过,JWT存在一个Signature 签名,如若没对签名进行认证,就可能存在越权情况
Lab: JWT authentication bypass via unverified signature
To solve the lab, modify your session token to gain access to the admin panel at /admin, then delete the user carlos.
我们需要把carlos删掉,而这就需要登录管理员账号,但是又没有给管理员的账号,只有一个普通用户,那我们就要想办法提升权限
先抓包
观察确定为JWT,将payload处字符base64解码得
把sub的wiener修改为administrator,重新传参
成功越权,然后就是删除用户即可
回顾一下Header的构成
{"alg":"加密算法","typ":"JWT"}
这里alg可以说明加密算法,但如果对该设置不进行强认证也会造成越权问题
我们可以把加密算法设成none来进行验证绕过
Lab: JWT authentication bypass via flawed signature verification
先和上题一致,将sub内容修改为administrator,然后发现还是没能成为管理员,接着修改header的alg为none,把后续的Signature删除,因为Signature是通过alg算法生成的,既然alg都为none了,那Signature也应该为空了
成功变成管理员
然后就是正常删除用户就行
Signature = HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload),"secret")
我们知道,签名存在一个secret密钥,这个一般都会保存在后端,别人无法查看,但是类同于弱口令,一旦这个密钥不复杂就有可能被爆破,gayhub上也有很多爆破的字典
Lab: JWT authentication bypass via weak signing key
还是一样先抓包得到JWT
然后用到hashcat来进行爆破,kali自带
hashcat -a 0 -m 16500/path/to/jwt.secrets.list
爆破得到secret为secret1
然后前往jwt.io生成我们需要的的jwt,把sub和secret进行修改
重新传包,成功
与很多注入一样,JWT标头注入也可大致分为两种情况,一是与数据库连接,搭配sql注入使用,一是不与数据库连接,单独进行越权操作
通过jwk参数注入自签名的JWT
还记得我们说过的JWS吗
JWS,即JSON Web Signature,只是 JWT 的一种实现
我们就可以通过JWK注入JWT,形成JWS
JWK 英文全称为 JSON Web Key,是一个JSON对象,表示一个加密的密钥,他不同于alg属性,JWK是可选的,以下就是一个示例
{
"kid": "ed2Nf8sb-sD6ng0-scs5390g-fFD8sfxG",
"typ": "JWT",
"alg": "RS256",
"jwk": {
"kty": "RSA",
"e": "AQAB",
"kid": "ed2Nf8sb-sD6ng0-scs5390g-fFD8sfxG",
"n": "yy1wpYmffgXBxhAUJzHHocCuJolwDqql75ZWuCQ_cb33K2vh9m"
}
}
Lab: JWT authentication bypass via jwk header injection
需要先安装一个插件,方便后续的操作
–JWT Editor
然后正常抓包,将sub内容修改为administrator
然后切换到JWT Editor Keys选项new一个RSA Key
我是已经生成的了,然后保存后回到Repeater选择Embedded JWK攻击
成功越权
有些服务器并不会直接使用JWK头部参数来嵌入公钥,而是使用JKU(JWK Set URL)来引用一个包含了密钥的JWK Set,我们就可以借此来构造一个密钥从而实现越权操作
Lab: JWT authentication bypass via jku header injection
先还是正常抓包修改sub内容,然后去到JWT Editor Keys生成一个RSA密钥,或者用上一道题目的也行,然后复制公钥
然后加上key头
{
"keys": [
]
}
保存到exploit的body中
然后将kid修改成自己生成的JWK中的kid值,将jku的值改为exploit,使其导入
然后回到点击下面的sign,选择Don’t modify header 模式,Sign 即可
成功越权
服务器可能会使用多个加密密钥来为不同类型的数据进行签名,出于这个原因,在JWT头部有时会包含一个kid参数,以避免服务器验证签名时出现错误,而在JWT规范中并没有对这个kid定义具体的结构,他仅仅是开发人员任意选择的一个字符串,可能只是一个指向数据库中的一个特定条目,甚至只是一个文件的名称也有可能
Lab: JWT authentication bypass via kid header path traversal
先生成一个Symmetric Key,也就是对称密钥,并将 k 的值修改为 AA==即为null
然后抓包修改kid值和sub进行目录遍历
–/dev/null是linux中的“黑洞”,代表空设备文件
/dev/null文件名与AA==一致都为null,对称密钥,应该可以成功绕过
然后回到repeater点击sign选择OCT8 的密钥攻击
成功越权
即使服务器的密码是攻击者无法破解的复杂密码,但是由于JWT库的一些原生安全问题,攻击者可能会以开发者想不到的算法来伪造有效的JWT
portswigges里也有相关靶场,目前尚未完全理解,先挖个坑,后续补上
我们可以看到,JWT攻击千奇百怪,但是万变不离其宗,主要的潜在漏洞为
我们也可围绕这些进行JWT攻击进行防御