少女祈祷中...

js原型链污染

基础概念辨析

原型

js中一切皆对象,js的每个对象都有一个属性原型(prototype),原型本身也是一个对象,因此该原型也有一个prototype指向上层原型,直到为null,这样就构成了原型链

简单来说,原型链是javascript中类继承的一种机制,每个对象都有对应的原型,储存了定义的属性和方法,之中包含了prototype,指向上一级的原型。最上层Object原型中的prototype为null

特性

类定义中的属性,会在创建对象时在对象中创建,类定义的方法而是存在于原型之中。当使用没有定义的属性和方法时,就会顺着原型链一直寻找,直到为undefined,用此来实现继承,子类能用父类的属性和方法

示例

function Person(name) {
this.name = name;
}

Person.prototype.greet = function () {
console.log(`Hello, my name is ${this.name}`);
};

const person1 = new Person('Alice');
person1.greet(); // 输出 "Hello, my name is Alice"

img

person1对象原型对象展示

prototype与__proto__

(1)所有引用类型(函数,数组,对象)都拥有__proto__属性(隐式原型),用来查看其原型

(2)所有函数拥有prototype属性(显式原型)(仅限函数)

简单来说,proto 属性是指向该对象的原型,而 prototype属性是用于创建该对象的构造函数的原型。

Function 与function

function是一个定义函数的关键字,Function是代表所有函数的内置原型对象

每一个js 的function都是Function对象,Function是js内置对象,用以实现很多基本功能,如Nunmber,toString

constructor是一个对象数据属性属性,创建对象后,访问constructor属性,可以返回构造该对象的来源(不是该对象的原型链上级)

new

new constructor[([arguments])]

参数

constructor

一个指定对象实例的类型的类或函数。

描述

new 关键字会进行如下的操作:

  1. 创建一个空的简单 JavaScript 对象(即 {});
  2. 为步骤 1 新创建的对象添加属性 __proto__,将该属性链接至构造函数的原型对象;
  3. 将步骤 1 新创建的对象作为 this 的上下文;
  4. 如果该函数没有返回对象,则返回 this

原型链污染

当我们添加或者修改上层原型的一些属性,子类都又调用该恶意属性时,子类调用的属性已经被我们篡改,造成原型链污染,原型链污染可能会造成命令执行(rce)或者任意文件读取,越权等等

示例

function merge(target, source) {
for (let key in source) {
if (key in source && key in target) {
// 如果target与source有相同的键名 则让target的键值为source的键值
merge(target[key], source[key])
} else {
target[key] = source[key] // 如果target与source没有相通的键名 则直接在target新建键名并赋给键值
}
}
}
let o1 = {}
let o2 = JSON.parse('{ "a": 1, "__proto__": { "b": 2} }');
merge(o1, o2)
console.log(o1.a, o1.b)

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

漏洞点

一般对对象进行复制拷贝,如merge函数或者copy函数,会将传入的json数据拷贝至已创建对象,此时可能会产生原型链污染

ejs模板基于原型链污染的rce

直接上POC

a; return global.process.mainModule.constructor._load('child_process').execSync('whoami'); //

参考学习:https://evi0s.com/2019/08/30/expresslodashejs-%e4%bb%8e%e5%8e%9f%e5%9e%8b%e9%93%be%e6%b1%a1%e6%9f%93%e5%88%b0rce/

扩展

对于toUpperCase()函数

字符"ı"、"ſ" 经过toUpperCase处理后结果为 "I"、"S"

对于toLowerCase

字符"K"经过toLowerCase处理后结果为"k"(这个K不是K)

详情可见p神的文章https://www.leavesongs.com

python中也存在原型链污染

可参考文章:https://tttang.com/archive/1876

ctf例题:https://chenlvtang.top/2021/08/17/NodeJs%E5%8E%9F%E5%9E%8B%E9%93%BE%E6%B1%A1%E6%9F%93%E7%9A%84%E5%AE%9E%E8%B7%B5

防御

1.Object.freeze()冻结原型

Object.freeze(Object.prototype);
Object.freeze(Object)

冻结原型后,无法添加新的原型至原型链

2.对JSON输入验证

npm上有很多库,如avj,可以对JSON数据验证,排除json中数据中不需要的属性

或者在复制对象时,遍历键名时候,检查__proto__和prototype

3.使用map代替{}

4.使用Object.create()安全创建对象

这样创建的对象没有属性

5.node.js中可以通过disable-proto直接禁止操作原型链