2020第五空间Final
source link: https://blog.szfszf.top/article/47/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
2020第五空间Final
2020年09月, 3437 views, #CTF #writeups第五空间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
执行命令
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK