在本系列文章中,我们将为读者详细介绍HTTP CORS攻防的相关知识。在上一篇文章中,我们为读者介绍了同源策略及其变种,以及CORS及其常见头部等基础知识,在本文中,我们将理论联系实际,介绍具体的攻防知识。
运行容器化环境
要使这些环境处于活动状态,请执行以下步骤:
在该项目的根目录下创建容器:
$ docker-compose build — no-cache; (注意:参数no-cache前面是两个减号)
通过docker-compose启动容器:
$ docker-compose up
docker-compose up命令的输出结果
如果您在docker环境中遇到任何麻烦,建议通过杀死和删除当前活动的docker镜像来进行清理。只需确保没有其他容器在被杀死之前需要保存其状态即可。
$ docker kill $(docker ps -a -q)
$ docker rm $(docker ps -a -q)
好了,希望到目前为止一切顺利。下面,让我们来看看前面创建的两个网络服务器。为此,可以打开浏览器,然后导航至http://trustedsite.com和http://evilsite.com。
Trusted Site
Trusted Site的Web服务器菜单
Evil Site
Evil Site的Web服务器菜单
使用XHR和Fetch请求探索CORS规则
用于XHR请求的CORS规则
XMLHttpRequest标准[11]定义了一个API,该API提供脚本化的客户端功能,用于在客户端和服务器之间传输数据。
关于它的安全性,我们应该注意一些警告性质的事实,正如《the Browser Security Handbook》part2[12]所说:
“XMLHttpRequest提供了一组在其他浏览器机制中没有的安全功能,包括:
· 能够指定一个任意的HTTP请求方法(通过open()方法);
· 能够对请求设置自定义的HTTP头部(通过setRequestHeader()方法);
· 能够读取完整的响应头部(通过getResponseHeader()和getAllResponseHeaders()方法);
能够以JavaScript字符串的形式读回完整的响应体(通过responseText属性)。
由于通过XMLHttpRequest发送的所有请求都包含了一套浏览器维护的目标站点的cookie,并且考虑到该机制提供的与服务器端组件交互的能力远大于任何其他脚本可用的功能,因此建立适当的安全控制极为重要。”
在处理XHR请求时,必须遵守以下浏览器保护机制,防止源被篡改:
· 当试图通过setRequestHeader()方法篡改/毒化origin头部时,原始头部将被保留;
· 当试图篡改/毒化其他头部(作为参数传递给xhr.send()方法)时,原始头部将被保留。
用于Fetch请求的CORS规则
“许多API都可以用来获取资源,例如HTML的img和script元素,CSS的cursor和list-style-image,navigator.sendBeacon()和self.importScripts()等JavaScript API。Fetch标准为这些特性提供了统一的体系结构,因此当涉及到Fetch的各个方面时,它们都是一致的,例如重定向和CORS协议。”——https://fetch.spec.whatwg.org/
虽然Fetch标准取代了origin元素的语义(最初在The Web Origin Concept[13] - RFC6454中定义),但XHR篡改保护的效果也适用于此。
· 当试图通过fetch()参数篡改/毒化origin头部时,原始头部会被保留下来。
通过实战理解请求过程
现在,你已经知道了SOP、CORS、XHR和Fetch请求的基本原理,接下来,我们就可以亲自动手尝试一下了。
Trusted Site和Evil Site页面都有类似的菜单。首先,让我们尝试一下Trusted Site的四个请求选项。
注意,请求的资源,OWASP的虫子图片或者显示“All your base belong to us ;D”的脚本,都已被页面成功加载和使用。这是属于同一域的一个页面调用本地资源的经典案例。
在同一个域页面请求之后执行hello_script.js的内容
现在,我们来尝试Evil Site:
· 打开http://evilsite.com网站;
· 调出Web Developer Tools窗口(在Firefox或Chrome浏览器中按F12即可);
· 点击与OWASP image + JavaScript XHR相关的锚链接;
· 点击与Alert script + JavaScript Fetch相关的锚链接。
将焦点从Web Developer Tools移到Console选项卡上,这时,我们应该看到下面的内容:
上面是第三步和第四生成的信息。为了便于阅读,现将它们抄录如下:
“ Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://trustedsite.com/img/owasp_bug.png. (Reason: CORS header ‘Access-Control-Allow-Origin’ does not match ‘http://trustedsite.com’).
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://trustedsite.com/static/hello_script.js. (Reason: CORS header ‘Access-Control-Allow-Origin’ does not match ‘http://trustedsite.com’).”
从这里可以了解浏览器禁止请求资源的原因。别忘了Apache服务器的.htaccess配置,它会将ACAO(Access-Control-Allow-Origin)CORS头部添加到了特定的HTTP响应(图片文件和.js脚本)中。
现在,我们要进一步考察这些请求。为此,请从Web Developer Tools中单击Network选项卡。然后,选择hello_script.js资源的OPTIONS请求。之后,在屏幕的右侧,你应该看到以下HTTP请求和响应:
对hello_script.js资源的HTTP请求和响应
注意上面的Origin头部,其内容是http://evilsite.com。但如果我们深入考察Evil Site的script_fetch.js实现中,就会发现Origin头部有一些非常让人感兴趣的地方。
evil_site_public_html/static/script_fetch.js文件的内容
虽然我们试图将Origin头部(第7行)与Trusted Site的域名授权地址,即http://trustedite.com重合起来,但浏览器实施的Fetch API却阻止了这一行为。
这就是为什么我们在Web Developer Tools的Network选项卡——HTTP OPTIONS请求中看到的HTTP://evilsite.com为Origin的原因。
通过代理拦截绕过CORS(手动)
读到这里的读者,已经有的累了,所以,是时候来点“小甜品”了。
CORS(以及CSP这样的内容安全策略)背后的主要原则是基于浏览器和Web服务器之间的信任关系。这样,Web服务器就可以进一步指示Web浏览器可以信任哪些域。在实践中,HTTP安全头部就可以完成这样的任务。
现在,我们从恶意攻击者的角度来思考问题。为了绕过CORS规则,攻击者必须拦截服务器的HTTP响应,其中包含CORS ACAO(Access-Control-Allow-Origin)头部。其次,他/她还可以试图改变其值,让它变成攻击者的页面的域或允许任意域(使用字符*)。
这里的“拦截”,可以通过代理服务器自动过滤HTTP请求-响应循环(详见下节),或者是通过Burp Suite这样的代理工具手动的方式完成,具体我们在后面详述。
设置代理工具snoopy
如果读者已经熟悉了ZAP、Burp Suite这样的代理工具,请直接阅读下一节。在这里,我们将使用PortSwagger的Burp Suite Community[14]。
您可以根据自己的平台/体系结构,来安装运行相应的Burp Suite版本。
在Options选项卡中,编辑Proxy Listeners处的Interface一栏,具体如下图所示(“127.0.0.1:10001”):
在同一选项卡上,按以下方式设置“Intercept Client Requests”:
仍然在“Options”选项卡上,按以下方式设置“Intercept Server Responses”:
确保“ Intercept ”选项卡中的“Intercept is on”按钮处于激活状态:
最后,将您的浏览器配置为通过代理传递请求。请注意,代理将会侦听端口10001。这里,我们有两个配置选项。不过,我通常使用FoxyProxy扩展[15]来设置代理。但是,您也可以根据自己的喜好,通过浏览器中的网络设置[16]手动执行该操作。
Firefox浏览器的FoxyProxy burp代理的具体设置
好了,万事俱备,我们可以出发了。
通过HTTP响应篡改绕过CORS
打开Evil Site(http://evilsite.com)
如前所述,Evil Site请求的来自Trusted Site的资源是不允许被相应的页面所使用的。这是由于CORS ACAO(Access-Control-Allow-Origin)头部只允许http://trustedsite.com域。
在浏览器上激活Burp代理
通过单击/XHR请求来请求OWASP图像资源
停止并查看对应于HTTP请求的Burp Suite对话框
点击“Forward”继续请求而不进行进一步的编辑
在包含CORS头部的HTTP响应处停止
将CORS ACAO头部的值改为*
通过单击“Forward”将响应提交到浏览器
将显示受保护的资源(OWASP的虫子图像)的内容(注意,这里并没有CORS错误消息):
黑客作业
在前面的场景中,我们通过Burp代理拦截并篡改了一个JavaScript XHR请求。成功实施这种方法的一个重要方面,就是需要彻底分析HTTP请求-响应周期,从而弄清楚哪些头部是必须的,以及当它们丢失或配置错误时,将在浏览器控制台得到了什么错误信息,等等。
现在是时候综合运用一下前面所学的知识了。下一节还将介绍绕过Alert script+JavaScript FETCH组合的技巧。
此外,您也可以自由尝试绕过Evil Site菜单中的其余选项。
绕过Alert script+JavaScript FETCH组合的技巧
实际上,除了修改Access-Control-Allow-Origin头部外,我们还必须在HTTP OPTIONS请求中添加Access-Control-Allow-Headers(为此,可以使用Burp请求编辑对话框中的Add按钮)。
这是很有必要的,因为客户端(启动XHR请求的.js脚本)会在随后的GET请求中默认添加cache-control和pragma头部。所以,我们要在HTTP OPTIONS响应中反映这一点。
请记住,这里我们有一个HTTP预检方案(有疑问时请查前面的相关部分),其中HTTP OPTIONS请求将在实际资源检索请求之前发出。
编辑了HTTP选项响应,以反映绕过相应机制所需的CORS头部
hello_script.js资源检索的HTTP GET响应(注意ACAO CORS头部)
上面的对话框说明攻击成功了
自动绕过及其他项目
在上一节中,我们看到了如何手动绕过CORS规则的保护。但是,从实际的角度来看,这样做的效率并不高。
实现自动绕过CORS的一个可行方法,就是部署一个代理服务器,如CORS Anywhere[17] API。这样的话,代理服务器将作为一个中介,过滤请求和响应头以反映在代理配置时指定的允许和拒绝规则。
在设置面向网络的代理时,请参考该项目的Github页面[18],以获取更多详细信息和注意事项(请谨慎使用开放代理,朋友)。您可以在此处测试实时API [19]。
另一个围绕CORS(以及CSP内容安全策略)头部的理解的奇妙举措是来自digi.ninja[21]的CORS Demos[20]。这里提供了一个非常好的概念证明。
小结
如果您从头至尾阅读到这里的话,那么恭喜你。HTTP头部本身就是一个非常宽泛的话题,因为该协议试图不断进化以适应Web应用的实际情况。这种情况变得越来越普遍,尤其是通过诸如REST之类的技术使API普及之后。
实际上,CORS头部不仅错综复杂,并且还充满了细微的差别。在实现处理inter-domain通信的解决方案时,要注意可能出现的常见陷阱。而moesif.com网站中的文章[22]很好地解释了这一点(以及更多方面)。
关于CORS及其弱点的最后一个信息是,浏览器和应用之间的信任应该由安全配置来现实保证。正确选择部署哪种AJAX方法和CORS头部将对你的API的整体安全性产生积极影响。
请记住我们提出的四个资源使用场景,尽管它们很简单。事实上,资源共享还有其他选择和组合。但也许(我希望我们做到了这一点),这里所介绍的理论和实践可以作为设计更复杂和安全的默认Web应用的基础。
本文用到的浏览器:
· Mozilla Firefox 68.5.0esr (64位)
· 谷歌浏览器70.0.3538.77版(官方构建)(64位)。
· 谷歌浏览器(2018年旧版)70
特别感谢审稿人Luiz Rennó和Vitoria Rio为本文付出的努力。
本文翻译自:https://medium.com/bugbountywriteup/hacking-http-cors-from-inside-out-512cb125c528如若转载,请注明原文地址: