2020第五空间Final
第五空间awd没web。。。解题模式3道web就做出了一道,一道要wasm逆向做不来,还有一道问了smi1e师傅感觉有点脑洞。
Hard node
有源码,但是主办方近一个小时忘放了。。。
关键代码:
app.js
const express = require('express');
const bodyParser = require('body-parser');
const proc = require('child_process');
const request = require('request');
const ip = require("ip");
const manage = require("./manage.js");
const path = require('path');
const app = express();
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(express.static(path.join(__dirname, 'public')));
//stop hackers
const disallowedKeys = [
'__proto__',
'prototype',
'constructor',
'eval','proccess','root','global','exec','!','fs'
];
function isValidPath(segment){
disallowedKeys.forEach(evilWord => {
if(segment.toString().indexOf(evilWord)!==-1){
return false
}
});
return true;
}
app.post('/add', (req, res) => {
let ip = req.ip;
console.log(ip.m);
if (ip.substr(0, 7) == "::ffff:") {
ip = ip.substr(7)
}
console.log(`method:${req.method},serverip:${server_ip},ip:${ip}`);
if (ip != '127.0.0.1' && ip != server_ip) {
res.status(403).send('Not Edit from Local!');
}else{
if(req.body.userName && req.body.nameVal){
let username = req.body.userName;
let nameVal = req.body.nameVal;
if (!isValidPath(username) || !isValidPath(nameVal)) {
username = 'username';
nameVal = 'guest';
}
manage.set(object, username, nameVal);
console.log(ip.k);
console.log(object);
res.send(`
<h1>Edit Success</h1>
<a href="/admin">View Admin Page</a>`)
}else{
res.send('param error');
}
}
});
app.get('/admin',(req,res)=>{
if(manage.get(object,'username','guest') === 'admin'){
console.log('Current User:'+object.username)
const child = proc.fork(`${__dirname}/public/user.js`,['admin']);
child.on('message', (body) => {
res.status(200).send(body);
});
child.on('close', (code, signal) => {
console.log(`subproccess ended with ${signal}`);
});
}else{
res.status(403).send('Only Admin Can View this');
}
})
app.get('/getContent',(req,res)=>{
res.sendfile(`${__dirname}/public/guest.html`);
})
app.get('/', (req,res) => {
// console.log(req.body)
let uri = req.query.url? req.query.url: 'http://127.0.0.1:3000/getContent';
console.log(uri)
try{
request.get(uri,(err,response,data)=>{
if (!err && response.statusCode == 200) {
res.send(data);
}else{
console.log(err);
}
})
}catch(e){
console.log(e);
}finally{
console.log('Make Server Continue Running');
}
});
var object = {username:'guest'};
var server_ip = ip.address();
app.listen(3002);
console.log(`${server_ip} is starting at port 3000`)
manage.js
const isObj = require('is-obj');
var manage = {
getPathSegments: function(path) {
const pathArray = path.split('.');
const parts = [];
for (let i = 0; i < pathArray.length; i++) {
let p = pathArray[i];
while (p[p.length - 1] === '\\' && pathArray[i + 1] !== undefined) {
p = p.slice(0, -1);
p += pathArray[++i];
}
parts.push(p);
}
return parts;
},
get: function(object, path, value) {
if (!isObj(object) || typeof path !== 'string') {
return value === undefined ? object : value;
}
const pathArray = this.getPathSegments(path);
for (let i = 0; i < pathArray.length; i++) {
if (!Object.prototype.propertyIsEnumerable.call(object, pathArray[i])) {
return value;
}
object = object[pathArray[i]];
if (object === undefined || object === null) {
if (i !== pathArray.length - 1) {
return value;
}
break;
}
}
return object;
},
set: function(object, path, value) {
Object.keys(Object.prototype).forEach(function(Val){
if(!Object.hasOwnProperty(Val)){
delete Object.prototype[Val];
console.log(`${Val} is delete`);
}
})
if (!isObj(object) || typeof path !== 'string') {
return object;
}
const root = object;
const pathArray = this.getPathSegments(path);
for (let i = 0; i < pathArray.length; i++) {
const p = pathArray[i];
if (!isObj(object[p])) {
object[p] = {};
}
if (i === pathArray.length - 1) {
object[p] = value;
}
object = object[p];
}
return root;
}
}
module.exports = manage
有4个路由
/add
/admin
/getContent
/
在/
有SSRF、/add
需要内网才能访问、/admin
需要将全局变量object的username熟悉覆盖值变为admin才能进入。
/
的SSRF使用的是request.get,而/add
需要发送POST包,查看request的文档https://github.com/request/request
可以利用har覆盖请求的方法

简单分析manage.js, 在set方法中存在原型链污染漏洞

那么我们就可以通过SSRF向/add
发送POST包,进行原型链污染。在结合/admin
中的child_process.fork,我们可以污染env熟悉实现RCE,具体可以看这里https://xz.aliyun.com/t/6755。
最后要进入/admin
我们需要覆盖全局变量object的username属性,可以直接使用manage中的set覆盖username
首先,覆盖admin

然后污染env变量

最后访问/admin
执行命令
