原型链污染

最近在打newstarCTF时遇到了原型链污染的题目,于是决定研究一下这个漏洞

参考文章https://juejin.cn/post/6963950629240733727

对象合并

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] } } }

问问gpt

img

认识javascript中的原型链

在javaScript中,实例对象与原型之间的链接,叫做原型链。 其基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。 然后层层递进,就构成了实例与原型的链条,这就是所谓原型链的基本概念。

其中有三个名词

· 隐式原型:所有引用类型(函数、数组、对象)都有 proto 属性,例如arr.proto

· 显式原型:所有函数拥有prototype属性,例如:func.prototype

· 原型对象:拥有prototype属性的对象,在定义函数时被创建

下面这张图片是对原型链之间关系的描述

img

原型链的查找机制

原型链是 JavaScript 中一种用于查找属性和方法的机制。在 JavaScript 中,每个对象都有一个指向另一个对象的链接,这个链接就是原型链。当你试图访问一个对象的属性或方法时,如果该对象本身没有这个属性或方法,JavaScript 引擎会沿着原型链向上查找,直到找到匹配的属性或方法,或者到达原型链的末尾。

以下是原型链查找机制的一般流程:

\1. 对象自身属性: 当你访问一个对象的属性时,JavaScript 首先检查该对象本身是否有这个属性。如果有,直接返回这个属性的值。

\2. 原型链: 如果对象本身没有这个属性,JavaScript 引擎会沿着对象的原型链向上查找。每个对象都有一个指向其原型的引用,这个引用可以通过 proto 属性访问(在现代 JavaScript 中,推荐使用 Object.getPrototypeOf() 方法)。引擎将继续检查原型对象是否有这个属性。

\3. 递归: 如果在当前原型对象上找不到属性,引擎会继续在原型对象的原型上查找,直到找到匹配的属性或到达原型链的末尾(即 null)。如果一直找到原型链的末尾仍然没有找到属性,返回 undefined

这种机制使得 JavaScript 中的对象能够共享属性和方法。例如,如果你在一个对象的原型上定义了一个方法,所有基于该原型创建的对象都可以访问这个方法。

以下是一个简单的例子,演示了原型链查找机制:

// 创建一个构造函数 function Person(name) { this.name = name; }

// 在 Person 的原型上定义一个方法 Person.prototype.sayHello = function() { console.log("Hello, my name is " + this.name); };

// 创建一个 Person 对象 var person = new Person("Sdegree");

// 调用对象上的方法,它会首先查找对象本身是否有该方法, // 如果没有,就会在原型链上查找,最终找到并执行 person.sayHello(); // 输出: Hello, my name is Sdegree

用处

在开发中,常常会用到 toString()、valueOf()等方法,array类型的变量拥有更多的方法,例如forEach()、map()、includes()等等。例如声明了一个arr数组类型的变量,arr变量却可以调用如下图中并未定义的方法和属性。

1.继承: 原型链是 JavaScript 中实现继承的基础。通过在对象的原型上定义属性和方法,可以让其他对象通过原型链继承这些属性和方法。这种继承方式使得对象之间可以共享代码,提高代码的复用性。

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

Animal.prototype.sayHello = function() { console.log("Hello, I'm " + this.name); };

function Dog(name, breed) { Animal.call(this, name); this.breed = breed; }

Dog.prototype = Object.create(Animal.prototype); Dog.prototype.constructor = Dog;

Dog.prototype.bark = function() { console.log("Woof! I'm a " + this.breed); };

var myDog = new Dog("Buddy", "Golden Retriever"); myDog.sayHello(); // 输出: Hello, I'm Buddy myDog.bark(); // 输出: Woof! I'm a Golden Retriever

2.属性和方法的共享: 在原型链上定义的属性和方法可以被所有基于该原型创建的对象共享。这样,如果需要在多个对象之间共享相同的行为,可以将这些行为放在它们的共同原型上,而不是每个对象都拥有一份独立的拷贝。

3.动态性: JavaScript 允许在运行时动态地修改对象的原型,从而实现动态的属性和方法添加。这种动态性使得对象可以在运行时适应不同的需求。

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

var person1 = new Person("Alice"); var person2 = new Person("Bob");

// 在运行时动态添加方法 Person.prototype.sayHello = function() { console.log("Hello, my name is " + this.name); };

person1.sayHello(); // 输出: Hello, my name is Alice person2.sayHello(); // 输出: Hello, my name is Bob

4.内置对象和函数的扩展: JavaScript 中的许多内置对象和函数(如 ArrayStringFunction 等)都使用原型链来提供额外的功能和方法。通过原型链,你可以轻松地扩展这些内置对象的功能,以满足特定需求。

总体而言,原型链在 JavaScript 中是实现继承、共享属性和方法、动态性等特性的关键机制,它使得 JavaScript 的对象系统更加灵活和强大。

风险点分析&原型链污染漏洞原理

举一个例子

var a = {name: 'Sdegree', age: 18}; a.proto.role = 'administrator' var b = {} b.role // output: administrator

这段代码会输出administrator

我们会发现,这里a中是没有role的,我们在这里使用隐式原型增加了一个role属性,但是我们却可以通过原型链读到对象啊在原型链上的赋值administrator

这里我们使用了proto,它指向的原型对象是可读可写的,那么我们是不是就可以实现增、删、改原型链上方法或属性的操作,达到污染程序原型链的操作而进行dos、越权等攻击。

实战演练

OtenkiGirl

img

拿到题目首先就看到这个题目和污染相关。

下载附件后,我们翻译hint里的内容后,得知这道题目的关键不是sql注入,而是和routes这个文件里的文件有关。这里我们看到,代码都是由js所写,又与污染有关,首先想到nodejs原型链污染。

"proto":{"min_public_time":"2000-07-09"},