前言
最近在做CTF題的時候遇到這個考點,想起來自己之前在做實驗吧的入門CTF題的時候遇到過這個點,當時覺得難如看天書一般,現在回頭望去,仔細琢磨一番感覺也不是那麼難,這裡就寫篇文章記錄一下自己的學習的過程。
正文
何為HASH長度擴充攻擊?
簡單的說,由於HASH的生成機制原因,使得我們可以人為的在原先明文資料的基礎上新增新的擴充字元,使得原本的加密鏈變長,進而控制加密鏈的最後一節,使得我們得以控制最終結果。
這裡我們以MD5加密演算法為例子。
MD5長度擴充攻擊
下面是個簡單的PHP例子。
<?php
include "flag.php";
$secretKey = 'xxxxxx'; #xxx為未知內容,但長度已知為6。
$v1 = $_GET['str'];
$sign = $_GET['sign'];
$token = md5($secretKey.$v1);
if($v1 === 'test') {
die($token); #token=2df51a84abc64a28740d6d2ae8cd7b16
} else {
if($token === $sign) {
die($flag);
}
}
?>
在這個例子中,我們需要使得變數$token
與我們輸入的sign引數滿足一致才會輸出flag。
而由於我們無法知道變數$secretKey
的內容,所以無法得到$token
的值,故而看似是沒有辦法獲取到flag的死局,而這時便輪到我們的擴充攻擊來大顯身手了。
MD5演算法流程
若想搞清楚原理,其演算法的流程是必須瞭解的。不過我們無需去關心那些複雜的運算,只需要知道的大概的一個流程就OK了。
這裡借一張神圖:
看不懂也沒關係,相信你看完我這篇文章後再返回來看這張圖就很清晰明瞭了。
我們還是舉個例子,對於字串aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbb
(64個a、3個b)。長度為19個字元,且根據ASCII表,字元a、b的十六進位制分別為0x61、0x62。
而我們知道,1
位十六進位制相當於4
位二進位制表示(16=2^4
)。所以對於64個字元a的長度來說,其二進位制長度為:字元長度*二進位制位數2*十六進位制轉二進位制位數擴充4=64*2*4=512
。
對於MD5演算法來說,我們需要將原資料進行分塊處理,以512位個二進位制資料為一塊。”最後“一塊的處理分為以下幾種情況:
- 明文資料的二進位制資料長度<=448,填充padding(無意義佔位)資料使其長度為448,再新增原始明文資料的二進位制長度資訊(64位)使其長度為512位即可。
- 448<明文資料的二進位制資料長度<=512,填充padding資料至下一塊的448位,而後再新增原始明文資料的二進位制長度資訊(64位)使其長度為512位即可。
兩種情況如下圖:
注意:每塊資料的長度均為512位二進位制,圖中的資料我沒有全都用二進位制來表示,將明文資料分塊之後就可以與向量進行運算了。
對於padding資料(長度不定)來說:首位二進位制位1,其餘位為0.
對於長度資訊位(長度8Byte=64bit)來說,從低位向高位數,如上圖的長度資訊:f0 03 00 00 00 00 00 00
即代表0x03f0,其對應的十進位制為1008,即為64+62=126個字元的二進位制位數(一個字元1Byte即8bit)。
對於MD5演算法來說,有一串初始向量如下:
A=0x67452301
B=0xefcdab89
C=0x98badcfe
D=0x10325476
這串初始向量的值是固定的,作為與第一塊資料運算的原始向量。
當這串向量與第一塊資料塊運算之後,得到了一串新的向量值,這串新的向量值接著與第二塊資料塊參加運算,直到最後一塊資料塊。
如下圖所示:
而最後的MD5值就是這最後的向量串經過如下轉換的結果。
如向量串:
A=0xab45bc01
B=0x6a64bb53
C=0x23ba8afe
D=0x46847a62
先兩兩為一組進行組合,得到如下資料:
ab 45 bc 01
6a 64 bb 53
23 ba 8a fe
46 84 7a 62
再進行高低位互換,得到如下資料:
01 bc 45 ab
53 bb 64 6a
fe 8a ba 23
62 7a 84 46
最終拼接得到MD5值:01bc45ab53bb646afe8aba23627a8446
。
現在,讓我們回到開始的那個例子。
對於MD5值:2df51a84abc64a28740d6d2ae8cd7b16。我們可以根據MD5與向量互轉規則,將MD5轉成md5($secretKey + "test")
的最終向量值(A'、B'、C'、D'):
A'=0x841af52d
B'=0x284ac6ab
C'=0x2a6d0d74
D'=0x167bcde8
過程如圖:
這時候我們修改$v1
變數的內容為:
"test" + [0x80 + (0x0)*45] + [0x50 + 0x0*7] + "abc"
相當於:
"test" + padding資料 + 長度資料 + "abc"
則上述過程則被延續成下圖所示:
而對於上述運算過程來說,我們知道了倒數第二個向量串的內容和最後一個資料塊,這樣一來,最終的MD5值我們也可以自己通過MD5演算法計算出來了。
擴充
如同MD5演算法那般分組後與向量運算的流程被統稱為Merkle–Damgård結構。
而同樣使用此結構的HASH演算法還有:SHA1、SHA2等
hashpump
hashpump是一個專門生成MD5長度擴充攻擊payload的工具。
Github倉庫:https://github.com/bwall/HashPump
安裝方法:
#Linux
git clone https://github.com/bwall/HashPump.git
apt-get install g++ libssl-dev
cd HashPump
make
make install
安裝好之後在終端裡輸入hashpump,回車即可:
以之前的例子為例,使用hashpump生成payload:
故我們的EXP即為(\x
用%
代替):
/?str=test%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%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00P%00%00%00%00%00%00%00abc&sign=bac6cb2d585d2de3f5f48f2759d2e5a7
成功讀取FLAG:
CTF
相關CTF題可供練習:
- [De1CTF2019]SSRFMe
- 實驗吧-讓我進去
後記
其實這個知識點確實不難,但是回看兩年前的自己,那時候是真的完完全全看不懂看不明白,但是現在只花了十幾分鍾就可以說是掌握這個知識點了。原來我們不知不覺間也對知識的認知又提升了一個臺階,原先難如天書的內容現在看來也不過爾爾,原先看不到、接觸不到的知識,現在也有信心能夠去嘗試去學習、去理解並掌握。學習本該如此,如攀登高山一般,只有開始攀登,才有機會看得到山腳下看不到的風景,也唯有不斷攀登,才能看到更多更多風景。