0x00 前言
md5碰撞只是一種掌握php弱型別的方式,弱型別的內容有很多,陣列、字串比較等等,但不論以哪種方式考,涉及的知識點都是相通的,希望通過對基礎知識的分享與大家一同學習進步,
0x01 什么是md5
“MD5,即訊息摘要演算法(英語:MD5 Message-Digest Algorithm),是一種被廣泛使用的密碼散列函式,將資料(如一段文字)運算變為另一固定長度值,是散列演算法的基礎原理,可以產生出一個128位(16位元組)的散列值(hash value),用于確保資訊傳輸完整一致,
顯然128位不足以把世界上所有訊息的摘要毫不重復的計算出來,當然現在16位元組(128位)、32位元組(256位)的md5也都有,選擇位數多的方式可以在一定程度上減少硬碰撞(collision)的可能性,
在php語言中呼叫中md5的使用方式為:
<?php
$str = "hello world!";
echo "str_md5-->",md5($str),"<br/>"; //為字串計算摘要值
$files = "02.php";
echo "file_md5-->",md5_file($files);//為檔案計算摘要值
?>
得到默認32位元組的密文,

由于php中的md5默認回傳32位元組的結果,所以要得到16位元組的需要使用字串截取,
<?php
$str = "hello world!";
echo "32byte-->",md5($str),"<br/>"; //為字串計算摘要值
$md5Str = substr(md5($str),8,16);//獲取16位元組的摘要值
echo "16byte-->",$md5Str;
?>
這是因為32位元組的字串中間8-24位元組與16位元組加密的結果是相同的,所以在php中我們可以通過這一方式來獲得16位元組的摘要值,

