前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >JavaScript原型、原型链及原型链污染

JavaScript原型、原型链及原型链污染

作者头像
字节脉搏实验室
发布2020-07-28 17:19:00
9600
发布2020-07-28 17:19:00
举报

文章源自【字节脉搏社区】-字节脉搏实验室

作者-purplet

0x00 前言

因为在CTF中时常也会考察原型链污染的问题,以前也一直让我捉襟见肘,一直没有系统的学习了解过JS原型的这些相关概念,因此写下本文,通过不断总结大佬的文章,写出自己对于此部分内容的理解。同时建议学习本文前要有对面向对象部分知识的一定理解(无论哪种语言)。

JavaScript没有”子类”和”父类”的概念,也没有”类”(class)和”实例(instance)的区分,全靠一种很奇特的”原型链”(prototype chain)模式,来实现继承。 在javascript中一切皆对象,因为所有的变量,函数,数组,对象 都始于object的原型即object.prototype。

0x01 JavaScript原型

一、对象和函数

在学习原型和原型链之前,首先一定要搞清楚对象和函数到底有什么区别和联系:

“对象是由函数创建的,而函数又是一种对象。”这样一句话要深刻记忆。

我们都知道JavaScript可以在浏览器中使用“F12”打开的控制台中输入JavaScript代码进行执行,但也要知道其实在浏览器中已经内置了几个全局函数可供随时调用,如:Number()、String()、Boolean()、Object()等 。

在JavaScript中声明一种数据类型的变量时其实有以下两种方式,而第一种可以更直观的体现对象和函数之间的关系,但第二种在各种语言中都较为常用。

但第二种虽然我们是用赋值形式创建的,但在JavaScript的内部,则仍然是通过调用函数来创建对象的。也就是说他们是一样的。

而对于“函数又是一种对象”这句话,也可以使用 instanceof 关键字来验证:

instanceof 的作用是判断一个对象是不是一个函数的实例。 比如 obj instanceof fn 实际上是判断fn的prototype是不是在obj的原型链上。 比如: obj.__proto__ === fn.prototype obj. __proto__.__proto__=== fn.prototype obj. __proto__ … __proto__ === fn.prototype 以上只要一个成立即可。

以上这个内容如果现在看不懂,不要着急后面会解释什么是原型、原型链和__proto__属性。

二、原型(共有属性):__proto__

首先定义个对象:var str = new String(‘hello’); 输出看看该对象中包含哪些属性:

再创建一个num对象。

可以看到两个不同的对象,但都存在__proto__属性。再看以下例子:

肯定会疑惑valueOf和toString方法是哪里来的呢,其实这两个方法也都是在__proto__属性中带来的,打开__proto__的指向箭头就可以看到

总结:不只是str和num对象,每个对象中都有__proto__属性,JavaScript将这些对象(如:Number(函数也是对象))中的共有属性,拿了出来,全都集中到一个新的对象(num)中。而新对象中,就保存着一个__proto__,指向这个原对象。

而__proto__所指向的这个原对象,也叫做原型对象。后文会继续解释。

而既然存在共有属性,那也一定存在独有属性。string对象有string对象的属性是其他对象没有的;number对象有number对象的属性是其他对象没的;boolean对象有boolean对象的属性是其他对象没有的;以此类推。 那这个“独有对象”又是保存在哪儿的呢?我们来看看什么是prototype。

三、函数的原型(prototype)

上面说到,__proto__是每个对象都有的属性,那么要区别记住的是prototype是函数才有的属性。

再继续了解prototype属性前再补充学习几个知识点:

1-什么是构造函数

Person就是一个构造函数,我们使用 new 创建了一个实例对象 person

2-constructor属性

接着按照刚刚的例子查看Person和person的结构输出。

可以看到person的构造函数Person存在的原型包含一个constructor属性。接下来记住一句话:“每个原型(prototype)都有一个 constructor 属性指向关联的构造函数,实例原型指向构造函数 ”。再看person的结果中__proto__属性所指的constructor属性也是与之关联的构造函数,而对于该例中,它的构造函数就是function Person()函数,因此整个结构图如下图所示。

所以以下代码是成立的

0x02 JavaScript原型链

其实当认真理解完上面的内容,原型链的概念就基本清楚了,以下总结出几点:

1-从上面的代码中可以看到,创建person对象虽然使用的是由构造函数Person创建,但是对象创建出来之后,这个person对象其实已经与Person构造函数没有任何关系了,person对象的__proto__属性指向的是Person构造函数的原型对象(Person.prototype)。 2-如果使用new Person()创建多个对象person1、person2、person3,则多个对象都会同时指向Person构造函数的原型对象。 3-我们可以手动给这个原型对象添加属性和方法,那么person1、person2、person3这些对象就会共享这些在构造函数的原型对象中添加的属性和方法。 4-如果我们访问person中的一个属性name,如果在person对象中找到,则直接返回。如果person对象中没有找到,则直接去person对象的__proto__属性指向的原型对象中查找,如果查找到则返回。(如果原型中也没有找到,则继续向上找原型的原型—原型链),直到最高级Object的__proto__为Null为止。 5-如果通过person对象添加了一个属性name,则通过person访问name时,就相当于屏蔽了原型中的属性name,输出的是person对象中的name值 6-通过person对象只能读取构造函数的原型中的属性name值,而不能修改原型中的属性name值。person.name = “purplet”; 并不是修改了原型中的值,而是在person对象中给添加了一个属性name。

下面可以把原型、原型链的关系当作一个公式一般去记忆:

由于__proto__是任何对象都有的属性,而JavaScript里万物皆对象,所以会形成一条__proto__连起来的链条,但递归访问__proto__必须最终到头,其终点是Null 当JavaScript引擎查找对象的属性时,先查找对象本身是否存在该属性,如果不存在,会在原型链上查找,但不会查找自身的prototype,如图所示。

0x03 JavaScript原型链污染

在看懂原型链的那几点内容后,其实就应该可以理解什么是原型链污染了,就是修改其构造函数的原型中的属性值,使其他通过该构造函数实例出的对象也具有该属性值。

可以看到我们修改成功了,新生成的 foo2 对象也具有hacker 属性,如果给foo1再往上加一个__proto__就可以修改(添加)Object的属性了。

那么在哪些情况下原型链会存在污染?

这里我引用郁离歌师傅的博客内容了。

我们思考一下,哪些情况下我们可以设置__proto__的值呢?其实找找能够控制数组(对象)的“键名”的操作即可:

  • 对象merge
  • 对象clone(其实内核就是将待操作的对象merge到一个空对象中)

以对象merge为例,我们想象一个简单的merge函数:

在合并的过程中,存在赋值的操作target[key] = source[key],那么,这个key如果是__proto__,是不是就可以原型链污染呢?

我们用如下代码实验一下:

结果是,合并虽然成功了,但原型链没有被污染:

这是因为,我们用JavaScript创建o2的过程(let o2 = {a: 1, "__proto__": {b: 2}})中,__proto__已经代表o2的原型了,此时遍历o2的所有键名,你拿到的是[a, b]__proto__并不是一个key,自然也不会修改Object的原型。

那么,如何让__proto__被认为是一个键名呢?

我们将代码改成如下:

可见,新建的o3对象,也存在b属性,说明Object已经被污染:

这是因为,JSON解析的情况下,__proto__会被认为是一个真正的“键名”,而不代表“原型”,所以在遍历o2的时候会存在这个键。

merge操作是最常见可能控制键名的操作,也最能被原型链攻击,很多常见的库都存在这个问题。

0x04 文章参考

https://www.zhihu.com/tardis/sogou/art/44035916

https://www.jianshu.com/p/be7c95714586

https://www.leavesongs.com/PENETRATION/javascript-prototype-pollution-attack.html#0x04

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-07-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 字节脉搏实验室 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 0x00 前言
  • 0x01 JavaScript原型
  • 二、原型(共有属性):__proto__
  • 三、函数的原型(prototype)
  • 0x02 JavaScript原型链
  • 0x03 JavaScript原型链污染
  • 0x04 文章参考
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档