原型链污染的攻击与修复

原型链污染的攻击与防御

先讲最为经典的Javascript原型链污染,再拓展到其他语言例如python和ruby

JavaScript

机理

在js中,函数有prototype属性,对象有__proto__属性

对象是由函数产生 的

对象的__proto__属性指向函数的prototype属性

来看一段代码来理解一下原型这个东西,其实和我们python的SSTI的继承链很像

let obj = {};
console.log(obj);
console.log(obj.__proto__);
console.log(obj.__proto__.__proto__);

接着我们会看到输出

{}
[Object: null prototype] {}
null

可以理出来这样的关系obj–>Object–>null

而Object自身的proto为null

也许你觉得不够直观,那么我会举出下面的例子:

function Person(name, age, gender) {
  this.name = name;
  this.age = age;
  this.gender = gender;
}

var person = new Person('lyc', 30, 'male');
console.log(person);

写一个html文件接着在里面引用这个js文件

在控制台就能看到

![屏幕截图 2025-09-10 083830](https://kisakiayano.oss-cn-hangzhou.aliyuncs.com/img/屏幕截图 2025-09-10 083830.png)

在网上找了一张图来总结:

图片1.png

不难看到所有对象的原型最终都会指向Object.prototype,接着指向null

那么到现在为止所讲的,和我们所说的污染有什么关系呢?我们再引入一个函数

function Teacher(name,age,gender,subject){
	Person.call(this,name);
	Person.call(this,age);
	Person.call(this,gender);
	this.subject=subject;
}
Teacher.prototype=new Person();

再对Person稍作修改

function Person(name, age, gender,nation) {
  this.name = name;
  this.age = age;
  this.gender = gender;
  this.nation ='China';
}

此时就会有

图片2.png

接下来用以下代码测试

function Person(name, age, gender,nation) {
  this.name = name;
  this.age = age;
  this.gender = gender;
  this.nation ="China";
}
function Teacher(name,age,gender,subject){
	Person.call(this,name);
	Person.call(this,age);
	Person.call(this,gender);
	this.subject=subject;
}
Teacher.prototype=new Person();

var person = new Person('lyc', 30, 'male',"China");
var teacher =new Person('lyc',30,'male','math');
console.log(teacher.nation);
//China

为什么我们明明没有给Teacher定义nation的属性,却可以打印出nation呢

在JavaScript里,每个对象都有一个原型(prototype),这个原型本身也是一个对象,它也有自己的原型,这样就形成了一条链,叫做原型链。当你访问一个对象的属性时,如果这个对象本身没有,JavaScript引擎就会沿着原型链往上找,直到找到为止。

所以我们就明白了原型链污染的原理了:通过污染原型的属性来达成攻击目的

那么假如我们正在使用一个合并的函数,那么就可以通过构造{"__proto__":{"isAdmin":true}}来污染原型进而实现伪造admin

[!NOTE]

  • __proto__ 是一个非标准的属性,但在大多数JavaScript引擎中都支持,它指向对象的原型。
  • 有些库或框架可能会使用其他的属性来表示原型,比如 constructor.prototype

利用

以下的利用需要自己去看,去理解,去复现,碍于篇幅我就不在分析,网上的文章也很多

不安全的递归合并(Merge)

jQuery.extend:CVE-2019-11358

3.4.0版本之前的jQuery存在一个原型污染漏洞CVE-2019-11358,PoC如下。

$.extend(true, {}, JSON.parse('{"__proto__": {"z": 123}}'))

console.log(z); // 123
merge.recursiveMerge :CVE-2020-28499

此 CVE 影响 2.1.1 以下的 merge 版本

测试代码:

const merge = require('merge');

const payload2 = JSON.parse('{"x": {"__proto__":{"polluted":"yes"}}}');

let obj1 = {x: {y:1}};

console.log("Before : " + obj1.polluted);
merge.recursive(obj1, payload2);
console.log("After : " + obj1.polluted);
console.log("After : " + {}.polluted);
lodash.defaultsDeep : CVE-2019-10744

2019 年 7 月 2 日,Snyk 发布了一个高严重性原型污染安全漏洞(CVE-2019-10744),影响了小于 4.17.12 的所有版本的 lodash。

Lodash 库中的 defaultsDeep 函数可能会被包含 constructor 的 Payload 诱骗添加或修改Object.prototype 。最终可能导致 Web 应用程序崩溃或改变其行为,具体取决于受影响的用例。以下是 Snyk 给出的此漏洞验证 POC:

const mergeFn = require('lodash').defaultsDeep;
const payload = '{"constructor": {"prototype": {"whoami": "Vulnerable"}}}'

function check() {
    mergeFn({}, JSON.parse(payload));
    if (({})[`a0`] === true) {
        console.log(`Vulnerable to Prototype Pollution via ${payload}`);
    }
  }

check();

console.log(Object.whoami);

从 Lodash 原型链污染到模板 RCE-安全KER - 安全资讯平台

按路径定义属性

修复

js原型链污染的修复相对较为容易

只要把常用的关键字都给waf掉就可以

const proto = ['__proto__', 'constructor', 'prototype']

套用到递归函数里面把它变安全

function safeMerge(target, source) {
    for (const key in source) {
        if (key === 'outputFunctionName'||key === '__proto__' || key === 'constructor'||key ==='prototype') {
            continue; // 跳过敏感属性
        }
        
        if (typeof target[key] === 'object' && typeof source[key] === 'object') {
            safeMerge(target[key], source[key]);
        } else {
            target[key] = value;
        }
    }
}

或者也可以从源头上解决问题,将有可能发生污染的对象obj做以下任一操作/重写:

  • let obj=Object.create(null);
  • const o=Object.freeze(obj);
  • let obj =new Map();
  • let obj =new Set();

Ruby

为什么我先讲Ruby而不是Python呢,因为Ruby也是OOP语言,并且更加贴近于js(虽说其实是先发现了Python的原型链才引出了Ruby的类污染

Python

后记

最近太忙了,比赛一大堆,还赶上开学,复现要等后续了,现在当务之急是赶快学到能够上场的程度。。