天天看點

JSON劫持漏洞(詳細講解利用JSON進而進行資料劫持的漏洞攻防政策)

  • 英文原文:JSON Hijacking
  • 英漢對照:JSON Hijacking 翻譯對照
  • 原文解析:JSON劫持漏洞分析和攻防演練

聲明(閱讀前必讀!!!):

轉載自這篇文章

本人是一個英語渣,英語四級都沒過。翻譯此文存粹用于英語學習,而且大部分還是用翻譯軟體翻譯的。請謹慎閱讀此譯文,注意不要被譯文雷到。

另外這是一篇2009年的老文章了,裡面有部分知識可能已經過時了,不過文章中關于JSON劫持的知識大部分還是十分有用的。在每段原文翻譯後面我都會加上自己了解和相關備注,主要是本文開闊了一個思路。

由于文章翻譯過來導緻有些知識點不好了解,這裡可以點選上面的JSON劫持漏洞分析和攻防演練,我會按照自己對JSON劫持的了解對這個漏洞進行講述。

前陣子我寫了篇關于一個微妙的JSON的漏洞(Anatomy of a Subtle JSON Vulnerability)可能導緻敏感資訊洩露的文章。這種特别的漏洞攻擊(exploit)主要通過覆寫JavaScript數組的構造函數,進而劫持暴露的JSON數組資料,現在大多數的浏覽器都不支援防禦這些漏洞攻擊。 

PS: exploit的英文本意為“利用”。但是在計算機安全領域中指的是漏洞利用、利用程式或者網站的某個漏洞進行攻擊!

這裡解釋下overriding the JavaScript Array(覆寫javascript數組的構造函數):因為javascript允許我們覆寫或重寫其他的方法,包括Array()這種内部的方法,是以惡意攻擊者可以很輕松的将js中的Array()方法替換和覆寫成惡意代碼,相當于C#、JAVA和C++中的方法重寫。原文作者使用的是__defineSetter__方法來實作這個功能的,但是此函數隻有部門浏覽器支援,是以一般來說還是直接使用JS的方法重寫。

一般替換的惡意代碼都是進行JSON劫持的代碼,将擷取到的JSON資料發送給第三方網站伺服器上。之是以可以利用這點進行劫持,是因為JSON數組本身就是特殊的Array,網站一旦傳回的JSON數組,惡意攻擊者就通過重寫Array()的構造函數将資料轉發他們的服務上,這就是JSON資料劫持。

然而,還有另外一個與exploit相關的漏洞有可能會對更多的浏覽器造成影響。最近在和微軟的ScottHanselman交流時引起了我對這個問題的注意,并且我在上周的挪威開發者大會上示範了這個漏洞,雖然這個漏洞曾經出現在Twitter(推特)上。

PS:這個與exploit相關漏洞的描述位址:http://hackademix.net/2009/01/13/you-dont-know-what-my-twitter-leaks/

在我深入讨論前,讓我首先給你講解下形成JSON劫持漏洞的條件。

這個JSON劫持漏洞需要你暴露出JSON服務(指JSON服務位址被攻擊者知道)并且進行如下操作:

  • 傳回敏感的資料
  • 傳回一個JSON數組
  • 響應一個GET請求
  • 發送請求的浏覽器啟用了JavaScript(很有可能)
  • 發送請求的浏覽器支援__defineSetter__方法

是以,如果你從不發送敏感的JSON格式資料,或者你僅僅發送POST請求以響應擷取JSON等等操作,那麼你的網站可能不會受到這個特定的漏洞的攻擊(雖然有可能是其他人)。

我讨厭使用Visio(Microsoft Office Visio:繪制流程圖的軟體),但我想我會盡我所能的通過圖表來更好的講解這個過程。在第一個螢幕截圖上,我們看到不知情的受害者登陸了這個容易受到攻擊的網站,而這個易受攻擊的網站向浏覽器發送一個身份驗證的cookie并且浏覽器将它儲存起來(浏覽器會将這個HTTP的身份認證資訊和相關的cookie緩存起來)。

