1. 程式人生 > >沒想到 Hash 衝突還能這麼玩,你的服務中招了嗎?

沒想到 Hash 衝突還能這麼玩,你的服務中招了嗎?

背景

其實這個問題我之前也看到過,剛好在前幾天,洪教授在某個群裡分享的一個《一些有意思的攻擊手段.pdf》,我覺得這個話題還是有不少人不清楚的,今天我就準備來“實戰”一把,還請各位看官輕拍。

洪強寧(洪教授),愛因互動創始人兼 CTO,曾任豆瓣首席架構師,為中國 Python 使用者組(CPUG)的創立者之一。

這才是真大佬,原來洪教授在宜信的時候,就有分享過這個內容,可惜當初不知道沒參加。看了之後才知道原來我上一篇的文章中講的 計時攻擊(Timing Attack) 也是其中的內容之一。哈哈,後面有空再研究研究繼續講其他內容。

Hash 衝突

啥叫 Hash 衝突?我們從 Hash 表(或者散列表)講起,我們知道在一個 hash 表的查詢一個元素,期望的時間複雜度為 O(1)

,怎麼做到的呢?其實就是 hash() 函式在起作用。

初略來講,hash 表內部實際儲存還是跟陣列類似,用連續的記憶體空間儲存元素,只要通過某種方法將將要儲存的元素對映為陣列的下標,即可像陣列一樣通過下標去讀取對應的元素,這也是為什麼能做到 O(1) 的原因。

Hash 示例

以上圖為例,假設是我設計的一個 hash 函式,恰好滿足如下條件:

  • hash("hello")=0:字串 "hello" 就儲存陣列下標為 0 的地方;
  • hash("world")=2: "world" 儲存陣列下標為 2 的地方;
  • hash("tangleithu")=5:"tangleithu" 儲存陣列下標為 5 的地方;

目前來看一切好像很完美,但這終歸是假設,我不能假設這個 hash 都很完美的將不同的字串都對映到了不同的下標處。

另外來了個字串,hash("石頭") = 2,怎麼辦?這就是所謂的 “Hash 衝突”,最常見 Hash 衝突的解決方案其實就是“開鏈”法,其實還有比如線性試探、平方試探等等。

類似講解 HashMap 的文章滿大街都是,一搜一大把,本文就不詳述了。為了方便讀者理解,就簡單來個例子。

Hash衝突開鏈法

開鏈法如上圖所示,我們儲存元素的時候,儲存形式為一個連結串列,當衝突的時候,就在連結串列末尾直接加衝突的元素。上圖示例恰好運氣比較差,字串 shitou

stone 算出來的下標都為 2。

這樣一來,問題大了。原本我們期望 O(1) 的時間複雜度查詢元素,現在變成在連結串列中線性查找了,而如果這個時候插入 N 個數據,最壞的情況下的時間複雜度就是 O(N^2) 了。(這裡就不討論連結串列轉樹的情形)

壞人乘機侵入

這就又給壞人留下了想象空間。只要壞人精心設計一組要放進 hash 表的字串,且讓這些字串的 hashcode 都一樣,這就會導致 hash 衝突,結果會導致 cpu 要花費大量的時間來處理 hash 衝突,造成 DoS(Denial of Service)攻擊。

而用 hash 表儲存的情形太常見了。在 Web 服務中,一般表單的處理都是用 hash 表來儲存的(後端往往要知道通過某個具體的引數 key 獲取對應的引數 value)。

實戰

本文石頭哥將以 Java SpringBoot 為例,嘗試進行一次攻擊。

不過別以為這種 “Hash 衝突 DoS” 以為只有 Java 才有哦,什麼 Python,Apache Tomcat/Jetty,PHP 之類都會有這個問題的。其實早在 2011 年年末的時候就被大量爆出了,有的框架陸陸續續有一些改進和修復。詳細情況可以看這篇文章:oCERT-2011-003 multiple implementations denial-of-service via hash algorithm collision[1]

這裡,咱們給列舉其中一個 Apatch Tomcat,來自 CVE-2011-4858[2]

Apache Tomcat before 5.5.35, 6.x before 6.0.35, and 7.x before 7.0.23 computes hash values for form parameters without restricting the ability to trigger hash collisions predictably, which allows remote attackers to cause a denial of service (CPU consumption) by sending many crafted parameters.

下面截圖來自洪教授的 PPT,但內容的具體來源不詳了(嘗試找了下,沒找到),大家參考參考就好。

實現 hash 衝突 DoS 攻擊所須頻寬

左邊表示用不同的語言(框架)實現這種攻擊所需要的頻寬,右邊是攻擊的 cpu 目標。可以看出,實施這種攻擊成本其實挺低的(後文石頭的試驗也佐證了這一點)。

PHP是世界上最好的語言1

不得不說 “PHP 是世界上最好的程式語言”(大家別打架),還是有一定道理的,哈哈哈哈哈哈