(I wrote a blog post for this, English, 中文)
One day, I was searching for a way to bypass Angular sandbox, and I found this interesting post: H5SC Minichallenge 3: "Sh*t, it's CSP!"
One of the solutions caught my eye, it uses Angular combining Prototype.js to bypass the sandbox, like this:
{{ $on.curry.call().alert(1337) }}
Typically, you can not access window
or document
in the expression. The reason why above work is because Prototype.js adds a new function called curry
to Function.prototype
, here is the source code:
function curry() {
if (!arguments.length) return this;
var __method = this, args = slice.call(arguments, 0);
return function() {
var a = merge(args, arguments);
return __method.apply(this, a);
}
}
When we call a function via .call
or .apply
without any arguments, this
will be the global object which is window
by default in non-strict mode. So, $on.curry.call()
returns window
.
After seeing this bypass, I wondered, "Are there any other libraries that have the same behavior?", so I started my research.
There are four steps:
- Find all libraries hosted on cdn.js
- Retrieve all data, including the files of the library, via cdn.js API
- Run a headless browser to automatically detect if the library "pollutes" the prototype by adding a new function to it.
- Find the exact file which pollutes the prototype because one library can have multiple files.
Timewise, I only check the latest version of every library.
There are 4290 libraries on cdn.js at the time of writing, and 74 of them add new functions directly to the prototype(which is not recommended because it pollutes the prototype):
(You can find the detail here: pollutes.json)
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- RGraph@606
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
- [email protected]
There are 12 libraries that can help you to get window
by just calling it:
[
{
"url": "https://cdnjs.cloudflare.com/ajax/libs/asciidoctor.js/1.5.9/asciidoctor.min.js",
"functions": [
"Array.prototype.$concat",
"Array.prototype.$push",
"Array.prototype.$append",
"Array.prototype.$rotate!",
"Array.prototype.$shuffle!",
"Array.prototype.$sort",
"Array.prototype.$to_a",
"Array.prototype.$to_ary",
"Array.prototype.$unshift",
"Array.prototype.$prepend",
"String.prototype.$initialize",
"String.prototype.$chomp",
"String.prototype.$force_encoding",
"Function.prototype.$to_proc"
]
},
{
"url": "https://cdnjs.cloudflare.com/ajax/libs/jquery-ui-bootstrap/0.5pre/third-party/jQuery-UI-Date-Range-Picker/js/date.js",
"functions": [
"Number.prototype.milliseconds",
"Number.prototype.millisecond",
"Number.prototype.seconds",
"Number.prototype.second",
"Number.prototype.minutes",
"Number.prototype.minute",
"Number.prototype.hours",
"Number.prototype.hour",
"Number.prototype.days",
"Number.prototype.day",
"Number.prototype.weeks",
"Number.prototype.week",
"Number.prototype.months",
"Number.prototype.month",
"Number.prototype.years",
"Number.prototype.year"
]
},
{
"url": "https://cdnjs.cloudflare.com/ajax/libs/ext-core/3.1.0/ext-core.min.js",
"functions": [
"Function.prototype.createInterceptor"
]
},
{
"url": "https://cdnjs.cloudflare.com/ajax/libs/datejs/1.0/date.min.js",
"functions": [
"Number.prototype.milliseconds",
"Number.prototype.millisecond",
"Number.prototype.seconds",
"Number.prototype.second",
"Number.prototype.minutes",
"Number.prototype.minute",
"Number.prototype.hours",
"Number.prototype.hour",
"Number.prototype.days",
"Number.prototype.day",
"Number.prototype.weeks",
"Number.prototype.week",
"Number.prototype.months",
"Number.prototype.month",
"Number.prototype.years",
"Number.prototype.year"
]
},
{
"url": "https://cdnjs.cloudflare.com/ajax/libs/json-forms/1.6.3/js/brutusin-json-forms.min.js",
"functions": [
"String.prototype.format"
]
},
{
"url": "https://cdnjs.cloudflare.com/ajax/libs/inheritance-js/0.4.12/inheritance.min.js",
"functions": [
"Object.prototype.mix",
"Object.prototype.mixDeep"
]
},
{
"url": "https://cdnjs.cloudflare.com/ajax/libs/melonjs/1.0.1/melonjs.min.js",
"functions": [
"Array.prototype.remove"
]
},
{
"url": "https://cdnjs.cloudflare.com/ajax/libs/mootools/1.6.0/mootools-core-compat.min.js",
"functions": [
"Array.prototype.erase",
"Array.prototype.empty",
"Function.prototype.extend",
"Function.prototype.implement",
"Function.prototype.hide",
"Function.prototype.protect"
]
},
{
"url": "https://cdnjs.cloudflare.com/ajax/libs/mootools/1.6.0/mootools-core.min.js",
"functions": [
"Array.prototype.erase",
"Array.prototype.empty",
"Function.prototype.extend",
"Function.prototype.implement",
"Function.prototype.hide",
"Function.prototype.protect"
]
},
{
"url": "https://cdnjs.cloudflare.com/ajax/libs/opal/0.3.43/opal.min.js",
"functions": [
"Array.prototype.$extend",
"Array.prototype.$to_proc",
"Array.prototype.$to_a",
"Array.prototype.$collect!",
"Array.prototype.$delete_if",
"Array.prototype.$each_index",
"Array.prototype.$fill",
"Array.prototype.$insert",
"Array.prototype.$keep_if",
"Array.prototype.$map!",
"Array.prototype.$push",
"Array.prototype.$shuffle",
"Array.prototype.$to_ary",
"Array.prototype.$unshift",
"String.prototype.$as_json",
"String.prototype.$extend",
"String.prototype.$intern",
"String.prototype.$to_sym",
"Number.prototype.$as_json",
"Number.prototype.$extend",
"Number.prototype.$to_proc",
"Number.prototype.$downto",
"Number.prototype.$nonzero?",
"Number.prototype.$ord",
"Number.prototype.$times",
"Function.prototype.$include",
"Function.prototype.$module_function",
"Function.prototype.$extend",
"Function.prototype.$to_proc"
]
},
{
"url": "https://cdnjs.cloudflare.com/ajax/libs/prototype/1.7.3/prototype.min.js",
"functions": [
"Array.prototype.clear",
"Number.prototype.times",
"Function.prototype.curry"
]
},
{
"url": "https://cdnjs.cloudflare.com/ajax/libs/tmlib.js/0.5.2/tmlib.min.js",
"functions": [
"Array.prototype.swap",
"Array.prototype.eraseAll",
"Array.prototype.eraseIf",
"Array.prototype.eraseIfAll",
"Array.prototype.clear",
"Array.prototype.shuffle",
"Number.prototype.times",
"Number.prototype.upto",
"Number.prototype.downto",
"Number.prototype.step",
"Object.prototype.$extend",
"Object.prototype.$safe",
"Object.prototype.$strict"
]
}
]
All JSON files are placed under /data
folder.
libs.json
is all the data from the algolia of cdnjslibDetail.json
is all the data from cdnjs APIpollutes.json
is all the libraries that pollute the prototypepollutesForWindow.json
is all the files that pollute the prototype and can get window by calling it
Install dependencies:
npm i
Then, run getLibs.js
to get all library's name from algolia, it generates data/libs.json
:
node getLibs.js
Next, run getLibDetail.js
to get all file paths from cdnjs API, it generates data/libDetail.json
:
node getLibDetail.js
After getting all the files, we can run scan.js
to test all libraries and generate data/pollutes.json
and data/pollutesForWindow.json
node scan.js