CVE-2019-11358 - jQuery 原型污染漏洞分析和修复建议

admin 2022年5月13日00:03:02安全漏洞评论22 views5406字阅读18分1秒阅读模式

更多全球网络安全资讯尽在邑安全

前置知识

JavaScript的类与实例

JavaScript中, 构造函数相当于类, 且可以将其实例化. 如果要定义一个类, 需要以定义构造函数的方式来定义. 在JavaScript中, 通过new关键词或Object.create()方法来进行对象实例的创建.

function Func() {
this.demo = 1;
}

var func = new Func();

CVE-2019-11358 - jQuery 原型污染漏洞分析和修复建议

JavaScript的prototype与proto

原型对象prototype

所有的JavaScript对象都会从一个原型对象prototype中继承属性和方法, JavaScript的每一个函数/类都有一个prototype属性, 用来指向该构造函数的原型.

例如前面的Func函数, 其prototype属性指向了该构造函数的原型本身.

CVE-2019-11358 - jQuery 原型污染漏洞分析和修复建议

proto属性

JavaScript的每一个实例对象都有一个__proto__属性指向该实例对象的原型. 例如前面的Func函数, 其实例对象func就有__proto__属性, 访问该属性可知是指向func这个实例对象的原型的.

CVE-2019-11358 - jQuery 原型污染漏洞分析和修复建议

总结

实例对象由函数生成, 实例对象的__proto__属性是指向函数的prototype属性的.

CVE-2019-11358 - jQuery 原型污染漏洞分析和修复建议

且在调用过程中, 无论是调用实例对象的__proto__属性, 还是调用构造函数/类的prototype属性, 它们均有一个__proto__属性指向Object, 而再往下调用__proto__属性就是调用Object.__proto__, 其值为null.

CVE-2019-11358 - jQuery 原型污染漏洞分析和修复建议

JavaScript原型链继承

原型链

原型链: 由于__proto__是任何JavaScript对象都有的属性, 而JavaScript中万物皆对象, 因此会形成一条__proto__连起来的链, 递归访问__proto__直至到终点即值为null. 上文中用到的Func构造函数和func实例对象的原型链如下:

func -> Func.prototype -> Object.prototype -> null

数组的原型链:

array -> Array.prototype -> Object.prototype -> null

日期的原型链:

data -> Date.prototype -> Object.prototype -> null

函数的原型链:

func -> function.prototype -> Object.prototype -> null

原型链的结构图如下图所示:

CVE-2019-11358 - jQuery 原型污染漏洞分析和修复建议

这里func是实例对象, Func.prototype是原型, 原型通过__proto__访问原型对象, 实例对象继承的就是原型及其原型对象的属性.

继承的查找过程

调用对象属性时会查找属性, 如果本身没有,则会去__proto__中查找, 也就是构造函数的显式原型中查找, 如果构造函数中也没有该属性, 因为构造函数也是对象, 也有__proto__, 那么会去__proto__的显式原型中查找, 一直到null, 这一过程很好的说明了原型才是继承的基础. 例如如下代码, Son类继承了Father类的last_name属性, 最后输出的是Name: Lisa Alpha.

function Father() {
this.first_name = 'Tome'
this.last_name = 'Alpha'
}

function Son() {
this.first_name = 'Lisa'
}

Son.prototype = new Father()

let son = new Son()
console.log(`Name: ${son.first_name} ${son.last_name}`)

对于对象son在调用son.last_name的时候, 实际上JavaScript引擎会进行如下操作:

  • 在对象son中寻找last_name.

  • 如果找不到, 则在son.__proto__中寻找last_name.

  • 如果仍然找不到, 则继续在son.__proto__.__proto__中寻找last_name.

  • 依次寻找, 直到找到null结束. 比如, Object.prototype__proto__就是null.

CVE-2019-11358 - jQuery 原型污染漏洞分析和修复建议

JavaScript原型链污染机制

