RyanJarv/cdn-proxy
GitHub: RyanJarv/cdn-proxy
利用 CDN 共享 IP 池特性绕过 IP 白名单限制,实现绕过 WAF/速率限制直接访问源站的渗透测试工具
Stars: 263 | Forks: 23
# CDN 代理
[](https://github.com/RyanJarv/cdn-proxy/actions/workflows/python-tests.yml)
[](https://github.com/RyanJarv/cdn-proxy/actions/workflows/go-tests.yml)
这是一个可供 Web 应用渗透测试人员使用的工具,用于创建目标网站的副本,并禁用 CDN 和 WAF 的限制。
相关博客文章:[通过备用域名路由绕过 CDN WAF](https://blog.apnic.net/2022/05/19/bypassing-cdn-wafs-with-alternate-domain-routing/)。
## 目录
- [概述](#overview)
- [前置条件](#prerequisites)
本仓库包含三个用于不同任务的独立工具,如下所示。
- [CDN Proxy](#cdn-proxy) -- 自动化部署基础设施。
- [安装说明](#installation)
- [CloudFront](#cloudfront)
- [CloudFlare](#cloudflare)
- [CDN Scanner](#cdn-scanner) -- 用于发现受影响源站的扫描器。
- [安装说明](#installation)
- [使用说明](#usage)
- [扫描器概述](#scanner-overview)
- [CloudFront Scanner](#cloudfront-scanner)
- [CloudFlare Scanner](#cloudflare-scanner)
- [Burp Suite Extension](#burp-suite-extension) -- 允许你通过本地浏览器浏览受影响的源站。
## 概述
cdn-proxy 是一组用于绕过 IP 允许列表的工具,这些列表旨在限制源站仅接受来自共享 CDN 的请求。
通过直接访问绕过 CDN 层的保护已有充分记录,然而对此常见的应对措施是设置 IP 允许列表,仅允许来自 CDN 共享网络范围的请求。由于共享 CDN 使用公共 IP 池进行源站请求,通过在同一网络上的第二个由攻击者控制的分配来路由流量,可以绕过这些 IP 限制。
当第二个(代理)分配的配置由攻击者控制时,请求将不受预期分配中相同的安全要求约束。WAF、速率限制、过滤以及预期分配中实施的任何身份验证都不会应用于通过代理分配的请求。
通过分配的请求也可能在某种程度上由攻击者控制,但这很大程度上取决于可用的配置选项。在 CloudFront 的情况下,请求的某些部分以及预期源站可以通过 Lambda@Edge 函数中的代码进行控制,这允许控制 X-Forwarded-For 标头,从而可能允许在后端 Web 应用中伪造 IP,以及快速扫描大量源站以确定它们是否易受此攻击。

https://user-images.githubusercontent.com/4079939/130310987-3ad7e7b4-db7f-4a6e-b511-14ef7a9dbab4.mov
## 前置条件
* 使用 CloudFlare 或 CloudFront 来过滤或限制流量。
* 这可能也适用于其他 CDN,目前只是支持这两个。
* 你知道源站(后端)IP。
* 可以使用 [cdn-scanner](#cdn-scanner) 来查找只能通过 CDN 网络访问的源站。
* 源站允许来自 CDN 网络使用的共享 IP 范围的访问。
此外,在 CloudFlare 的情况下,源站 Web 应用需要使用默认虚拟主机来提供网站服务。这是因为无法在发往源站的请求上任意设置 Host 标头。当你拥有 CloudFlare 企业版账户时,即使不是这种情况也应该可以执行此攻击,但是 cdn-proxy 目前不支持此功能。要手动执行此操作,你需要确保 CloudFlare 配置在请求通过 CloudFlare 网络时正确设置了 host 标头。
# cdn-proxy
## 安装说明
注意:目前 PyPi 版本不是最新版本,可能对你无效。我会尽快修复,在此期间你可以直接从 GitHub 仓库安装/更新。
```
pip3 install -U git+https://github.com/RyanJarv/cdn-proxy.git@main
cdn-proxy --help
```
## 使用说明
cdn-proxy 命令的结构是 `cdn-proxy `,其中:
不同提供商的具体过程和所需选项有所不同,请参阅下面的提供商部分以获取更多详细信息。
## CloudFront
### 使用说明
cdn-proxy cloudfront -h
```
Usage: cdn-proxy cloudfront [OPTIONS] COMMAND [ARGS]...
Manage CloudFront distributions
Options:
--region REGION Sets the AWS region. [default: us-east-1]
--profile PROFILE Shared credential profile to use.
-h, --help Show this message and exit.
Commands:
create Create a new CloudFront distribution and Lambda@Edge function...
delete Disable and delete the specified distribution.
status Get the status of the CloudFront deployment.
```
### 概述
CloudFront 模块将设置充当通过 CloudFront 代理的分配。使用 CloudFront 时无需指定目标,并且同一个分配可以用于多个目标。这是因为可以通过在发往 CDN 的客户端请求中设置 Cdn-Proxy-Origin 和 Cdn-Proxy-Host 标头,来动态设置每个请求的源站和 host 标头。X-Forwarded-For 标头也将从客户端传递,如果未设置,则在发往源站的请求中将默认为随机 IP 地址,这在某些情况下允许绕过应用端的 IP 速率限制。
在使用 `cdn-proxy cloudfront create` 部署 CloudFront 配置后,你可以导航到该分配以找到一个包含更多控制请求的标头信息和示例的帮助页面。
除了使用 curl 手动发出请求外,你还可以使用 CDN Proxy 的 [Burp Suite Extension](#burp-suite-extension) 通过 CloudFront 代理代理所有 Burp 请求。除其他功能外,这允许你像通常通过内置 Burp 浏览器那样浏览任何仅向 CloudFront IP 暴露的站点。
要扫描仅允许通过 CDN 网络请求的源站 IP,你可以使用 [cdn-scanner](#cdn-scanner) 或 CloudFront 分配域名上托管的(功能较少的)基于 Web 的扫描器。你将在创建分配后的 cdn-proxy 输出中找到此域名。
使用 curl 向特定源站发出请求可以通过以下方式完成:
```
curl -H 'Cdn-Proxy-Origin: Origin_IP' Distribution_Domain_Name
```
其中 "Origin_IP" 是目标源站 IP,"Distribution_Domain_Name" 是使用 `cdn-proxy cloudfront create` 命令创建的 CloudFront 分配的域名。也可以通过运行 `cdn-proxy cloudfront status` 找到该域名。
更多 curl 示例可以在此子命令部分的末尾找到。
在后端,CloudFront 需要源站的域名,为了解决这个问题,与该分配关联的 Lambda@Edge 函数会动态设置源站,并将它在 Cdn-Proxy-Origin 标头中找到的 IP 地址转换为等效的 sslip.io 子域名。sslip.io 子域名将采用 `1-1-1-1.sslip.io` 的格式,并映射到相应的 IP,实际上允许我们在 CloudFront 仅接受域名时使用 IP。这一切都是透明发生的,此处提及仅供参考。
CloudFront 模块也相当慢,可能需要一段时间来设置和拆除,但是你可以通过更改 `Cdn-Proxy-Origin` 标头对任何目标重用同一个分配。
### 标头
* Cdn-Proxy-Origin
* 此标头是必需的,需要设置为请求通过 CloudFront 后应路由到的源站。你可以将其设置为主机名或 IP,但是由于 CloudFront 仅支持源站的主机名,任何 IP 都将使用 sslip.io 替换为等效的域名。
* Cdn-Proxy-Host
* 发往源站的请求中 Host 标头的值。此标头是可选的,但建议设置。如果未设置,它将默认为 Cdn-Proxy-Origin 的值。
* X-Forwarded-For
* 如果已设置,则传递给源站(像大多数其他未列出的标头一样)。如果未设置此标头,则在发往源站的请求中默认为随机 IP 地址。这在某些情况下允许绕过后端基于 IP 的速率限制。
* 你可能还想尝试将其设置为受信任的值,例如 127.0.0.1 或另一个内部 IP 地址,以暴露 Web 应用程序中任何受 IP 限制的管理或调试页面(相比于 CloudFront/WAF 中强制执行的限制,使用此代理时这些限制已被禁用)。
* 有时会在源站前面使用多个缓存代理,例如 CloudFront 路由到 Varnish,再路由到 nginx。在这种情况下,当你使用 nginx 服务作为源站时,你可能需要设置目标 IP,向此标头添加多个 IP,并将你想伪造的 IP 放在最左边(例如:X-Forwarded-For: 127.0.0.1, 172.32.10.10)。也有可能这里的第二个 IP 需要是源站内部网络上的受信任 IP。
### Curl -- 无 Host 标头示例
在这里,我们只是在请求通过 CloudFront 后将其转发到公共 ifconfig.me 服务。返回的 IP 将是我们的请求从 CloudFront 网络发出的源 IP。我们不需要设置 Cdn-Proxy-Host,因为无论 host 标头设置为什么,ifconfig.me 的响应都是相同的。
```
curl -H 'Cdn-Proxy-Origin: ifconfig.me' -H 'Cdn-Proxy-Host: ifconfig.me' XXXXXXXXXXXXX.cloudfront.net
```
### Curl -- EC2 源站示例
你更有可能运行这样的命令,其中 Cdn-Proxy-Origin 是特定的后端服务器,而 Cdn-Proxy-Host 是其运行的网站的域名。如果 Cdn-Proxy-Host 设置不正确,你可能无法访问该站点,但这取决于服务器配置。
```
curl -H 'Cdn-Proxy-Origin: ec2-XX-XX-XX-XX.us-west-2.compute.amazonaws.com' -H 'Cdn-Proxy-Host: example.com' XXXXXXXXXXXXX.cloudfront.net
```
## CloudFlare
### 使用说明
```
Usage: cdn_proxy cloudflare [OPTIONS] COMMAND [ARGS]...
Manage CloudFlare distributions
Options:
--token REGION Sets the AWS region. [required]
--zone-name REGION Sets the AWS region. [required]
--help Show this message and exit.
Commands:
create Create a new CloudFront distribution and Lambda@Edge function...
delete Disable and delete the specified distribution.
list List CloudFront distributions IDs and targets created with...
scan HTTP scan of IP's both directly and via proxy...
```
### 概述
*警告:请不要在生产域名上使用此功能。*
cdn-proxy 的 CloudFlare 子命令比 cloudfront 子命令简单得多,因为它仅用于添加、删除和列出现有域中的代理 DNS 记录。它无法为你创建此域,并且你无法动态指定目标。
这需要在 CloudFront 中已经创建了一个现有域,这是因为 CloudFlare 不会像 CloudFront 那样为你分配临时域。该域需要已注册并处于活动状态,但应是一个仅用于 cdn-proxy 的备用域。你还需要禁用该域上的所有安全功能,以免它们应用于通过它的请求。
### 注意事项
此模块的一个缺点是,它在请求通过 CDN 后不会将 host 标头设置为目标域。这意味着此模块仅对未设置默认虚拟主机来托管你 targeting 的网站的源站有效。在客户端请求中设置 host 标头将不会按预期工作,因为这会导致请求被路由到不同的 CloudFront 配置。
如果你拥有 CloudFlare 企业版账户,则可以解决此限制。但是 cdn-proxy 目前不处理此问题,但你可以使用区域配置中的转换规则手动配置此功能。如果你希望看到 cdn-proxy 增加对此的支持,请在 GitHub issue 中告诉我们。
第二个限制是你无法完全控制发往源站的请求中的 X-Forwarded-For 标头。CloudFlare 将其视为受保护的标头。
# CDN 扫描器
问:为什么要将其拆分为另一个应用程序?为什么不将此功能包含在 cdn-proxy python 脚本中?
答:扫描器实际上最初是用 python 编写的,并作为子命令添加到 cdn-proxy 中。然而 Python 的并行和 async 让我焦头烂额,这一切都在一次愤怒驱动的开发会话中被重写了。既然它是用 GoLang 编写的,以一种实际上有意义的方式,它比以前...快...得多。在使用 CloudFront 时,客户端速度非常有用,这是因为我们可以真正以 CloudFront 能处理的最快速度发送请求..这应该相当快。
值得注意的是,python 版本之所以慢,更多是因为我缺乏编写快速/并发/异步 python 代码的知识,而不是 python 本身的问题,尽管我相信两者都在这里帮了不少忙。
## 安装说明
```
GOPRIVATE=github.com/RyanJarv/cdn-proxy go install github.com/RyanJarv/cdn-proxy
mv ~/go/bin/cdn-{proxy,scanner}
export PATH=$PATH:~/go/bin
cdn-scanner -h
```
## 使用说明
```
Usage of cdn-scanner: /var/folders/t6/z_k2wx1j3dbcf83ym91bspf40000gr/T/go-build1086559817/b001/exe/main [-domain string] [-report string] [-workers int] [args...] ...
-domain string
The domain to route requests through, fetched from AWS if not specified.
-report string
JSON report file output location.
-workers int
Maximum number of workers used to make requests, defaults to 100. (default 100)
Sub Commands
cloudfront [IP/Hostname/CIDR/file path] ...
-profile string
Proxy domain AWS Profile, not used if -proxyDomain is passed.
-region string
Proxy domain AWS Region, not used if -proxyDomain is passed
cloudflare [IP/Hostname/CIDR/file path] ...
```
## 扫描器概述
cloudflare 或 cloudfront 子命令都接受 IP、主机名、CIDR 列表,或者可选的包含额外 IP、主机名或 CIDR 列表的文件。然后扫描每个网络资产,分别针对 http 和 https 进行一次,包括直接访问以及通过指定 CDN 代理访问,然后比较响应以确定该资产是否启用了 IP 允许列表。
例如,如果直接 http 请求的 TCP 连接被远程主机关闭,而通过 CDN 代理的请求返回 200,则表明扫描的资产上使用了 IP 允许列表。
### 输出示例
```
http://1.2.3.4 -- Both: open (200)
https://1.2.3.5 -- Via Proxy: filtered (504), Origin: closed (0)
```
在上面的输出中,第一行表示地址 1.2.3.4 上的端口 80 (http) 可以从扫描器当前 IP 以及所使用的 CDN 源 IP 访问。
第二行表示地址 1.2.3.5 上的端口 443 (https) 在通过代理访问时被过滤(未响应),而在直接访问时远程关闭(远程拒绝连接)了连接。
代理请求的状态通过请求代理经过的 CDN 返回的 HTTP 状态代码确定。通常,CDN 会针对各种故障条件返回不同的 5XX 错误代码,这应允许你确定远程是拒绝了连接、未响应还是以其他方式超时。值得注意的是,将这些 HTTP 状态代码与正确状态关联的工作目前正在进行中。
通常,你可能想要查找的有趣结果是代理请求返回 2XX 状态代码,而直接请求关闭、过滤或拒绝访问 (403)。这很可能意味着源站上设置了 IP 允许列表,并且你可以通过在你控制的 CDN 中的分配路由请求来绕过此限制。
## CloudFront 扫描器
cloudfront 子命令假定使用 -domain 传递的值是使用 cdn-proxy 设置的 cloudfront 分配。如果未传递 -domain,则 cdn-scanner 将尝试在由 cdn-proxy 创建的当前账户中查找 CloudFront 分配。
源站配置针对每个请求动态设置,这使得 CloudFront 扫描器比 cloudflare 扫描器快得多。
## CloudFlare 扫描器
注意:假定访问密钥位于环境变量 CLOUDFLARE_API_KEY 和 CLOUDFLARE_API_EMAIL 中。
CloudFlare 扫描器的工作方式略有不同,它不一定需要n-proxy 设置的任何内容。此扫描器只需要访问一个备用的 CloudFlare zone。请使用备用域,而不是冒险在任何重要域上运行此操作。
每个网络地址首先向 -domain 选项中传递的 zone 添加一个代理 DNS 子域。然后它向最近创建的子域以及直接向网络地址发出请求,比较这两个响应。
一段时间后,子域将开始被重用,因为很难确切知道对 CloudFlare API 的更改何时生效,因此需要执行几个步骤,这会大大减慢此扫描速度。首先,将轮换的子域从 CloudFlare 删除,我们要么等待 CloudFlare 开始返回相应的错误消息,要么等待 DNS 失败。一旦子域成功删除,就会使用更新的记录再次创建它。一旦观察到子域再次开始工作,就可以处理代理请求。到目前为止,这是发现的最可靠的方法,如果没有这些步骤,结果最终会与发出请求时 CloudFlare 网络的状态不同步。这种情况通常一开始不会发生,而是在扫描运行一段时间后发生,这表明 CloudFlare API 可能在某个阈值后开始为单个用户排队请求。
# Burp Suite 扩展
[Burp Suite 扩展脚本](./burp_extension/cdn_proxy_burp_ext.py) 可用于通过使用 cdn-proxy 创建的 CloudFront 代理来代理流量。
上图显示了使用 aws-proxy Burp 插件和 Burp 内置 Chromium 浏览器通过 CloudFront CDN 代理流量。左浏览器 (64.252.70.134) 中显示的 IP 是 Google 服务器认为的客户端 IP 地址。当然,这个 IP 实际上是 CloudFront 用于向后端服务器发出请求的众多共享 IP 之一,你可以通过检查 Amazon 的 [ip-ranges.json](https://ip-ranges.amazonaws.com/ip-ranges.json) 并检查该 IP 是否属于 CloudFront 服务的 CIDR 范围来验证这一点。在这种情况下,它确实属于 `64.252.64.0/18` CIDR 范围,如右侧浏览器所示。第三个窗口是一个非代理浏览器,正在查看 Burp 插件窗口中显示的分配。
## 安装说明
```
git clone https://github.com/RyanJarv/cdn-proxy.git
cd cdn-proxy/burp_extension
python3 -m venv venv
source venv/bin/activate
pip3 install -r requirements.txt
```
在 Burp Extender 的 Options 选项卡下:
* 确保在 Python Environment 部分下指定了 Jython jar 位置。
* 在同一部分将 `Folder for loading modules` 设置为 `/burp_extension/venv/lib/python3.9/site-packages`
在 Burp Extender 的 Extensions 选项卡下:
* 点击 `Add`
* 将 `Extension type` 设置为 python
* 使用 `Select file...` 加载 [cdn_proxy_burp_ext.py](./burp_extension/cdn_proxy_burp_ext.py)
* 点击 `Next`
在显示的新 CDN Proxy 选项卡中,将 `Proxy Host` 字段设置为使用 `cdn-proxy cloudflare create` 创建的分配的域。来自 Burp 的流量现在将通过 CloudFront 代理路由。
## 支持
请[提交 issue](https://github.com/RyanJarv/cdn-proxy/issues/new)以获取支持。
## 贡献
请使用 [Github Flow](https://guides.github.com/introduction/flow/) 进行贡献。创建一个分支,添加提交,然后[提交 pull request](https://github.com/RyanJarv/cdn-proxy/compare/)。
上图显示了使用 aws-proxy Burp 插件和 Burp 内置 Chromium 浏览器通过 CloudFront CDN 代理流量。左浏览器 (64.252.70.134) 中显示的 IP 是 Google 服务器认为的客户端 IP 地址。当然,这个 IP 实际上是 CloudFront 用于向后端服务器发出请求的众多共享 IP 之一,你可以通过检查 Amazon 的 [ip-ranges.json](https://ip-ranges.amazonaws.com/ip-ranges.json) 并检查该 IP 是否属于 CloudFront 服务的 CIDR 范围来验证这一点。在这种情况下,它确实属于 `64.252.64.0/18` CIDR 范围,如右侧浏览器所示。第三个窗口是一个非代理浏览器,正在查看 Burp 插件窗口中显示的分配。
## 安装说明
```
git clone https://github.com/RyanJarv/cdn-proxy.git
cd cdn-proxy/burp_extension
python3 -m venv venv
source venv/bin/activate
pip3 install -r requirements.txt
```
在 Burp Extender 的 Options 选项卡下:
* 确保在 Python Environment 部分下指定了 Jython jar 位置。
* 在同一部分将 `Folder for loading modules` 设置为 `标签:AWS CloudFront, Burp Suite, C2日志可视化, CDN Re-fronting, CDN绕过, CISA项目, Cloudflare, IP 地址批量处理, IP白名单绕过, MITRE ATT&CK, WAF绕过, Web安全, 云计算安全, 反向代理, 域名前置, 密码管理, 指纹识别, 数据展示, 日志审计, 源站发现, 私有化部署, 红队, 网络基础设施攻击, 网络流量代理, 蓝队分析, 边界突破, 逆向工具, 防御规避