# Bug Bounty Guest Post: Local File Read via Stored XSS in The Opera Browser

Stored javascript: URI in Opera Pinboards executed in privileged opera: scheme, letting an attacker screenshot local files like /etc/passwd and exfiltrate them.

- Payout: $4,000
- Severity: high
- Program: Opera
- Credited: Renwa
- Disclosed: 2021-09-08
- Write-up quality: 8/10
- Original: https://blogs.opera.com/security/2021/09/bug-bounty-guest-post-local-file-read-via-stored-xss-in-the-opera-browser/
- Library page: https://bugbountylibrary.com/r/blog%3Ablogs-opera-com-security-2021-09-bug-bounty-guest-post-local-file-read-via-stored-xss-in-the-opera-browser
- Tags: domain:browser impact:data-leak kind:bounty platform:blog sev:high tech:chromium technique:chained technique:source-review vuln:info-disclosure vuln:xss

**Target:** Opera browser Pinboards feature (opera:pinboards privileged scheme)
**Root cause:** Pinboard API accepted a javascript: URI as a pin link without scheme validation, and the privileged opera:pinboards page rendered it as a clickable link, executing script in a context with native function access and file: scheme loading.
**Steps:**
1. Observe pin creation POST to `pinboard.opera-api.com/v1/items` with JSON `link.href` field.
2. Submit pin with `link.href` set to `javascript:@opera.com`; it appears as clickable link in opera:pinboards.
3. Refine payload to `javascript:'@opera.com/';alert(1)` to get execution.
4. Bypass `target=_blank` by Ctrl+Click or middle-clicking the link.
5. From privileged context call `chrome.tabs.create` to open `file:///etc/passwd`.
6. Call `opr.pinboardPrivate.getThumbnail` to screenshot the tab.
7. Base64-encode PNG and send to attacker server.
**How found:** Noticed Opera redirects pinboard.opera.com to privileged opera:pinboards; proxied pin creation to find the API; hypothesized a javascript: URI in link.href would execute in the privileged scheme; iterated payloads until alert fired, then explored native functions available to the scheme.
**Exploitation:** Attacker creates a pinboard pin whose link is a javascript: URI; victim middle-clicks it, executing script in opera:pinboards context. Script opens file:///etc/passwd in a new tab, uses opr.pinboardPrivate.getThumbnail to capture a screenshot, and exfiltrates the base64 PNG to the attacker's server.
**Escalation:** Chained stored XSS in privileged scheme with native thumbnail API and file: scheme access to convert a popup into local file read and exfiltration.
**Insight:** When a vendor redirects a web origin to a privileged internal scheme (opera:, chrome:), test whether user-controlled data stored via the public API is rendered as links in that privileged page; javascript: URIs there inherit native APIs and file: access. Also remember target=_blank can be bypassed with middle/Ctrl+click.