JavaScript中访问一个对象的属性可以用param1.param2.param3或者praram1["param2"]["param3"]来访问. 由于对象是无序的, 当使用第二种方式访问对象时, 只能使用指明下标的方式去访问. 因此我们可以通过param1["__proto__"]的方式去访问其原型对象. 原型链污染一般会出现在对象或数组的键名或属性名可控, 而且是赋值语句的情况下.

在实际应用场景中, 当攻击者控制并修改了一个对象的原型, 那么将可以影响所有和这个对象来自同一个类、父祖类的对象, 这种攻击方式就是原型链污染.

从代码层面上来理解原型链污染机制, 代码如下所示:

function Func() {
this.param = 1;
}

var func = new Func();
console.log(func.param);

func.__proto__.__proto__["params"] = 2;

var funcs = new Func();
console.log(funcs.params);

CVE-2019-11358 - jQuery 原型污染漏洞分析和修复建议

JavaScript原型链污染应用场景

在上文中说到了, 原型链污染一般会出现在对象或数组的键名或属性名可控, 而且是赋值语句的情况下. 因此, 一般可以设置__proto__值的场景, 即能够控制数组(对象)的键名的操作的场景中容易出现JavaScript原型链污染. 这里主要有以下两种:

  • 对象merge, 即合并数组对象的操作.

  • 对象clone, 内核中就是将待操作的对象merge到一个空对象中.

以对象merge为例, 参考P神文章中用到的merge函数:

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

其在合并的过程中, 存在赋值的操作target[key] = source[key]. 因此, 当控制target的键key__proto__时就能对原型链进行污染. 示例Payload如下所示:

let demo1 = {};
let demo2 = {name : 'h3', "__proto__" : {age : 20}};

merge(demo1, demo2);
console.log(demo1.name, demo1.age);

let demo3 = {};
console.log(demo3.name, demo3.age);

CVE-2019-11358 - jQuery 原型污染漏洞分析和修复建议

可以看到该Payload并未污染成功, 这是因为, JavaScript创建demo2的过程中, proto已经代表demo2的原型, 此时遍历demo2的所有键名时, 拿到的是[name, age]proto并不是一个key, 自然也不会修改Object的原型. 此时, 需要将demo2实例对象那部分改为JSON格式, 修改后的Payload如下:

let demo1 = {};
let demo2 = JSON.parse('{"name" : "h3", "__proto__" : {"age" : 20}}');

merge(demo1, demo2);
console.log(demo1.name, demo1.age);

let demo3 = {};
console.log(demo3.age);

CVE-2019-11358 - jQuery 原型污染漏洞分析和修复建议

可以看到, 新建的demo3对象, 也存在age属性, 说明Object已经被污染了. 这是因为, JSON解析的情况下, __proto__会被认为是一个真正的"键名", 而不代表"原型", 所以在遍历demo2的时候会存在这个键. merge操作是最常见可能控制键名的操作, 也最能被原型链攻击, 很多常见的库都存在这个问题.

CVE-2019-11358漏洞分析

漏洞信息

CVE-2019-11358原型污染漏洞: 3.4.0版本之前的jQuery存在一个原型污染漏洞, 由攻击者控制的属性可被注入对象, 之后或经由触发JavaScript异常引发拒绝服务, 或篡改该应用程序源代码从而强制执行攻击者注入的代码路径.

CVE-2019-11358 - jQuery 原型污染漏洞分析和修复建议

PoC如下:

$.extend(true, {}, JSON.parse('{"__proto__": {"exploit": "h3rmesk1t"}}'))

console.log(exploit);

漏洞分析

查看3.3.1版本的JQuery源码, 漏洞点在在src/core.js文件中的extend函数. 如果该函数的第一个参数为布尔型的true, 合并操作就是深拷贝模式.

