JavaScript object are working on the
prototype-based inheritance model. The
prototype is a hierarchy where the object itself goes first, but then it's prototype is then checked, if a value cannot be found. For more on this, read the article linked or
Mozilla docs.
The idea behind this vulnerability is to poison the prototype (of an object) in order to cause bad behavior to occur. In the past, prototype pollution has been used in NodeJS by object being parsed literally by the program (along with a merge operation) to cause crazy behavior in the program.
In this case, the author is attacking client-side HTML sanitizers. Because of this, we have complete control over the prototypes of objects.
The first parser (sanitize-html) is found to be vulnerable to prototype pollution by setting Object.prototype['*'] = ['onload']. By setting the * on the prototype (a check for all tags usable) will pass. Now, the onload attribute will be a valid tag! Also, this library was attempting to protect itself from prototype pollution but failed.
The second parser (xss) had a field called options which dictates what can and cannot be used. By polluting a field called whiteList by setting options.whiteList it is possible to set arbitrary tags/attributes for this field.
The third parer (dompurify) is very similar to the second parser issue. A field called ALLOWED_ATTR on an object was used in order to add more attributes than what should be allowed for an image. Now, when the parsing for the image takes place, the attributes were not removed because they were added to the prototype object.
The final victim (Closure by Google) has a custom format for specifying what is allowed and disallowed (which uses objects). By setting the prototypes of this object with the custom format to include src and onerror, it was trivial to get XSS.
The author created an
automated way for finding prototype pollution methods for the HTML sanitization library! This can be found at
pollute.js.
Although the author uses JavaScript in order to pollute the objects, this is STILL possible to trigger without JavaScript. In particular, insecure parsing of the JSON is often required.
Overall, great read that takes old research and brings it to be new again! :)