JSON劫持漏洞(詳細講解利用JSON進而進行資料劫持的漏洞攻防政策)

在某些時候,無論是在過去,或者是在将來,總有一些壞人(惡意攻擊者)不停的發送着大量的垃圾郵件(這裡應該是釣魚/欺詐郵件),受害者會收到一封内容是推薦一個非常有趣的視訊的電子郵件,比如郵件中帶有"鋼琴上的松鼠"這樣内容的一個視訊連結。

JSON劫持漏洞(詳細講解利用JSON進而進行資料劫持的漏洞攻防政策)

但是這個連結實際上是指向這些惡意攻擊者的網站(就是釣魚網站),當受害人點選了這個連結,接下來會連續執行兩個步驟。首先,受害人的浏覽器會向惡意攻擊者的網站發送一個GET請求。

JSON劫持漏洞(詳細講解利用JSON進而進行資料劫持的漏洞攻防政策)

這些惡意攻擊者的網站(釣魚網站)會響應并傳回一些HTML語句,這些HTML語句包含着帶有script标簽的JavaScript語句(惡意腳本語句)。當浏覽器運作到這些script标簽時,它就會執行這段代碼并向那些容易受到攻擊的網站(這裡假設攻擊和劫持的網站是http://vulnerable.example.com/Json)發送另一個加載腳本的GET請求(如上圖所示,這裡會将從網站擷取的響應文本放到/替換到script标簽中,一般是傳回JSON資料),該請求還會将受害人浏覽器中的身份驗證cookie一同發送過去。

JSON劫持漏洞(詳細講解利用JSON進而進行資料劫持的漏洞攻防政策)

攻擊者通過僞裝成受害者的浏覽器,利用浏覽器的授權(就是上面發送過去的身份驗證cookie) 發出一個請求,這個請求可以到擷取到擁有敏感資料的JSON數組。攻擊者通過将請求的JSON數組載入到script中以替換重寫成可執行的腳本語句,進而竊取到這些資料(腳本語句會将竊取到的資料發送給攻擊者)。

為了能更深入的了解,看看這些示範攻擊的實際代碼,這樣也許對我們了解有所幫助(你可以将代碼下載下傳拷貝下來并運作)。另外要注意的是下面的示範無論如何都不是專門針對ASP.NET 或者 ASP.NET MVC,我隻是碰巧使用ASP.NET MVC來示範它。假設這個容易受到攻擊的網站通過一個Action方法傳回敏感的JSON資料。

[Authorize]
public JsonResult AdminBalances() {
  var balances = new[] {
    new {Id = , Balance=}, 
    new {Id = , Balance=},
    new {Id = , Balance=}
  };
  return Json(balances);
}
           

假如這是一個HomeController控制器的的方法,你可以通過向“/Home/AdminBalances”這個URL位址發送GET請求,進而擷取Action方法傳回的JSON資料:

[{"Id":,"Balance":},{"Id":,"Balance":},{"Id":,"Balance":}]
           

請注意,我還通過Action方法上聲明了AuthorizeAttribute(Authorize屬性)使得請求這個方法必須通過身份驗證,是以一個匿名(指未通過身份驗證)的GET請求将無法檢視此敏感資料。

事實上,這是一個JSON數組是很重要的。事實證明,包含着一個JSON數組的腳本是一個有效的JavaScript腳本。一個僅包含着JSON對象的腳本不是有一個有效的JavaScript檔案。例如,如果您有一個包含以下JSON的JavaScript檔案:

{"Id":, "Balance":}
           

并且你有一個引用這個腳本檔案的script标簽:<script src=”http://example.com/SomeJson”></script>

你将會在HTML頁面中得到一個JavaScript錯誤。然而,通過一個不幸的巧合,如果你有一個script标簽,并且這個腳本标簽引用檔案的内容隻有JSON數組,那麼這(指的是script标簽所引用檔案中的JSON數組内容)将被認為是有效的JavaScript和數組被執行。

PS:如果JSON是以對象形式傳回,即傳回{"Id":1, "Balance":3.14},會提示非法标簽錯誤。因為在JS中"{"和"}"這兩個大括号(又稱作花括号)在代碼中是作為起始和結束的含義。是以為在JS檔案中,單獨的JSON資料要麼以數組形式存在,要麼在傳回的對象上加小括号({"Id":1, "Balance":3.14}),要麼傳回JSON數組,否則就是非法标簽錯誤。

現在讓我們看看放置在這些攻擊者伺服器主機上的HTML頁面:

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

這裡發生了什麼?沒錯,惡意攻擊者改變了對象的原型,正是利用了__defineSetter__這個特殊方法在原型對象調用屬性設定方法的時候進行覆寫。

在這個案例中,無論何時所有的對象都會設定一個屬性名稱為Id的字段(通過__defineSetter__方法),一個通過使用alert函數顯示原型屬性值的匿名函數将被調用和觸發。注意,該腳本(上面使用alert函數顯示屬性值的腳本)可以很容易地替換成将資料上傳給攻擊者的腳本,敏感資料就是是以被洩露的。

正如我前面所說的(要實作上面的動作),攻擊者需要你通路他的惡意網頁,前提你剛剛登陸這個受攻擊的頁面不久并且頁面中的會話仍然保持有效。最常見的釣魚攻擊伎倆就是通過發送一個包含惡意網站連結的郵件(誘騙受害人點選連結)。

如果你運氣不好沒注意到這個釣魚連結,當你點選了這個連結時你仍然保持原來網站的登陸狀态,那麼在進入釣魚網站後載入script标簽的引用時,浏覽器将會發送你的身份驗證cookie至原來的網站。直到與原來網站連結上為止,你會發送一個包含有效身份驗證的請求進而通過響應取得JSON資料,這些資料會擷取到你的浏覽器中。這可能聽起來很熟悉,因為它真的是一個變種的跨站請求僞造(CSRF)攻擊,之前我有寫過相關的文章。

PS:這裡原來的網站指的是受攻擊的站點,載入的script标簽指的是這段代碼:

如果你想親身感受一下,你可以從GitHub上擷取這個CodeHaacks的解決方案,并在本地運作JsonHijackDemo項目(右鍵單擊該項目,并選擇設定為啟動項目,隻需按照項目首頁上的說明進行攻擊操作,它會告訴你要通路這個頁面:http://demo.haacked.com/security/JsonAttack.html 。

請注意,這種攻擊方式對IE8浏覽器是無法起作用的,它會提示你 __defineSetter__不是一個有效的方法。上一次我測試的時候,在Chrome和Firefox都可以正常的起到作用。

避免措施是簡單的。要麼從不發送的JSON數組或者擷取資料時必須使用一個HTTP POST請求(除非這些資料不是敏感的資料,在這種情況下你可能毫不/無需不關心這些) 。例如,在ASP.NET MVC中,你可以使用AcceptVerbsAttribute執行,像下面這樣:

[Authorize]
[AcceptVerbs(HttpVerbs.Post)]
/*
*這就糾正下,現在新的ASP.NET MVC中,如果要限制操作方法隻能接受POST請求,
*隻要在操作方法上聲明[HttpPost]這個特性就可以了
*原文中的[AcceptVerbs(HttpVerbs.Post)]已經被淘汰不用了。
*/
public JsonResult AdminBalances() {
  var balances = new[] {
    new {Id = , Balance=}, 
    new {Id = , Balance=},
    new {Id = , Balance=}
  };
  return Json(balances);
}
           

這個方法存在着一個問題,許多像JQuery這樣的JavaScript類庫預設發送JSON請求都是使用GET,而不是POST。例如,$.getJSON 預設就是發起一個GET請求。是以調用這個JSON的服務時,你需要確定你的用戶端庫是發出一個POST請求。

ASP.NET和WCF的JSON服務端點實際上是用"d"屬性将自己的JSON對象包裹起來,前段時間我有寫一篇關于這個的問題的文章。雖然這聽起來很奇怪,要通路資料必須要經過這個屬性,實際上這是不優雅的緩解方式 通過所生成的用戶端代理這些服務來去除“d”屬性,以便最終的使用者不需要知道這是曾經發生過的。

在ASP.NET MVC (和其他類似的架構)中,大部分開發人員沒有使用用戶端生成代理(我們不需要生成),而是使用jQuery和其他類似的庫調用到這些方法,使“d”屬性的問題變得有點兒尴尬。

檢查報頭怎麼樣?

你們當中有些人可能會想, “為什麼不在服務響應一個GET請求之前,讓JSON服務做一個特殊的報頭檢查,例如 X-Requested-With:XMLHttpRequest或者Content-Type:application/json 這樣的報頭”。 我也認為這可能是一個很好的緩解方法,因為大多數的用戶端庫總會發送其中的一個報頭,但浏覽器響應script标簽發送的GET請求則不會發送報頭。

這樣做的問題(如幾個同僚向我指出)是在過去的某個時刻,使用者可能已經制造了一個擷取JSON的合法GET請求,在這種情況下它(指前面說的合法GET請求)也許會在使用者的浏覽器中或者在受害者的浏覽器和受攻擊的網站之間的一些代理伺服器中被緩存起來。

在這種情況下,當浏覽器從腳本中産生了GET請求,該請求可能會被浏覽器緩存或代理緩存實作(指發送的GET請求所傳回的資料是直接從緩存中擷取)。你可以試下設定No-Cache報頭,在設定了之後,你所信任的浏覽器和所有的代理服務能正确執行緩存而且使用者(應該是指使用者發送的請求)不會不小心被覆寫。

當然,如果你使用SSL來提供JSON,這種特殊的緩存問題就不是​​一個問題了。

真正的問題?

在Mozilla開發者中心有一篇文章這樣陳述:對象和數組初始化取值時不應該調用setters,而在這一點上,我表示同意,雖然文章中有産生争論的評論:浏覽器或許真的不應該執行腳本,無論腳本有什麼樣的内容和類型,這也是一個有效的申訴。

但是最終來說,配置設定責任(應該是指上文中浏覽器禁用腳本)并不會讓您的網站更安全。這些類型的浏覽器怪癖會繼續不時地冒出來,我們作為Web開發人員需要處理它們。 Chrome浏覽器2.0.172.31和Firefox 3.0.11都容易受到這個漏洞攻擊。

IE8不容易受到這個漏洞攻擊因為它不支援這個方法。我沒有在IE 7或者IE 6 浏覽器上試驗過。

這種預設的情況在我看來是安全的:通路JSON的預設行為也許應該使用POST方式而選擇性的使用GET方式 ,而不是在目前的用戶端中使用其他的方式來通路JSON。你覺得如何呢?并且在其他平台上你工作中是如何處理這些問題呢?我很想聽聽你的想法。

如果你沒有領會本文,這裡有再現步驟:從GitHub上擷取CodeHaacks這個項目解決方案,并運作JsonHijackDemo項目在本地(右鍵單擊該項目并選擇設為啟動項目。隻要按照該項目的首頁上的說明就能看到整個攻擊行動的流程。要看到一個成功的漏洞攻擊流程,你需要使用容易受攻擊(指支援__defineSetter__方法的)的浏覽器,例如Firefox 3.0.11 。

我之後有釋出《建議修複JSON》這篇文章來描述防止這一特别問題。

作者:十有三

出處:https://shiyousan.com/post/635441704246553316

版權聲明:本文采用知識共享許可協定:署名-相同方式共享 4.0 國際(CC BY-SA 4.0)。歡迎轉載本文,轉載請聲明出處或保留此段聲明。

繼續閱讀