README.md
Rendering markdown...
diff --git a/index.js b/index.js
new file mode 100644
index 0000000..732ca8d
--- /dev/null
+++ b/index.js
@@ -0,0 +1,25 @@
+async function main() {
+ try {
+ const tough = require("tough-cookie");
+ const jar = new tough.CookieJar(undefined, { rejectPublicSuffixes: false });
+
+ jar.setCookieSync(
+ "Slonser=polluted; Domain=__proto__; Path=/notauth",
+ "https://__proto__/admin"
+ );
+ jar.setCookieSync(
+ "Auth=Lol; Domain=google.com; Path=/notauth",
+ "https://google.com/"
+ );
+ const pollutedObject = {};
+ if (pollutedObject["/notauth"] === undefined) {
+ throw new Error("EXPLOIT FAILED");
+ }
+ console.log("Polluted object:", pollutedObject["/notauth"], "EXPLOITED SUCCESSFULLY");
+
+ } catch (error) {
+ console.error("EXPLOIT FAILED");
+ }
+}
+
+main();
diff --git a/lib/memstore.js b/lib/memstore.js
index d2b915c..5d63448 100644
--- a/lib/memstore.js
+++ b/lib/memstore.js
@@ -28,6 +28,9 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
+
+// Change the objects to be prototype-free, breaking the prototype chain and preventing pollution.
+
'use strict';
var Store = require('./store').Store;
var permuteDomain = require('./permuteDomain').permuteDomain;
@@ -36,7 +39,7 @@ var util = require('util');
function MemoryCookieStore() {
Store.call(this);
- this.idx = {};
+ this.idx = Object.create(null);
}
util.inherits(MemoryCookieStore, Store);
exports.MemoryCookieStore = MemoryCookieStore;
@@ -115,10 +118,10 @@ MemoryCookieStore.prototype.findCookies = function(domain, path, cb) {
MemoryCookieStore.prototype.putCookie = function(cookie, cb) {
if (!this.idx[cookie.domain]) {
- this.idx[cookie.domain] = {};
+ this.idx[cookie.domain] = Object.create(null);
}
if (!this.idx[cookie.domain][cookie.path]) {
- this.idx[cookie.domain][cookie.path] = {};
+ this.idx[cookie.domain][cookie.path] = Object.create(null);
}
this.idx[cookie.domain][cookie.path][cookie.key] = cookie;
cb(null);
@@ -150,7 +153,7 @@ MemoryCookieStore.prototype.removeCookies = function(domain, path, cb) {
};
MemoryCookieStore.prototype.removeAllCookies = function(cb) {
- this.idx = {};
+ this.idx = Object.create(null);
return cb(null);
}
diff --git a/package.json b/package.json
index 8af9909..a2a7269 100644
--- a/package.json
+++ b/package.json
@@ -73,6 +73,7 @@
},
"dependencies": {
"psl": "^1.1.28",
- "punycode": "^2.1.1"
+ "punycode": "^2.1.1",
+ "tough-cookie": "file:tough-cookie-2.5.0-PATCHED.tgz"
}
}
diff --git a/test/ietf_data/parser.json b/test/ietf_data/parser.json
index c40ad54..9b6aab3 100644
--- a/test/ietf_data/parser.json
+++ b/test/ietf_data/parser.json
@@ -11,7 +11,7 @@
{
"test": "0002",
"received": [
- "foo=bar; Expires=Fri, 07 Aug 2019 08:04:19 GMT"
+ "foo=bar; Expires=Fri, 07 Aug 2026 08:04:19 GMT"
],
"sent": [
{ "name": "foo", "value": "bar" }
@@ -707,7 +707,7 @@
{
"test": "COMMA0006",
"received": [
- "foo=bar; Expires=Fri, 07 Aug 2019 08:04:19 GMT"
+ "foo=bar; Expires=Fri, 07 Aug 2026 08:04:19 GMT"
],
"sent": [
{ "name": "foo", "value": "bar" }
@@ -716,7 +716,7 @@
{
"test": "COMMA0007",
"received": [
- "foo=bar; Expires=Fri 07 Aug 2019 08:04:19 GMT, baz=qux"
+ "foo=bar; Expires=Fri 07 Aug 2026 08:04:19 GMT, baz=qux"
],
"sent": [
{ "name": "foo", "value": "bar" }
diff --git a/test/prototype_pollution_test.js b/test/prototype_pollution_test.js
new file mode 100644
index 0000000..e1a4abc
--- /dev/null
+++ b/test/prototype_pollution_test.js
@@ -0,0 +1,30 @@
+const vows = require('vows');
+const assert = require('assert');
+const tough = require('../lib/cookie'); // use patched tough-cookie;
+const CookieJar = tough.CookieJar;
+
+vows.describe('Prototype pollution test').addBatch({
+ "when setting a cookie with the domain __proto__": {
+ topic: function() {
+ const jar = new CookieJar(undefined, {
+ rejectPublicSuffixes: false
+ });
+ // try to pollute the prototype
+ jar.setCookieSync(
+ "Slonser=polluted; Domain=__proto__; Path=/notauth",
+ "https://__proto__/admin"
+ );
+ jar.setCookieSync(
+ "Auth=Lol; Domain=google.com; Path=/notauth",
+ "https://google.com/"
+ );
+ this.callback();
+ },
+ "results in a cookie that is not affected by the attempted prototype pollution should be undefined": function() {
+ const pollutedObject = {};
+ assert.strictEqual(pollutedObject["/notauth"], undefined);
+ }
+ }
+ })
+ .export(module);
+
diff --git a/tough-cookie-2.5.0-PATCHED.tgz b/tough-cookie-2.5.0-PATCHED.tgz
new file mode 100644
index 0000000..ee7ffc9
Binary files /dev/null and b/tough-cookie-2.5.0-PATCHED.tgz differ