前言
最近在做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
- 實驗吧-讓我進去
后記
其實這個知識點確實不難,但是回看兩年前的自己,那時候是真的完完全全看不懂看不明白,但是現在只花了十幾分鍾就可以說是掌握這個知識點了。原來我們不知不覺間也對知識的認知又提升了一個台階,原先難如天書的內容現在看來也不過爾爾,原先看不到、接觸不到的知識,現在也有信心能夠去嘗試去學習、去理解並掌握。學習本該如此,如攀登高山一般,只有開始攀登,才有機會看得到山腳下看不到的風景,也唯有不斷攀登,才能看到更多更多風景。