绿盟杯(重庆市大学生安全竞赛)glowworm

搜集信息,先F12查看网页源代码。看到提示,访问/source,得到部分源代码。

const express = require('express');
const bodyParser = require('body-parser')
const path = require('path');
const crypto = require('crypto');
const fs = require('fs');
const app = express();
const FLAG = require('./config').FLAG;

app.set('view engine', 'html');
app.engine('html', require('hbs').__express);
app.use(express.urlencoded());
app.use(bodyParser.urlencoded({extended: true})).use(bodyParser.json())

var glowworm=[];
var content=[];
function sha1(string) {
    return crypto.createHash("sha1").update(string).digest("hex");
}

app.get('/', (req, res) => {
    const { page } = req.query;
    if (!page) res.redirect('/?page=index');
    else res.render(page, { FLAG, 'insect': 'glowworm' });
});

app.get('/source', function(req, res) {
    res.sendFile(path.join(__dirname + '/app.js'));
});

app.post('/data', function(req, res) {
    var worm = req.body;
    content[worm.wing][worm.fire] = worm.data;
    res.end('data success')
});

app.get('/refresh', (req, res) => {
    let files = [];
    var paths = path.join(__dirname,'views/sandbox')
    if(fs.existsSync(paths)){
        files = fs.readdirSync(paths);
        files.forEach((file, index) => {
            let curPath = paths + "/" + file;
            if(fs.statSync(curPath).isFile()){
                fs.unlinkSync(curPath);
            }
        });
    }
    res.end('refresh success')
});

app.post('/', (req, res) => {
    const key = "worm";
    const { content , a, b} = req.body;

    if (!a || !b || a.length !== b.length) {
        res.send("no!!!");
        return;
    }
    if (a !== b && sha1(key + a) === sha1(key + b)) {
        if(glowworm.token1 && req.query.token2 && sha1(glowworm.token1) === req.query.token2){
            if (typeof content !== 'string' || content.indexOf('FLAG') != -1) {
                res.end('ban!!!');
                return;
            }
            const filename = crypto.randomBytes(8).toString('hex');
            fs.writeFile(`${path.join('views','sandbox',filename)}.html`, content, () => {
            res.redirect(`/?page=sandbox/${filename}`);
            })
        }else{
          res.send("no no no!!!");
        }
    }else{
      res.send("no no!!!");
    }
});

app.listen(8888, '0.0.0.0');

容易发现如下代码,

content[worm.wing][worm.fire] = worm.data;

nodejs声明变量的方式和php非常像,不用声明具体类型。nodejs的路由写法app.get(’/’, (req, res) => {expression})

提供两个对象req和res。和其他语言一样,拥有对请求和响应的处理函数。路由绑定的方法和flask比较像。

nodejs原型链污染

之前遇到nodejs原型链污染,一般都是直接略过了。这次是不得不做此题了,就差一个就ak了web怎么能放弃。

在javascript中一切皆对象,因为所有的变量,函数,数组,对象 都始于object的原型即object.prototype,任何对象都拥有__proto__属性,正确的用法是建立两个对象之间的关联。(一个对象可以使用__proto__关联另外一个对象,被关联的对象就叫原型)

var animal={
	eat:function(){
		...
	}
};
var dog={
	__proto__:animal
};

在以上代码中,dog对象可以使用eat方法。

结合上面我们看到的content[worm.wing][worm.fire] = worm.data;可以发现,如果我们可以控制worm的值,以此来控制该程序的变量。如果worm.wing的值是__proto__,那么就有如下结构

var content={
	__proto__:worm.fire=worm.data
}

worm.fire可以是我们想要的任何变量。

再看下面的if语句

if(glowworm.token1 && req.query.token2 && sha1(glowworm.token1) === req.query.token2)

在上下文中,token1是没有进行定义的,那么这里就需要用到原型链污染,对token1进行赋值。

在这里插入图片描述

这是一个单独的请求。如果我们访问/的时候,这个值还会在程序的上下文中。为什么在,我也不知道。

javascript弱类型

凡是在定义变量时不声明类型的操作,都有可能触发弱类型比较。以上代码有

if (a !== b && sha1(key + a) === sha1(key + b))

javascript在进行拼接字符的时候,会忽略类型,也就是这里传数组[1]和’0’,与给定字符串key相加后,会获得相同的值。

这里req.query.token2,简单说明一下req.query会获取URL中get请求获得的值。这里获取的值经过sha1加密应该和原型链污染的data值相同。

在这里插入图片描述

模板注入

app.get('/', (req, res) => {
    const { page } = req.query;
    if (!page) res.redirect('/?page=index');
    else res.render(page, { FLAG, 'insect': 'glowworm' });
});

这里调用了res.render将参数渲染到前端,常见的模板注入方式为{{}}。这里进行简单尝试__proto__.toString()。可以发现并没有将字符直接打印出来。

在javascript中,几乎任何数据类型都是对象,对象使用this关键字调用。那么利用模板注入遍历this关键字指向的属性。即可获取本题的flag。

在这里插入图片描述

关于该模板的文档:https://www.handlebarsjs.cn/guide/hooks.html#helpermissing

在这里插入图片描述

最终利用存在模板注入的路由来读取flag,这里路由/后的代码会创建一个随机文件名的html文件,然后将文件名显示在前端页面。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值