hunt-clickjacking

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

What is Clickjacking

什么是 Clickjacking

Clickjacking (UI Redressing) lets an attacker load a target page inside a transparent iframe on a malicious site. The victim sees the attacker's decoy UI but clicks the hidden target UI beneath it. No JavaScript on the target is required.
Highest-value targets:
  • Login / authentication pages — force login with attacker credentials
  • Money transfer / checkout / "confirm payment" buttons
  • Account settings (email change, password change, 2FA disable)
  • OAuth / social-login "Authorize app" confirmation dialogs
  • Admin actions (delete, promote user, change role)
Clickjacking(UI Redressing)允许攻击者在恶意站点中通过透明 iframe 加载目标页面。受害者看到的是攻击者设置的诱饵 UI,却实际点击了其下方隐藏的目标 UI。目标页面无需运行任何 JavaScript。
最高价值的目标:
  • 登录 / 身份验证页面——强制受害者使用攻击者的凭据登录
  • 资金转账 / 结算 / “确认支付”按钮
  • 账户设置(更改邮箱、更改密码、禁用 2FA)
  • OAuth / 社交登录的“授权应用”确认对话框
  • 管理操作(删除用户、提升用户权限、更改角色)

Protection Headers

防护响应头

Two mechanisms prevent framing:
X-Frame-Options: DENY              # strongest — blocks all framing
X-Frame-Options: SAMEORIGIN        # allows same-origin frames only
Content-Security-Policy: frame-ancestors 'none'     # CSP equivalent of DENY
Content-Security-Policy: frame-ancestors 'self'     # CSP equivalent of SAMEORIGIN
If NEITHER is present, the page is frameable from any origin.
以下两种机制可以防止页面被嵌入框架:
X-Frame-Options: DENY              # strongest — blocks all framing
X-Frame-Options: SAMEORIGIN        # allows same-origin frames only
Content-Security-Policy: frame-ancestors 'none'     # CSP equivalent of DENY
Content-Security-Policy: frame-ancestors 'self'     # CSP equivalent of SAMEORIGIN
如果两者均不存在,则该页面可以被任意来源的页面嵌入框架。

How to Test

测试方法

Header-absence is the trigger for investigation, not the finding. Two steps:
Step 1 — Header check (screening). Fetch the target page and inspect the response headers:
curl -sI https://target.example/account/transfer | grep -iE 'x-frame-options|content-security-policy'
If BOTH
X-Frame-Options
and CSP
frame-ancestors
are absent, the page is a candidate. If either is present and restrictive (
DENY
/
SAMEORIGIN
/
frame-ancestors 'none'|'self'
), stop — it's protected.
Step 2 — Prove it actually frames and clicks (required for a real finding). Build a minimal PoC and load it in a real browser:
html
<!doctype html>
<h1>Win a prize — click below</h1>
<iframe src="https://target.example/account/transfer"
        style="opacity:0.1;position:absolute;top:0;left:0;width:1000px;height:800px"></iframe>
Confirm ALL of the following, or it is not exploitable:
  • The page actually renders inside the iframe (no framebusting JS that blanks/redirects it — e.g.
    if(top!==self)
    breakout, or a
    Sec-Fetch-Dest
    /JS frame check).
  • The sensitive action still works while framed — critically, the action must succeed cross-site. If it relies on a session cookie set
    SameSite=Lax
    or
    SameSite=Strict
    (the modern default), the cookie is not sent on the cross-site framed request, and the clickjack fails. Verify the victim's authenticated state carries into the frame.
  • The target is a state-changing action (transfer, settings/email/password change, 2FA disable, OAuth authorize, admin action), not a read-only page.
Strategy: target the most sensitive action pages first — severity scales directly with what the victim is tricked into doing.
缺少响应头只是触发调查的条件,而不是漏洞发现本身。测试分为两个步骤:
第 1 步——响应头检查(初筛)。 抓取目标页面并检查响应头:
curl -sI https://target.example/account/transfer | grep -iE 'x-frame-options|content-security-policy'
如果 X-Frame-Options 和 CSP frame-ancestors 两者均不存在,则该页面是一个候选项。如果其中任一响应头存在且具有严格限制(
DENY
/
SAMEORIGIN
/
frame-ancestors 'none'|'self'
),则可以停止——页面已受到保护。
第 2 步——证明页面确实可以被嵌入且点击能够生效(构成实际漏洞发现的必要条件)。 构建最小化 PoC,并在真实浏览器中加载:
html
<!doctype html>
<h1>Win a prize — click below</h1>
<iframe src="https://target.example/account/transfer"
        style="opacity:0.1;position:absolute;top:0;left:0;width:1000px;height:800px"></iframe>
必须确认以下所有条件,否则不构成可利用漏洞:
  • 页面确实能够在 iframe 内渲染(不存在会将页面清空或重定向的 framebusting JS——例如
    if(top!==self)
    跳出检测,或
    Sec-Fetch-Dest
    /JS 框架检查)。
  • 敏感操作在被嵌入框架时仍然有效——关键是该操作必须能够跨站成功。如果操作依赖设置为
    SameSite=Lax
    或
    SameSite=Strict
    的会话 Cookie(现代默认值),该 Cookie 不会在跨站框架请求中发送,Clickjacking 攻击就会失败。需要验证受害者的身份验证状态能够传递到框架内。
  • 目标是状态变更操作(转账、设置更改、邮箱或密码更改、禁用 2FA、OAuth 授权、管理操作),而不是只读页面。
策略: 优先测试最敏感的操作页面——漏洞严重程度直接取决于受害者被诱骗执行的操作。

False Positives

误报

  • Public, read-only pages (home/marketing) lacking frame protection are low/informational — no sensitive action to redress.
  • APIs and non-HTML endpoints (JSON, images) are not clickjacking targets.
  • Header-absence alone is NOT a finding. SameSite cookies, framebusting JS, or the lack of any sensitive framed action can each fully defeat it — which is why Step 2 is mandatory.
  • 对于首页 / 营销页等缺少框架防护的公开只读页面,风险通常为低危或信息级——没有可供劫持的敏感操作。
  • API 和非 HTML 端点(JSON、图片等)不是 Clickjacking 的测试目标。
  • 仅凭响应头缺失不能构成漏洞发现。 SameSite Cookie、framebusting JS 或框架内不存在敏感操作,任何一项都可能完全阻止攻击——因此第 2 步是必需的。

Proof Requirements

证明要求

A valid clickjacking report shows: (1) the target page rendered inside an attacker-controlled iframe in a real browser, (2) a sensitive state-changing action reachable by a framed click while the victim is authenticated (cookies survive the cross-site context), and (3) a screenshot/recording of the overlay. Reporting missing headers with no working frame PoC is a documentation-quality issue, not a vulnerability.
一份有效的 Clickjacking 报告应证明:(1)目标页面在真实浏览器中成功渲染于攻击者控制的 iframe 内;(2)受害者在已通过身份验证时,可以通过框架内的点击触发敏感的状态变更操作(Cookie 在跨站上下文中仍会发送);(3)提供覆盖层的截图或录屏。如果仅报告响应头缺失,却没有可正常工作的框架嵌入 PoC,则这属于文档质量问题,而不是安全漏洞。