Prototype Pollution in domcloud/dom-portal

Valid

Reported on

Jan 21st 2022


Prototype Pollution in dom-portal

Reported on Jan 20th 2022 | Timothee Desurmont

Description

The function unflatten located in domainbio.php could potentially leed to prototype pollution and givie an attacker unprivilladge access to sensitive information.

Proof of Concept

Create a file called poc.html, paste below code and open it in the browser.

<!doctype html>
<html lang="en">
    <head>
    <meta charset="utf-8">
    <script src="https://code.jquery.com/jquery-3.5.0.js"></script>
    </head>
    <body>
        
        <form name="domainBio">
        <div><input input type="text" name="domain.owner.fname"><div>
            <input type="submit" name="g" value="Submit" id="g">
        </div>
        </form>
        
        <script>
        // https://unpkg.com/json-unflat@1.0.1/index.js
        function unflatten(n) {
            var t = {};
            var r = function(r) {
            var a, f = r.split(".");
            f.map(function(i, u) {
                0 == u && (a = t),
                a[i] ? (a = a[i]) :
                f.length === u + 1 ?
                (a[i] = n[r]) :
                ((a[i] = {}), (a = a[i]));
            });
            };
            for (var a in n) r(a);
            return t;
        }

        $("form").submit(function( e ) {
            var obj = {};
            var data = $(this).serializeArray().reduce((m, o) => (m[o.name] = o.value, m), {});
            var b = unflatten(data).domain;
            console.log(`Object has been polluted:`, obj.polluted);
            e.preventDefault();
        });
        </script>
    
    </body>
</html>
  1. Open your browser console by clicking on F12,
  2. Rename the input as domain.__proto__.polluted from the inspector (*),
  3. Enter true in the inupt,
  4. And click on the submit button.

The console will log the following message:

Object has been polluted: true

The obj - declared but to whom we never add any properties (or methods) - has inherided the property polluted from Object (on the top of the prototype inheritance chain).

Notes (*): renaming the input as domain.constructor.prototype.polluted in point number 2 will have the exact same effect.

Proof of Fix

Replace the unflatten function with the following code.

function unflatten(n) {
    var t = {};
    var r = function(r) {
      var a, f = r.replace(/(__proto__|constructor)/g, "").split(".");
      f.map(function(i, u) {
        0 == u && (a = t),
          a[i] ? (a = a[i]) :
          f.length === u + 1 ?
          (a[i] = n[r]) :
          ((a[i] = {}), (a = a[i]));
      });
    };
    for (var a in n) r(a);
    return t;
  }

We need to escape the terms terms __proto__ and constructor from the string n being passed as a variable to the function unflatten.

We are processing your report and will contact the domcloud/dom-portal team within 24 hours. a year ago
Timothee Desurmont modified the report
a year ago
We created a GitHub Issue asking the maintainers to create a SECURITY.md a year ago
Timothee
a year ago

Researcher


Hi, the owner has already been informed of the vulnerability by email (as requested in his readme file). I have the submitted a PR to patch the code which has been merged. The owner of the application gave me his consent to publish the report on hunter.dev. permalink to the vulnerability, and link to the merge can be found in the code above. Owner email address is in the readme file

We have contacted a member of the domcloud/dom-portal team and are waiting to hear back a year ago
domcloud/dom-portal maintainer
a year ago

Hi, the owner here. I appreciate your finding so far.

But it's hard for me to accept that this is a high-level security vulnerability. I only use this code only in the client browser. If the vuln requires a user to edit in the inspector, well it is called self XSS. Anyone can do that.

If the vuln comes in the server files or misinterpretation from URL paths, I would really appreciate it as a high-risk vuln.

I consider this as low risk unless there's something else is found combined with an exploit found from this report.

Timothee Desurmont modified the report
a year ago
Timothee
a year ago

Researcher


Hi Wildan,

Thank you for your reply,

I have changed the Base Score Metrics and hope that the overall CVSS score of 4 (Medium) is now acceptable (I could not go lower, otherwise the score would be null):

  • Attack Vector: Local (not bound to network stack);

  • Attack Complexity: High (requires attacker to invest some more mesurable in preparation of the execution);

  • Privileges Required: Low (requires privileges that provide basic user capabilities;

  • User Interaction: None (the vulnerability system can be exploited without interaction from the user);

  • Scope: Unchanged (can only affect resources managed by the same authority);

  • Confidentiality Impact: None (there is no loss of confidentiality);

  • Integrity Impact: Low (Modification of data is possible, but the attacker does not have control over the consequence of a modification);

  • Availability Impact: Low (There is no direct serious consequence on the impacted component).

With best regards,

Timothee

domcloud/dom-portal maintainer validated this vulnerability a year ago
Timothee Desurmont has been awarded the disclosure bounty
The fix bounty is now up for grabs
domcloud/dom-portal maintainer marked this as fixed in 1.0.0 with commit 58755b a year ago
The fix bounty has been dropped
This vulnerability will not receive a CVE
as3617
a year ago

Hello, it seems possible to bypass the patch.

function unflatten(n) {
    var t = {};
    var r = function(r) {
      var a, f = r.replace(/(__proto__|constructor)/g, "").split(".");
      f.map(function(i, u) {
        0 == u && (a = t),
          a[i] ? (a = a[i]) :
          f.length === u + 1 ?
          (a[i] = n[r]) :
          ((a[i] = {}), (a = a[i]));
      });
    };
    for (var a in n) r(a);
    return t;
  }

If you look at the patched source code, you can see that it removes proto, constructor. This patch can be bypassed.

Instead of domain.__proto__.polluted, try putting domain.__pr__proto__oto__.polluted. __proto___ between domain.__pr__proto__oto__.polluted will be removed. so domain.__proto__.polluted will be created and finally prototype pollution will be possible.

Proof of Fix

function unflatten(n) {
    const UNSAFE_KEYS = ["__proto__", "constructor", "prototype"];
    var t = {};
    var r = function(r) {
    var a, f = r.split(".");
    f.map(function(i, u) {
        if (UNSAFE_KEYS.includes(i)) { return; }
        0 == u && (a = t),
        a[i] ? (a = a[i]) :
        f.length === u + 1 ?
        (a[i] = n[r]) :
        ((a[i] = {}), (a = a[i]));
    });
    };
    for (var a in n) r(a);
    return t;
}
Timothee
a year ago

Researcher


👍

to join this conversation