原作者:KTWO
出處:https://www.k2zone.cn/?p=2019
0X00 摘要
CTF中md5判等可使用0e繞過,但是如果是雙md5該如何繞過呢?本文將教你如何繞過md5(md5($_GET[‘a’])) == md5($_GET[b’])。
0X01 引言
在php中,所有變量都是若類型的,在使用if判等的時候要格外小心,使用特殊的參數可能會使本來不相等的if判斷位相等,比如下面的例子。
<?php
if (isset($_GET['a']) && isset($_GET['b'])) {
$a = $_GET['a'];
$b = $_GET['b'];
if ($a != $b && md5($a) == md5($b)) {
echo "flag{XXXXX}";
} else {
echo "wrong!";
}
} else {
echo 'wrong!';
}
?>
上面只要傳入參數a=s1885207154a,b=s1836677006a,即可,為什么呢?看一下這兩個字符串的md5值可以返現分別如下:
MD5值:
md5("s1885207154a") => 0e509367213418206700842008763514
md5("s1836677006a") => 0e481036490867661113260034900752
二者都是0e開頭,在php中0e會被當做科學計數法,就算后面有字母,其結果也是0,所以上面的if判斷結果使true,成功繞過!
0x02 雙MD5碰撞繞過
上面只是小試牛刀,下面看一個升級版:
<?php
if (isset($_GET['a']) && isset($_GET['b'])) {
$a = $_GET['a'];
$b = $_GET['b'];
if ($a != $b && md5($a) == md5(md5($b)) {
echo "flag{XXXXX}";
} else {
echo "wrong!";
}
} else {
echo 'wrong!';
}
?>
雙面的判斷出現了md5(md5($b),有了前面的鋪墊,這里我們第一感覺就是找到一個字符串其MD5值的MD5仍然是0e開頭的那就好了。開始的時候我不敢相信,那幾率得多小啊,但是在昨天做一道md5截斷碰撞的時候我就來了靈感,何不嘗試一下,結果發現原來這種字符串使真的存在,並且碰撞0e開頭的時候不到一秒鍾就能碰撞到。各位觀眾,下面請看:
MD5值:
md5("V5VDSHva7fjyJoJ33IQl") => 0e18bb6e1d5c2e19b63898aeed6b37ea
md5("0e18bb6e1************") => 0e0a710a092113dd5ec9dd47d4d7b86f
原來真的存在0e開頭的MD5值其md5結果也是0e開頭,所以此題答案便出來了。a=s1885207154a,b=V5VDSHva7fjyJoJ33IQl即可繞過if判斷。
其實上面的這種雙md5值0e開頭的字符串有很多,但是網上似乎很見到,幾乎沒有,下面發布一些。
0x03 雙md5結果仍為0e開頭字符串大全
MD5大全:
CbDLytmyGm2xQyaLNhWn
md5(CbDLytmyGm2xQyaLNhWn) => 0ec20b7c66cafbcc7d8e8481f0653d18
md5(md5(CbDLytmyGm2xQyaLNhWn)) => 0e3a5f2a80db371d4610b8f940d296af
770hQgrBOjrcqftrlaZk
md5(770hQgrBOjrcqftrlaZk) => 0e689b4f703bdc753be7e27b45cb3625
md5(md5(770hQgrBOjrcqftrlaZk)) => 0e2756da68ef740fd8f5a5c26cc45064
7r4lGXCH2Ksu2JNT3BYM
md5(7r4lGXCH2Ksu2JNT3BYM) => 0e269ab12da27d79a6626d91f34ae849
md5(md5(7r4lGXCH2Ksu2JNT3BYM)) => 0e48d320b2a97ab295f5c4694759889f
0x04 MD5碰撞腳本
授人以魚不如授人以漁,下面使多線程MD5哈希碰撞腳本,威力巨大。是根據網上代碼改編而成,非原創。
# -*- coding: utf-8 -*-
import multiprocessing
import hashlib
import random
import string
import sys
CHARS = string.letters + string.digits
def cmp_md5(substr, stop_event, str_len,. start=0, size=20):
global CHARS
while not stop_event.is_set():
rnds = ''.join(random.choice(CHARS) for _ in range(size))
md5 = hashlib.md5(rnds)
value = md5.hexdigest()
if value[start: start+str_len] == substr:
print rnds
stop_event.set()
'''
#碰撞雙md5
md5 = hashlib.md5(value)
if md5.hexdigest()[start: start+str_len] == substr:
print rnds+ "=>" + value+"=>"+ md5.hexdigest() + "\n"
stop_event.set()
'''
if __name__ == '__main__':
substr = sys.argv[1].strip()
start_pos = int(sys.argv[2]) if len(sys.argv) > 1 else 0
str_len = len(substr)
cpus = multiprocessing.cpu_count()
stop_event = multiprocessing.Event()
processes = [multiprocessing.Process(target=cmp_md5, args=(substr,
stop_event, str_len, start_pos))
for i in range(cpus)]
for p in processes:
p.start()
for p in processes:
p.join()
上面腳本注釋部分是雙MD5碰撞,取消注釋然后注釋掉16行即可。
使用方法:python md5Crack.py "你要碰撞的字符串" 字符串的起始位置
例如:python md5Crack.py “0e" 0
將產生MD5值為0e開頭的字符串。