深挖CORS配置漏洞:跨域资源共享的安全陷阱(第一部分)
James Kettle在2016年的研究显著提高了人们对CORS(跨域资源共享)错误配置对Web安全有害影响的认识。但故事到此结束了吗?在2022年写关于CORS相关安全问题是否徒劳?我不这么认为。这篇文章是本系列的第一部分,我将讨论更多次要的CORS相关问题,并介绍较少为人知的检测技术。我的主要受众是攻击方人员,但防御方人员也可能对这个系列感兴趣。我在这里不介绍CORS协议的基础知识。如果您需要熟悉该协议,MDN Web Docs的简介是一个很好的起点。
测试资源,不仅仅是域名
CORS不仅可以在技术栈的不同层(反向代理、源服务器等)配置,还可以在不同粒度级别配置。例如,Express的CORS中间件允许开发人员不仅为整个服务器配置CORS,还可以为单个路由配置。在给定域上寻找CORS错误配置时,请记住,某些资源可能支持CORS,而其他资源不支持,并且不同的支持CORS的资源可能有不同的CORS配置。仅测试目标域上的单个路径的CORS意识和错误配置是一个错误,因为您可能无法检测到该域上配置错误的CORS感知资源。
扩大允许来源的搜索范围
由于CORS协议仅在Access-Control-Allow-Origin响应头中容忍单个序列化来源值,允许的来源集(如果有)不容易向攻击者揭示。相比之下,页面的内容安全策略的script-src指令明确指定了页面上有效的JavaScript来源。
历史注记
W3C 2009年关于CORS的工作草案确实规定了多值Origin头,RFC 6454(题为“Web Origin概念”)也是如此。然而,没有主流浏览器支持此功能,这导致W3C在2013年的候选推荐中修订了立场。此后取代后者作为CORS协议官方规范的Fetch标准不支持多值Origin头。
要检测CORS意识,更不用说推断尽可能多的受信任来源集,您通常别无选择,只能进行动态分析,将服务器视为预言机,并反复向其提出是或否问题,一次一个候选来源:
- 客户端:您允许这个来源吗?
- 服务器:不。
- 客户端:好吧……那个呢?
- 服务器:不行。继续尝试。
- 客户端:另一个呢?
- 服务器:是的,我允许。
根据公认的智慧,一个好的第一步是提供资源本身的来源:例如,如果资源https://example.com/whatever配置了CORS,它可能允许自己的来源https://example.com。然而,我遇到过一些情况,资源https://example.com/whatever配置了CORS并允许来源https://foo.example.com,但不允许来源https://example.com或example.com的任何子域,除了foo.example.com。如果我没有枚举example.com的子域并将它们输入到目标的动态分析中,我可能甚至无法检测到它是否配置了CORS,因为对我的大多数探测请求的响应不包含任何CORS响应头。
除了子域之外,感兴趣的资源在其CORS配置(如果有)中可能允许哪些其他来源?候选来源的一个有希望的来源是目标拥有的其他域及其子域。几次反向WHOIS搜索可以揭示许多这些域名。不要就此止步。Meiser等人在一篇引人入胜的2021年论文《Careful Who You Trust: Studying the Pitfalls of Cross-Origin Communication》中建议,可以从目标的/crossdomain.xml(Flash)和/clientaccesspolicy.xml(Silverlight)资源中收集有用的线索,如果这些资源存在于服务器上。根据我的经验,这是一个好建议。在Wayback Machine上查找这些页面也可能产生一些额外的候选来源。此外,正如Meiser等人正确指出的,https://b.com的connect-src CSP指令中列出https://a.com是一个很好的迹象,表明https://a.com在其CORS配置中允许来源https://b.com。
正则表达式中eTLD+1部分未转义的点
许多旨在允许多个来源的CORS配置依赖于某些正则表达式进行来源验证。在某些情况下,正则表达式存在缺陷,因为它匹配的来源比开发人员预期的要多。一个常见的错误是在正则表达式中使用未转义的点来表示DNS标签分隔符。通过对目标的动态分析,您可能能够推断出开发人员为了允许https://example.com及其任意子域,正在使用类似以下的正则表达式进行来源验证:
^https:\/\/(.*\.)?example.com$
请注意,将子域DNS标签与eTLD+1(example.com)分开的点已按应有的方式转义,这排除了来自像https://notexample.com这样的来源的攻击。如果您无法在example.com的子域上找到某些XSS或子域接管,您应该放弃并将精力集中在其他地方。细心的读者可能已经注意到TLD(com)和二级域(example)之间存在一个未转义的点。这个诱人的转义遗漏是否以某种方式可利用?不幸的是,公共后缀列表中没有以example开头后跟单个字符再后跟com的条目(至少在我写这篇文章时)。因此,获取像attacker.examplezcom这样的域名是不可能的,除非您愿意经历注册一个全新的examplezcom eTLD的艰巨且昂贵的过程。
然而,在允许来源的eTLD由不止一个而是多个DNS标签组成的情况下,并非一切都失去了。例如,现在假设使用以下正则表达式进行来源验证:
^https:\/\/(.*\.)?example.co.uk$
请注意,正则表达式中对应于eTLD+1的部分包含两个未转义的点。第二个未转义的点(co和uk之间的那个)不可利用,原因如上所述。第一个未转义的点(example和co之间的那个)更有希望,因为uk本身是一个公共后缀,并且像examplezco.uk这样的域,其安全来源与正则表达式匹配,可能可供购买。如果该域的价格不令人望而却步,您可以购买它并从那里对目标发起攻击。诚然,由于由多个DNS标签组成(在我的例子中是co.uk)并且存在不太具体的公共后缀(uk)的公共后缀相对罕见,正则表达式中eTLD+1部分未转义点的可利用案例甚至更罕见。到目前为止,我和信息安全超级明星James Kettle都从未遇到过。但您应该不遗余力。事实上,这种漏洞的可能性应进一步激励您扩大对目标CORS配置允许的域的搜索。
CORS与SameSite
旨在允许可靠跨站点访问的CORS感知资源现在需要会话标识cookie明确设置(即不依赖浏览器的默认设置)SameSite=None和Secure。然而,与您可能在其他地方读到的相反,与CORS结合使用的更严格的Lax和Strict值的合法用例确实存在。希望保护其CORS感知资源免受跨站点攻击但仍允许(全部或部分)同站点来源的开发人员确实可以使用这两个SameSite值中的任何一个设置其cookie。由于SameSite属性仅影响跨站点请求,同站点请求确实无条件携带此类cookie。如果您发现cookie标记为Lax或Strict,不要过早排除滥用的可能性。寻找CORS感知资源信任的那些同站点来源上的跨站点脚本或子域接管实例,正如Sam Curry曾经非常有效地做过的那样:
这是让我能够利用CORS错误配置泄露会话令牌的救星。看到SameSite cookie非常难过,但这绝对是一个有趣的新挑战:)
更多关于这种微妙之处的信息,请参阅我之前的文章之一。
致谢
我要感谢Alesandro Ortiz,他在发布前友好地审阅了这篇文章的草稿。