Skip to content

aszx87410/cdnjs-prototype-pollution

Repository files navigation

Who pollutes your prototype?

(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.

Methodology

There are four steps:

  1. Find all libraries hosted on cdn.js
  2. Retrieve all data, including the files of the library, via cdn.js API
  3. Run a headless browser to automatically detect if the library "pollutes" the prototype by adding a new function to it.
  4. 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.

Result

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)

  1. [email protected]
  2. [email protected]
  3. [email protected]
  4. [email protected]
  5. RGraph@606
  6. [email protected]
  7. [email protected]
  8. [email protected]
  9. [email protected]
  10. [email protected]
  11. [email protected]
  12. [email protected]
  13. [email protected]
  14. [email protected]
  15. [email protected]
  16. [email protected]
  17. [email protected]
  18. [email protected]
  19. [email protected]
  20. [email protected]
  21. [email protected]
  22. [email protected]
  23. [email protected]
  24. [email protected]
  25. [email protected]
  26. [email protected]
  27. [email protected]
  28. [email protected]
  29. [email protected]
  30. [email protected]
  31. [email protected]
  32. [email protected]
  33. [email protected]
  34. [email protected]
  35. [email protected]
  36. [email protected]
  37. [email protected]
  38. [email protected]
  39. [email protected]
  40. [email protected]
  41. [email protected]
  42. [email protected]
  43. [email protected]
  44. [email protected]
  45. [email protected]
  46. [email protected]
  47. [email protected]
  48. [email protected]
  49. [email protected]
  50. [email protected]
  51. [email protected]
  52. [email protected]
  53. [email protected]
  54. [email protected]
  55. [email protected]
  56. [email protected]
  57. [email protected]
  58. [email protected]
  59. [email protected]
  60. [email protected]
  61. [email protected]
  62. [email protected]
  63. [email protected]
  64. [email protected]
  65. [email protected]
  66. [email protected]
  67. [email protected]
  68. [email protected]
  69. [email protected]
  70. [email protected]
  71. [email protected]
  72. [email protected]
  73. [email protected]
  74. [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"
    ]
  }
]

Files

All JSON files are placed under /data folder.

  • libs.json is all the data from the algolia of cdnjs
  • libDetail.json is all the data from cdnjs API
  • pollutes.json is all the libraries that pollute the prototype
  • pollutesForWindow.json is all the files that pollute the prototype and can get window by calling it

Development

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

About

Find all libraries on cdn.js that pollute your prototype

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published