jQuery.extend = jQuery.fn.extend = function() {
var options, name, src, copy, copyIsArray, clone,
target = arguments[ 0 ] || {},
i = 1,
length = arguments.length,
deep = false;

// Handle a deep copy situation
if ( typeof target === "boolean" ) {
deep = target;

// Skip the boolean and the target
target = arguments[ i ] || {};
i++;
}

......

};

经过if函数判断后, 进入for循环, options取传入的参数arguments[i], 接着将options遍历赋值给copy, 即copy外部可控.

for ( ; i < length; i++ ) {

// Only deal with non-null/undefined values
if ( ( options = arguments[ i ] ) != null ) {

// Extend the base object
for ( name in options ) {
src = target[ name ];
copy = options[ name ];

......

}
}
}

接着判断copy是否是数组, 若是, 则调用jQuery.extend()函数, 该函数用于将一个或多个对象的内容合并到目标对象, 这里是将外部可控的copy数组扩展到target数组中; 若copy非数组而是个对象, 则直接将copy变量值赋值给target[name].

// Recurse if we're merging plain objects or arrays
if ( deep && copy && ( jQuery.isPlainObject( copy ) ||
( copyIsArray = Array.isArray( copy ) ) ) ) {

if ( copyIsArray ) {
copyIsArray = false;
clone = src && Array.isArray( src ) ? src : [];

} else {
clone = src && jQuery.isPlainObject( src ) ? src : {};
}

// Never move original objects, clone them
target[ name ] = jQuery.extend( deep, clone, copy );

// Don't bring in undefined values
} else if ( copy !== undefined ) {
target[ name ] = copy;
}

此时, 如果将name设置为__proto__, 则会向上影响target的原型, 进而覆盖造成原型污染. 而target数组时取传入的参数arguments[0]target = arguments[ 0 ] || {}.

target变量可以通过外部传入的参数arguments数组的第一个元素来设置target数组的键name对应的值为__proto__, 而options变量可通过外部传入的参数arguments[i]进行赋值, copy变量又是由options遍历赋值的, 进而导致copy变量外部可控, 最后会将copy合入或赋值到target数组中, 因此当target[__proto__] = 外部可控copy时, 即存在原型污染漏洞.

漏洞复现

var jquery = document.createElement('script');
jquery.src = 'https://code.jquery.com/jquery-3.3.1.min.js';

let exp = $.extend(true, {}, JSON.parse('{"__proto__": {"exploit": "h3rmesk1t"}}'));
console.log({}.exploit);

CVE-2019-11358 - jQuery 原型污染漏洞分析和修复建议

漏洞修复

jQuery3.4.0版本里修复了该漏洞, 通过判断属性中是否有__proto__, 如果有就跳过, 不合并.

CVE-2019-11358 - jQuery 原型污染漏洞分析和修复建议

原文来自: xz.aliyun.com

原文链接: https://xz.aliyun.com/t/11272

欢迎收藏并分享朋友圈,让五邑人网络更安全

CVE-2019-11358 - jQuery 原型污染漏洞分析和修复建议

欢迎扫描关注我们,及时了解最新安全动态、学习最潮流的安全姿势!


推荐文章

1

新永恒之蓝?微软SMBv3高危漏洞(CVE-2020-0796)分析复现

2

重大漏洞预警:ubuntu最新版本存在本地提权漏洞(已有EXP) 



原文始发于微信公众号(邑安全):CVE-2019-11358 - jQuery 原型污染漏洞分析和修复建议

特别标注: 本站(CN-SEC.COM)所有文章仅供技术研究,若将其信息做其他用途,由用户承担全部法律及连带责任,本站不承担任何法律及连带责任,请遵守中华人民共和国安全法.
  • 我的微信
  • 微信扫一扫
  • weinxin
  • 我的微信公众号
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年5月13日00:03:02
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                  CVE-2019-11358 - jQuery 原型污染漏洞分析和修复建议 http://cn-sec.com/archives/992409.html

发表评论

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: