深挖CORS配置漏洞(第一部分)
James Kettle在2016年的研究显著提高了人们对CORS(跨源资源共享)错误配置对Web安全有害影响的认识。但故事就此结束了吗?在2022年撰写关于CORS相关安全问题的文章是否徒劳?我不这么认为。
本文是一个系列的第一篇,我将讨论更多次要的CORS相关问题,并介绍较少为人知的检测技术。我的主要受众是攻击方人员,但防御方人员也可能对这个系列感兴趣。
测试资源,不仅仅是域名
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源概念”)也是如此。然而,没有主流浏览器支持此功能,这导致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或除foo.example.com之外的任何example.com子域。
如果我没有枚举example.com的子域并将它们输入到目标的动态分析中,我可能甚至无法检测到它是否配置了CORS,因为对我大多数探测请求的响应不包含任何CORS响应头。
除了子域之外,感兴趣的资源在其CORS配置(如果有)中可能允许哪些其他来源?候选来源的一个有希望的来源是目标拥有的其他域及其子域。几次反向WHOIS搜索可以揭示许多这些域名。不要就此止步。
Meiser等人在2021年一篇题为《小心你信任的人:研究跨源通信的陷阱》的引人入胜的论文中建议,可以从目标的/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,他在本文发表前审阅了草稿。