0x02 什么是php弱型別
php是一門弱型別的語言,它不會嚴格檢驗變數型別,變數可以不顯示地宣告其型別,而是在運行期間直接賦值,
在php中比較是否相等有兩種符號:==和===,
其中==在比較時會將不同型別的變數或值轉換為相同型別再進行比較,
而===則直接比較型別是否相同,如果同型別,再比較值,
那么php弱型別這里的“弱”,指的不是某個型別有什么問題,而是整個php語言中某些函式在處理賦值、字串比較、變數比較的程序中對型別似乎并不關心,榷訓了型別帶來的影響,在使用某個變數時不需要我們定義變數的型別,而是根據內容判斷這是什么型別,從而導致了各種漏洞的發生,
例如:
<?php
var_dump("a"==0); //true
var_dump("1a"==1); //true
var_dump("a1"==1); //false
var_dump("a1"==0); //true
var_dump("0e123456"=="0e234567"); //true
var_dump(0=="1a"); //false
?>
其結果為:
bool(true)
bool(true)
bool(false)
bool(true)
bool(true)
bool(false)
上述判斷輸出的原因是,在php中當一個字串被當作一個數值來取值時,如果該字串沒有包含'.','e','E',并且其數值值在整形的范圍之內時,該字串被當作int來取值,其他所有情況下都被作為float來取值,而該字串的開始部分決定了它的值,如果該字串以合法的數值開始,則使用該數值,否則其值為0,
所以在上面的情況中,1a轉換為1,a1轉換為0,而"0e123456"=="0e234567"相互比較的時候,會將0e這類字串識別為科學計數法的數字,0的無論多少次方都是零,所以相等,
0x03 md5==繞過(0e比較)
<?php
$flag = 'ook!';
$a = $_GET['a'];
if ($a != 'QNKCDZO' && md5($a) == md5('QNKCDZO')) {
echo $flag;
}else{
echo('你的答案不對0.0');
}
上面這段代碼中就是上述0e開頭的所有字串都被認為是0,所以我們先看看md5('QNKCDZO')的結果是0e830400451993494058024219903391,那么所有0e開頭的md5串都可以滿足上面的條件,
常用的有:
QNKCDZO
0e830400451993494058024219903391
240610708
0e462097431906509019562988736854
s878926199a
0e545993274517709034328855841020
s155964671a
0e342768416822451524974117254469
s214587387a
0e848240448830537924465865611904
包含了純數字、純字母、數字字母組合三種型別的結果,在沒有條件限制的情況下,無論使用哪一個都可以成功繞過,在上面的代碼中使用?a=240610708,即可列印flag值,
0x04 md5===繞過(陣列比較)
在php中的hash函式md5、sha1等處理中若傳入一個陣列的值,則會報錯回傳NULL,而回傳的值在型別和內容上都是相同的,所以可以用來繞過某些兩邊引數可控的場景,上面只能控制一邊的值傳入,所以陣列型別不適用,
<?php
$flag = "ook!";
$a = $_GET['a'];
$b = $_GET['b'];
if ($a != $b && md5($a) === md5($b)) //這里==也可以使用陣列繞過,
echo $flag;
?>
上述的例子中傳入?a[]=a&b[]=b即可滿足既不相等,md5后又相等的條件,雖然報錯,但仍然輸出了正確的值,
Warning: md5() expects parameter 1 to be string, array given in 1.php on line 5
Warning: md5() expects parameter 1 to be string, array given in 1.php on line 5
ook!
0x05 md5===繞過(硬碰撞)
前面我們也提到了md5無論是32位還是16位,都不可能不重復的表示所有資訊,這種重復的例子就稱為 硬碰撞 ,有如下代碼:
<?php
$s1 = $_GET['a'];
$s2 = $_GET['b'];
$s3 = $_GET['c'];
echo md5($s1),"<br/>";
echo md5($s2),"<br/>";
echo md5($s3),"<br/>";
?>
當傳入的值為(url編碼后)
?a=%af%13%76%70%82%a0%a6%58%cb%3e%23%38%c4%c6%db%8b%60%2c%bb%90%68%a0%2d%e9%47%aa%78%49%6e%0a%c0%c0%31%d3%fb%cb%82%25%92%0d%cf%61%67%64%e8%cd%7d%47%ba%0e%5d%1b%9c%1c%5c%cd%07%2d%f7%a8%2d%1d%bc%5e%2c%06%46%3a%0f%2d%4b%e9%20%1d%29%66%a4%e1%8b%7d%0c%f5%ef%97%b6%ee%48%dd%0e%09%aa%e5%4d%6a%5d%6d%75%77%72%cf%47%16%a2%06%72%71%c9%a1%8f%00%f6%9d%ee%54%27%71%be%c8%c3%8f%93%e3%52%73%73%53%a0%5f%69%ef%c3%3b%ea%ee%70%71%ae%2a%21%c8%44%d7%22%87%9f%be%79%6d%c4%61%a4%08%57%02%82%2a%ef%36%95%da%ee%13%bc%fb%7e%a3%59%45%ef%25%67%3c%e0%27%69%2b%95%77%b8%cd%dc%4f%de%73%24%e8%ab%66%74%d2%8c%68%06%80%0c%dd%74%ae%31%05%d1%15%7d%c4%5e%bc%0b%0f%21%23%a4%96%7c%17%12%d1%2b%b3%10%b7%37%60%68%d7%cb%35%5a%54%97%08%0d%54%78%49%d0%93%c3%b3%fd%1f%0b%35%11%9d%96%1d%ba%64%e0%86%ad%ef%52%98%2d%84%12%77%bb%ab%e8%64%da%a3%65%55%5d%d5%76%55%57%46%6c%89%c9%df%b2%3c%85%97%1e%f6%38%66%c9%17%22%e7%ea%c9%f5%d2%e0%14%d8%35%4f%0a%5c%34%d3%73%a5%98%f7%66%72%aa%43%e3%bd%a2%cd%62%fd%69%1d%34%30%57%52%ab%41%b1%91%65%f2%30%7f%cf%c6%a1%8c%fb%dc%c4%8f%61%a5%93%40%1a%13%d1%09%c5%e0%f7%87%5f%48%e7%d7%b3%62%04%a7%c4%cb%fd%f4%ff%cf%3b%74%28%1c%96%8e%09%73%3a%9b%a6%2f%ed%b7%99%d5%b9%05%39%95%ab
&b=%af%13%76%70%82%a0%a6%58%cb%3e%23%38%c4%c6%db%8b%60%2c%bb%90%68%a0%2d%e9%47%aa%78%49%6e%0a%c0%c0%31%d3%fb%cb%82%25%92%0d%cf%61%67%64%e8%cd%7d%47%ba%0e%5d%1b%9c%1c%5c%cd%07%2d%f7%a8%2d%1d%bc%5e%2c%06%46%3a%0f%2d%4b%e9%20%1d%29%66%a4%e1%8b%7d%0c%f5%ef%97%b6%ee%48%dd%0e%09%aa%e5%4d%6a%5d%6d%75%77%72%cf%47%16%a2%06%72%71%c9%a1%8f%00%f6%9d%ee%54%27%71%be%c8%c3%8f%93%e3%52%73%73%53%a0%5f%69%ef%c3%3b%ea%ee%70%71%ae%2a%21%c8%44%d7%22%87%9f%be%79%6d%c4%61%a4%08%57%02%82%2a%ef%36%95%da%ee%13%bc%fb%7e%a3%59%45%ef%25%67%3c%e0%27%69%2b%95%77%b8%cd%dc%4f%de%73%24%e8%ab%66%74%d2%8c%68%06%80%0c%dd%74%ae%31%05%d1%15%7d%c4%5e%bc%0b%0f%21%23%a4%96%7c%17%12%d1%2b%b3%10%b7%37%60%68%d7%cb%35%5a%54%97%08%0d%54%78%49%d0%93%c3%b3%fd%1f%0b%35%11%9d%96%1d%ba%64%e0%86%ad%ef%52%98%2d%84%12%77%bb%ab%e8%64%da%a3%65%55%5d%d5%76%55%57%46%6c%89%c9%5f%b2%3c%85%97%1e%f6%38%66%c9%17%22%e7%ea%c9%f5%d2%e0%14%d8%35%4f%0a%5c%34%d3%f3%a5%98%f7%66%72%aa%43%e3%bd%a2%cd%62%fd%e9%1d%34%30%57%52%ab%41%b1%91%65%f2%30%7f%cf%c6%a1%8c%fb%dc%c4%8f%61%a5%13%40%1a%13%d1%09%c5%e0%f7%87%5f%48%e7%d7%b3%62%04%a7%c4%cb%fd%f4%ff%cf%3b%74%a8%1b%96%8e%09%73%3a%9b%a6%2f%ed%b7%99%d5%39%05%39%95%ab
&c=%af%13%76%70%82%a0%a6%58%cb%3e%23%38%c4%c6%db%8b%60%2c%bb%90%68%a0%2d%e9%47%aa%78%49%6e%0a%c0%c0%31%d3%fb%cb%82%25%92%0d%cf%61%67%64%e8%cd%7d%47%ba%0e%5d%1b%9c%1c%5c%cd%07%2d%f7%a8%2d%1d%bc%5e%2c%06%46%3a%0f%2d%4b%e9%20%1d%29%66%a4%e1%8b%7d%0c%f5%ef%97%b6%ee%48%dd%0e%09%aa%e5%4d%6a%5d%6d%75%77%72%cf%47%16%a2%06%72%71%c9%a1%8f%00%f6%9d%ee%54%27%71%be%c8%c3%8f%93%e3%52%73%73%53%a0%5f%69%ef%c3%3b%ea%ee%70%71%ae%2a%21%c8%44%d7%22%87%9f%be%79%ed%c4%61%a4%08%57%02%82%2a%ef%36%95%da%ee%13%bc%fb%7e%a3%59%45%ef%25%67%3c%e0%a7%69%2b%95%77%b8%cd%dc%4f%de%73%24%e8%ab%e6%74%d2%8c%68%06%80%0c%dd%74%ae%31%05%d1%15%7d%c4%5e%bc%0b%0f%21%23%a4%16%7c%17%12%d1%2b%b3%10%b7%37%60%68%d7%cb%35%5a%54%97%08%0d%54%78%49%d0%93%c3%33%fd%1f%0b%35%11%9d%96%1d%ba%64%e0%86%ad%6f%52%98%2d%84%12%77%bb%ab%e8%64%da%a3%65%55%5d%d5%76%55%57%46%6c%89%c9%df%b2%3c%85%97%1e%f6%38%66%c9%17%22%e7%ea%c9%f5%d2%e0%14%d8%35%4f%0a%5c%34%d3%73%a5%98%f7%66%72%aa%43%e3%bd%a2%cd%62%fd%69%1d%34%30%57%52%ab%41%b1%91%65%f2%30%7f%cf%c6%a1%8c%fb%dc%c4%8f%61%a5%93%40%1a%13%d1%09%c5%e0%f7%87%5f%48%e7%d7%b3%62%04%a7%c4%cb%fd%f4%ff%cf%3b%74%28%1c%96%8e%09%73%3a%9b%a6%2f%ed%b7%99%d5%b9%05%39%95%ab
三個回傳相同的md5值,可以通過其有限計算的特性繞過特定的條件,
ea8b4156874b91a4ef00c5ca3e4a4a34
ea8b4156874b91a4ef00c5ca3e4a4a34
ea8b4156874b91a4ef00c5ca3e4a4a34
0x06 json解碼繞過
php在處理傳入的json串時使用json_decode將其解碼,再進行比較時,我們不需要知道比較字串的內容,也可以利用字串與0比較為真的特點繞過,
<?php
$flag = 'ook!';
$a = $_GET['a'];
$b = json_decode($a);
echo $b->abc;
var_dump($b->abc == $flag);
if ($b->abc == $flag)
echo $flag;
else
echo "error!!";
?>
當傳入?a={"abc":0}時,分別輸出
0
bool(true)
ook!
注意這里的{"abc":0},0是數字,而加雙引號{"abc":"0"}之后兩邊都是字符,就不相等了,
0x07 array_search 繞過
原理都是型別轉換的問題,函式的原型為:
mixed array_search ( mixed $needle , array $haystack [, bool $strict = false ] )
其中$needle,$haystack必需,$strict可選 函式判斷$haystack中的值是存在$needle,存在則回傳該值的鍵值(陣列的下標,例如該值在第一位回傳0,第二位回傳1),第三個引數默認為false,如果設定為true則會進行嚴格過濾(帶型別的比較),
<?php
$a = array(0,1,2,3);
var_dump(array_search("a",$a));
var_dump(array_search("1a",$a));
var_dump(array_search("2a",$a));
var_dump(array_search("1a",$a,true));
?>
其結果為:
int(0)
int(1)
int(2)
bool(false)
0x08 小結
從上面我們可以看出,所有繞過的形式都是基于弱型別的比較,或值對于不合規引數型別的錯誤處理,陣列與字串、字串與整數等等情形,我們不能保證每個用戶都乖乖輸入我們想要的值,因此在php中限定用戶輸入的型別和值就顯得尤為重要,
這里感謝 Mrsm1th 師傅的分享,自己動手接受知識會更快,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/292876.html
標籤:其他
