Despite being declared dead years ago, reverse tab nabbing exploits are still alive and well today. The vulnerability has changed form, but the attack surface has remained and is arguably even more dangerous today.
How reverse tab nabbing works
The exploit is based on a simple browser feature: When opening a new tab, the new tab receives a reference to the parent tabs window object as window.opener, but this only works for links opening new tabs with target="_blank" and rel="opener".
In the past, rel="opener" was the default value when no rel attribute was present, but that was fixed years ago.
Nowadays, reverse tab nabbing is a lot more constrained, because it requires a literal rel="opener" attribute to be set on the <a> element, and on top of that CORS (Cross-Origin Resource Sharing) prevents reading any data on window.opener even if you manage to place a correct link. Trying to read values like window.opener.location.href will throw a DOMException, for example:
DOMException: Permission denied to get property "href" on cross-origin objectDevelopers cannot be blamed for assuming the vulnerability is fixed with implicit default CORS and rel="noopener" rules applied throughout modern browsers, but there is one exception that keeps the exploit viable in practice.
The exploit remains
While it is often a good idea to rely on CORS, the spec explicitly chose not to apply restrictions to navigation elements.
The window.opener.location itself is part of that navigation API, as setting it to a string would navigate that tab to the new URL. Even with fully locked down CORS rules, a child tab can redirect the parent tab with a single line of code:
window.opener.location = "https://example.com";Assuming the issue was resolved long ago led to a state where many developers do not think about this edge case anymore, and decide to only strip javascript or executable attributes like src, onerror etc. Since rel cannot be used to execute code directly, it is often overlooked in naive sanitization of user inputs.
Suppose an attacker found a site example.com where users may leave comments, and those comments do not strip rel attributes. They could then insert a link to their own website attacker.com like this:
<a href="https://attacker.com" target="_blank" rel="opener">Click here</a>And on attacker.com, they host a website with some bait information, while also running a single line of javascript in the background:
window.opener.location = "https://attacker.com/fake";This redirects the original/parent tab to attacker.com/fake while the user is busy looking at the newly opened tab on attacker.com.
If the attacker has for example created a phishing site on attacker.com/fake that looks like the original, but perhaps shows a login form with a message like "Your session has expired, please log in again", the user may trust it implicitly. This trust is what makes reverse tab nabbing so dangerous today: even tech savvy users do not re-check the browser address bar when coming back to an already opened tab; they might check once when they click on a link, but the tab retains blind trust once opened.
Blind exploits in the wild
Malicious advertisement networks have been caught abusing reverse tab nabbing for click farming. If a website has embedded their ad widget, that widget contains a rel="opener" link to a proxy site, which hijacks the parent tab and redirects it to one ad page, then redirects the newly opened tab to another.
Effectively, they have turned one user click on an ad into two user visits to different ad partner sites, doubling their income. Many browsers allow only opening a single tab per user click, but hijacking the parent does not count towards that limit.
Such ad networks are typically associated with less trustworthy sites, as using them does damage user retention for the website embedding the ad widget: once a user clicks on the ad, they are redirected off their page, and often do not return.
Targeted phishing attacks
A more dangerous, although less reusable attack is to copy the original site visually and add some fake login form, or simply inject a hook that catches all forms before submission and forwards form contents to an attacker database before submitting them.
Such attacks require the attacker to first craft the counterfeit website and make adjustments for the phishing attack, then host it on a special url/path on their own server, and finally add the XSS rel="opener" link to the victim site.
As the manual user interaction is necessary (no tab nabbing without the user clicking the XSS link), fake limited offers or prize contests are typical bait offered by attackers.
More sophisticated attacks have rarely been seen where the attacker server fetched the victim site in realtime, injected a form submission hook script and even replacing all navigation links with spoofed links to their servers to hijack navigation as well. This malicious page is then hosted on the attackers server and the parent tab is redirected to it, while the user is busy in the newly opened tab from the target="_blank" link. Since the malicious copy site is hosted on the attackers server, neither CORS nor CSP rules of the victim site apply to it, allowing them to run arbitrary scripts and send data wherever they like.
Luckily, this approach breaks apart for sites that hide content behind a login (as logged in user and logged out server fetch will differ visually), but removing the manual effort of first having to clone and adjust the victim page is still a huge help for attackers.
Preventing the attack
The only certain way to prevent the attack is to be paranoid when sanitizing user input anywhere on your site. Select a subset of allowed elements and what attributes are allowed for each element, and strip anything else from the input, no matter how harmless you may think it is. Neither CORS nor CSP (Content Security Policy) headers have any effect on the remaining exploit variant.
You may have seen universal advice to always set rel="noreferrer noopener" on all links on your site, but noopener is already the default value, and setting them does not mitigate the exploit at all - only preventing user input from setting rel="opener" does.
Trying to blacklist certain attributes or URLs is an easy way to overlook something and introducing an XSS vulnerability, so always whitelist only the elements/attributes you genuinely need. It is better to be strict now than to be sorry later.