JSON劫持漏洞攻防原理及演練


  注* 作者發表這篇文章的時間較早,某些方法可能並不是最好的解決方案,但針對這種漏洞進行的攻擊還依然可見,如早期的:QQMail郵件泄露漏洞,下面介紹的是對這種攻擊原理的介紹。

  不久之前,我寫了一篇文章《一個微妙的JSON漏洞》,文中講到這個漏洞可能會導致敏感信息泄露。針對該漏洞的特點,通過覆蓋JavaScript數組構造函數以竊取(暴露)JSON返回數組,而現在大多數瀏覽器還無法防范這種攻擊。

  然而,通過和微軟的Scott Hanselman交流,我了解到另外一個方法可能會影響更多的瀏覽器。在上周的挪威開發者大會上,我做了一個針對Json劫持漏洞的演示。

  在我進一步講之前,我先說一說,這個漏洞可能帶來的影響。

  在以下條件下,會出現這個漏洞:首先暴露JSON服務,並且該服務會返回敏感數據;返回JSON數組;對GET請求做出響應;發送這個請求的瀏覽器啟用了JavaScript並且支持_defineSetter_方法。

  如果我們不使用JSON發送敏感數據,或者只對報文請求做出響應,那么我們的網站就不存在這個漏洞。

  我不喜歡用流程圖展示這個過程,我會盡量用圖表描述。在第一頁截圖上,我們可以看到不知情的受害者登陸漏洞網站,漏洞網站返回了一個身份認證的cookie。

  我們都可能收到過一些垃圾郵件,郵件中附有鏈接,發送者聲稱這有一段搞笑視頻。這些大量的垃圾郵件都是一些別有用心的人發的。

  但是實際上,這些鏈接指向的是那些壞家伙自己網站。當我們點擊了鏈接,接下來的兩個步驟會迅速進行。第一,我們的瀏覽器向這些網站發送請求。

  

  第二,那些網站會響應一些包含JavaScript的HTML。這些JavaScript會帶一個script標記。當瀏覽器檢測到script標記,它就會向那些漏洞網站再發一個下載腳本的GET請求,攜帶着身份驗證的cookie。

  這樣,那些壞家伙就偽裝成了受害者的瀏覽器,利用其身份發出了一個包含敏感數據的JSON請求。接着,把JSON加載為可執行的JavaScript,這樣以來,那些黑客就能夠獲取到這些數據。
為了加深理解,我們可以看看一個攻擊的實際代碼。假如漏洞網站返回帶有敏感數據的JSON響應通過如下方式發送:

[Authorize]
publicJsonResultAdminBalances(){
    varbalances=new[]{
        new{Id=1,Balance=3.14},
        new{Id=2,Balance=2.72},
        new{Id=3,Balance=1.62}
    };
    returnJson(balances);
}

  需要說明的是,上面的演示不是專門針對ASP.NET或者ASP.NET MVC,我僅僅是恰巧用ASP.NET MVC來演示這個漏洞而已。
假如這是HomeController的一種方法,我們通過對/Home/AdminBalances發送了一個GET請求,並且返回如下JSON文本:

[{“Id”:1,”Balance”:3.14},{“Id”:2,”Balance”:2.72},{“Id”:3,”Balance”:1.62}]

  注意,我定義這個方法時使用了Authorize屬性,用來驗證請求者的身份。所以一個匿名的GET請求將不會得到敏感數據。
重要的是:這是一個JSON數組。包含JSON數組的文本是一個有效的JavaScript腳本,並且可以被執行。僅僅包含JSON對象的腳本不是一個有效的JavaScript可執行文件。
舉個例子,如果我們有一個包含如下JSON代碼的JavaScript文檔:

{“Id”:1,”Balance”:3.14}

  並且有一個指向這個文檔的腳本標簽:

<script src=”http://example.com/SomeJson”></script>

  這樣,我們會在HTML頁中得到一個JavaScript錯誤。然而,倘若存在一個不幸的巧合,如果我們有一個script標簽,這個標簽指向僅僅含有一個JSON數組的文檔,這樣的話,這個標簽就會被誤認為是有效的JavaScript,並且數組會生效。

  下面就讓我們看看那些別有用心的人的服務器上的HTML頁。

  注*這里我們可以看到使用 Json Object 而不是Json Array返回你的數據,可以在一定程度上預防這種漏洞。

