先扫一下目录
首先这是一个koa
框架,所以我们先熟悉一下这个框架的目录结构
然后访问/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
| 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(); } };
|
这儿的大致意思就是注册登录,并把信息保存在jwt
中,一般jwt是可以伪造的,但是这儿密码我们根本不可能爆破出来
所以只能寻求其它的方法,我们注意到,我们每一次登录,都会生成密匙并插入到列表当中,然后登录的时候根据sid
来选择对应的密匙
值得注意的是,我们这儿的sid可控,假如我们令sid=0.1
会怎么样呢?
这个就是node
的jwt
漏洞
当jwt secret
为空时 jsonwebtoken
会采用algorithm none
进行解密
解题步骤
登录的时候会返回sses:aok
,里面包含我们的登录信息,这个我们可以更改(改登录名)
我们先解密一下注册时返回的jwt
链接
- 进入flag页面
这两个值都进行替换(登录页面返回的值),就能得到flag