長度擴展攻擊詳解

這幾天在看密碼學的長度擴展攻擊,看了不少文章,感覺都說得不夠清楚,自己弄清楚之後想寫一篇,做一個記錄。


1. 簡介

       長度擴展攻擊(length extension attack),是指針對某些允許包含額外信息的加密散列函數的攻擊手段。對於滿足以下條件的散列函數,都可以作爲攻擊對象:

       ① 加密前將待加密的明文按一定規則填充到固定長度(例如512或1024比特)的倍數;

       ② 按照該固定長度,將明文分塊加密,並用前一個塊的加密結果,作爲下一塊加密的初始向量(Initial Vector)。

       滿足上述要求的散列函數稱爲Merkle–Damgård散列函數(Merkle–Damgård hash function),下列散列函數都屬於Merkle–Damgård散列函數:

  • MD4
  • MD5
  • RIPEMD-160
  • SHA-0
  • SHA-1
  • SHA-256
  • SHA-512
  • WHIRLPOOL
       對於H(salt+data)形式的加密,在以下條件滿足的情況下,攻擊者可以通過該方法獲取H(salt+一定規則構造的data):

       ① 知道密文的加密算法且該算法滿足Merkle–Damgård散列函數特徵;

       ② 不知道salt,但知道salt的長度,並可控制data的值;

       ③ 可以得到一個H(salt+data)的值。


2. 攻擊方法詳解

         下面以MD5算法爲例,講述該攻擊方式如何進行攻擊。

         百度百科中詳細闡述了MD5算法的實現過程https://baike.baidu.com/item/MD5/212708?fr=aladdin,我們並不需要知道MD5具體的算法是怎麼回事,只需要知道它的實現是滿足上面所說的Merkle–Damgård散列函數的兩個條件的,具體過程是這樣的:

        ① 填充

        拿到明文後,MD5現將明文轉爲二進制文件,然後將二進制文件的長度除以512比特(即64字節),如果餘數等於448比特(即64-8字節),那麼直接在後面加上八個字節的長度標識,使之成爲512比特的倍數。否則則在明文後填一個1,再填充0直至其長度除以512等於448,再加上8位的長度標識。長度是使用大端序(big Endian)來存儲,即低字節放在高地址位上。

        比如加密的明文是admin,其二進制文件以16進製表示是0x61646d696e,長度是40比特(5字節),那麼需要補充408比特(51字節)的填充符,填充內容第一位是1,其餘全部是0。16進製表示是0x800000000000000000000000000000000000000000000000000。然後再添加8個字節的長度標識,admin長度爲40比特,16進制是0x28,這個28要放在高位,就是0x280000000000000。結果如下所示:


         ② 分塊運算

         填充完畢後,函數就將填充後的明文以512比特的長度分塊,進行運算。在運算中會用到四個初始向量(MD5中稱作鏈變量,Chaining Variable),分別是A=0x67452301,B=0xefcdab89,C=0x98badcfe,D=0x10325476。經過一系列複雜的數學運算,函數會得到第一塊的MD5值,然後將該MD5值分成四塊,以大端序形成新的鏈變量,投入到第二塊的運算,形成新的MD5值……以此類推,直到算出最後一塊的MD5值,就是整個數據塊的MD5值。

        例如加密的明文是adminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadmin,首先根據
①的描述的算法填充後,分成兩塊進行運算,第一塊是adminadminadminadminadminadminadminadminadminadminadminadminadmi,其MD5碼是e7d6ca05773d038378f5e2674850be25,分成四塊並以大端序存儲,則A=0x05cad6e7,B=0x83033d77,C=0x76e2f578,D=0x25be5048。將這四個變量作爲鏈變量投入運算,再將第二部分加密,得到最終的MD5值是9ea2d490481dbcdadf61e7e404b99585。

        流程如下圖所示:


        如果攻擊者知道MD5(salt+data)的值並可控制data的值,攻擊者可以設定data爲與data+padding+append等長的任意字符串,然後計算MD5(str+append)。我們知道,MD5需要先填充再運算,攻擊者可以在程序計算append所在塊之前,將MD5(salt+data)的值直接替換掉初始的鏈變量,就能夠算出MD5(salt+data+padding+append)的值了。設MD5(CV,data)表示以鏈變量CV計算data的MD5值,那麼(爲簡便起見,這裏設append的長度不超過448比特,超過的原理也類似)

MD5(IV, salt+data+padding+append) = MD5(MD5(IV, salt+data), append)。

         攻擊原理如下:


         之所以要知道salt的長度,是爲了確保salt+data+padding+append和攻擊者輸入的data擁有相同的填充(即padding2相等),以確保最後一步的運算得到相同的結果。


3. 示例

         以實驗吧一道CTF題爲例,展示長度擴展攻擊的過程。

         這道題給出了頁面的代碼:

<?php 

$flag = "flag{flag is here}";
$secret = "xxxxxxxxxxxxxxxx"; // This secret is 15 characters long for security!

$username = $_POST["username"];
$password = $_POST["password"];

if (!empty($_COOKIE["getmein"])) {
    if (urldecode($username) === "admin" && urldecode($password) != "admin") {
        if ($_COOKIE["getmein"] === md5($secret . urldecode($username . $password))) {
            echo "Congratulations! You are a registered user.\n";
            die ("The flag is ". $flag);
        }
        else {
            die ("Your cookies don't match up! STOP HACKING THIS SITE.");
        }
    }
    else {
        die ("You are not an admin! LEAVE.");
    }
}

setcookie("sample-hash", md5($secret . urldecode("admin" . "admin")), time() + (60 * 60 * 24 * 7));

if (empty($_COOKIE["source"])) {
    setcookie("source", 0, time() + (60 * 60 * 24 * 7));
}
else {
    if ($_COOKIE["source"] != 0) {
        echo ""; // This source code is outputted here
    }
}
?>

       根據題意,我們得到以下信息:

       ①程序使用MD5算法進行加密;

       ②$secret值未知,但是知道$secret長度是15位,且知道MD5($secret+"adminadmin")的值;

       ③$password可控,需要計算MD5($secret+"admin"+$password),且$password不等於admin。

       首先設定$password="admin"+padding+append,這裏padding按照填充規則是0x8000000000000000000000000000000000000000000000000000000000000c800000000000000,append也設定爲admin,總共是69個字節(64+5)。我從百度百科上下載了MD5的JAVA實現算法,將getMD5()替換如下:

private void setChainingVariable(int A,int B,int C,int D){
    this.Atemp = A;
    this.Btemp = B;
    this.Ctemp = C;
    this.Dtemp = D;
}
public String getMD5(String source,int A,int B,int C,int D){
    init();
    int strByte[]=add(source);
    for(int i=0;i<strByte.length/16;i++){
        int num[]=new int[16];
        for(int j=0;j<16;j++){
            num[j]=strByte[i*16+j];
        }
        if(i == strByte.length/16 - 1){
           setChainingVariable(A, B, C, D);
        }
        MainLoop(num);
    }
    return changeHex(Atemp)+changeHex(Btemp)+changeHex(Ctemp)+changeHex(Dtemp);
}

       接着以隨意填充字符代替$secret+"adminadmin"+padding,我填充了64個A,然後運行程序計算AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAadmin的md5值。通過抓包,我們得到MD5($secret+"adminadmin")=e2c25a7f7fd42f0f03194d7258fbcdb6,那麼替換的鏈變量A=0x7f5ac2e2,B=0x0f2fd47f,C=0x724d1903,D=0xb6cdfb58。計算得出值是c7b48fd99a4387ad46bd6f37da840c52。最後構造POST請求,將參數改爲

username=admin&password=%61%64%6d%69%6e%80%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%c8%00%00%00%00%00%00%00%61%64%6d%69%6e

然後在COOKIE中添加getmein=c7b48fd99a4387ad46bd6f37da840c52,顯示成功:


       至此,長度擴展攻擊宣告成功。


       最後介紹一個進行長度擴展攻擊的工具:hash_extender,提供了簡單的命令來進行攻擊,詳情可參見頁面當中的說明。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章