<html>
...
<body>
<scripttype="text/javascript">
Object.prototype.__defineSetter__('Id',function(obj){alert(obj);});
</script>
<scriptsrc="http://example.com/Home/AdminBalances"></script>
</body>
</html>

  看到了什么?黑客正在改變對象的原型,用_defineSetter_這種特殊方法,覆蓋JSON對象(Object)原本應有的默認行為。

  在這個例子中,一個命名合適的ID在任何時候都可以被設置到任何對象上時,一個匿名的函數將會被調用,這個函數將會利用alert 函數顯示屬性值。注意,這時腳本僅僅會將數據發回給那些壞家伙,而不會發送敏感數據。

  就像之前提到的,壞家伙需要使我們在登錄漏洞網站后不久並且在會話仍舊有效時,訪問他的惡意的網頁。通過含有惡意網站鏈接的郵件進行釣魚攻擊的方式是很典型的。

  如果你仍舊點擊鏈接登錄原網站,瀏覽器將會在加載腳本標簽中引用的腳本並向網站發送你的身份驗證cookie。直到連接上原網站,我們對JSON數據發出一 個有效的身份驗證請求,並且會收到在我們瀏覽器中響應的有效數據。這些話可能聽着很熟悉,因為它是一個真的變種偽造跨站請求,之前我寫過這種情況。

  因為在IE8上_defineSetter_是一個無效的方法,所以在IE8上看不到現象。我在Chrome和Firefox上都試過,都可以。

  避免這個漏洞也很簡單:或者從不發送JSON數組,或者只訪問HTTP POST以獲得需要的數據。舉個例子:在ASP.NET MVC中,你可以用AcceptVerbsAttribute來實現:

[Authorize]
[AcceptVerbs(HttpVerbs.Post)]
publicJsonResultAdminBalances(){
    varbalances=new[]{
        new{Id=1,Balance=3.14},
        new{Id=2,Balance=2.72},
        new{Id=3,Balance=1.62}
    };
    returnJson(balances);
}

  這個方法的一個問題就是:像jQuery這樣的很多JS庫默認都是用GET方式發送JSON請求,而不是POST。舉個例子,$.getJSON 默認發起的是GET請求。所以,當進行這種JSON訪問時,我們需要確信我們是用客戶端庫發起的POST請求。

  ASP.NET和WCF JSON服務端實際上在對象中用了“d”屬性,包裹了他們的JSON,這我在另一篇文章中討論過:

  必須經過這些屬性獲得數據,似乎有些奇怪,但這需要通過一個客戶端代理來實現來去除“d”屬性,,以便不影響最終用戶。

  在ASP.NET MVC下,絕大多數開發者沒有生成客戶端代理,而是使用jQuery和其他類似的庫,這樣一來使用“d”屬性的就有些尷尬。

  注*其實MVC的方法有點復雜,到這里我們可以看出,JSON劫持漏洞是要以在受豁者的瀏覽器上執行JSON返回對象為前提的,其實Google使用了一種更加聰明的方法,通過添加“死循環”命令,防止黑客運行這段腳本,可參見這篇文章:為什么谷歌的JSON響應以while(1);開頭? 

  檢查首部(Http-Header)怎么樣?

  一些人可能會有疑問:“為什么不在響應一個GET請求之前,用一個特殊的首部對JSON服務進行檢查?就像X-Requested- With:XMLHttpRequest或者Content-Type:application/json”。我認為這可能是個過渡,因為大多數的客戶端 庫會發送一種或兩種Header,但是瀏覽器響應腳本標簽的GET請求不這樣。

  問題是:在過去某個時候,用戶可能會發出合法的JSON GET請求,在這種情況下,這個漏洞可能會隱藏在用戶瀏覽器的正常請求之間。也是在此種情況下,當瀏覽器發出GET請求,這種請求可能會緩存在瀏覽器和代 理服務器的緩沖區。我們可以嘗試着設置No-Cache header,這樣,我們信任瀏覽器和所有的代理服務器能夠正確地實現高速緩存並且信任用戶也不會被意外地覆蓋。

  當然,如果我們用SSL提供JSON文本,這個特定的緩存問題將會很容易被解決。

  注*這里我們可以看到防范方法這三:不要cache你的ajax請求,不過目前似乎所有的js庫默認都是不cache的。

  真正的問題在哪里?

  Mozilla Developer Center發表的一篇文章中寫道:對象和數組初始化設定項在賦值時不應該調用setters方法。這一點我同意。盡管有評論認為:也許瀏覽器真的不應該執行腳本。

  但是在一天結束的時候,分派責任並不能使你的網站更加安全。這些瀏覽器的怪癖毛病將會時不時地出現。我們作為網站的開發者需要解決這些問題。 Chrome2.0.172.31和Firefox3.0.11也有這個軟肋。IE8沒有這個問題,因為它不支持這種方法,我也沒有在IE7或者IE6中 試驗過。

  在我看來,在當前的客戶端庫下,安全訪問JSON的默認方式應該是POST,並且我們應該選擇GET,而不是其他方式。您覺得呢?您所了解的其他平台是怎么解決這個問題的呢?我很想聽聽大家的想法。

  原文 haacked.com


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM