Book a call Start a run

The XSS inside your favorite iOS app

How a default WKWebView implementation makes thousands of apps vulnerable to sandboxed HTML/CSS injection or XSS.

RenwaV12 Renwa, V12 The XSS inside your favorite iOS app

Summary

The default configuration of a WKWebView honors Content-Disposition as a sandbox instead of a download, rendering the file’s contents inside the WebView, which allows HTML/CSS spoofing and in some cases XSS, whatever filename the server suggests. This makes any downloadable file render in the context of the hosting origin.

Technical Details

RFC 6266 §4.2 states “If the disposition type matches “attachment” (case-insensitively), this indicates that the recipient should prompt the user to save the response locally, rather than process it normally (as per its media type).” What this means is that if the Content-Disposition response header had “attachment” in the application should tell the user to save the file instead of rendering, about all modern browsers follow this but what about WebKit?

To create a simple WebKit webview that shows contents of a url inside your browser is something like this:

import UIKit
import WebKit
class VC: UIViewController {
override func loadView() {
let web = WKWebView()
view = web
web.load(URLRequest(url: URL(string: "https://example.com")!))
}
}

This simple usage of WKWebView() is vulnerable to sandboxed HTML/CSS rendering without JS when Content-Disposition is present or in some cases even XSS.

Impact

If a website hosted user uploads to the same-origin site or a subdomain and it’s opened inside a vulnerable application; the severity would be Medium and could lead to:

  • HTML/CSS spoofing inside a trusted origin
  • Same-origin GET request CSRF using <img src=>

But in some rare cases we saw that JS execution was also possible which would lead to a full XSS on the origin inside the vulnerable application.

POC

Open in any vulnerable iOS or other WebKit app:

https://webkitxss.v12.sh/

Suggested Patch

Manually override the default behavior to download the file or just cancel it

func webView(_ webView: WKWebView,
decidePolicyFor navigationResponse: WKNavigationResponse,
decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
let cd = (navigationResponse.response as? HTTPURLResponse)?
.value(forHTTPHeaderField: "Content-Disposition")?.lowercased() ?? ""
if cd.contains("attachment") || !navigationResponse.canShowMIMEType {
decisionHandler(.download) // or .cancel
return
}
decisionHandler(.allow)
}

Responsible Disclosure

Before writing this blog we contacted tens of vendors and informed them about the vulnerability, some of them acted responsibly and patched the bug but unfortunately most of them didn’t acknowledge the issue which was disappointing. It was either they didn’t understand the bug or just didn’t care. Cases that make this attack a deliverable payload and risk to the user along with some reported and patched cases:

iOS dApp Browser Wallets

Almost every mobile wallet nowadays has a dApp explorer which allows you to browse dApps and connect your wallet within the app, these web browsers are just embedded webviews and are vulnerable to this attack. An attacker could use this vulnerability to create a spoofing airdrop campaign by hosting a malicious file on a trusted site and pretending to be from that project by matching the origin with the spoofing claim.

A fake Google ETH giveaway rendered inside an iOS dApp wallet under a trusted Google origin

Social Media apps

Most social media apps are using embedded browsers, when you send a link to a friend and they tap on it the link will be opened inside the app and not the default web browser, these so called in-app browsers are also using WKWebView and vulnerable too. An attacker would post a malicious file on Facebook, Snapchat, Pinterest, Instagram… and whenever a user opens the link they will get the file rendered on the trusted site instead of downloading or showing an error.

A malicious attachment rendered in the X in-app browser instead of being downloaded

PlayStation 5 and PlayStation 4 browsers

The PlayStation browser also uses WebKit engine, which we found the same vulnerability in, reported and was awarded $10,000 for a high severity console bug. In the POC below we upload a malicious .html file to Discord which will be hosted on cdn.discordapp.com then send it to the victim to steal their credentials. Patched in PS5 software version 25.06-12 and now you get an error instead of rendering the downloadable file.

Firefox iOS and Firefox Focus

Both Firefox iOS browsers were also vulnerable, these were assigned CVE-2025-55030 and CVE-2025-55032 with a bounty of $6,000

The attachment-rendering vulnerability demonstrated in Firefox Focus and Firefox for iOS

Vulnerable Applications (reported and NOT patched)

  • Twitter (X)
  • Google Gemini
  • Facebook
  • Instagram
  • Snapchat
  • Messenger
  • OKX
  • Binance
  • Trust Wallet
  • Zeal Wallet
  • Reddit
  • TikTok
  • Telegram
  • YouTube
  • TronLink
  • OneKey Wallet
  • Pinterest
  • MyEtherWallet
  • Threads
  • Petra Wallet

Type to search.