淺談md5弱類型比較和強碰撞


本文首發於“合天網安實驗室”,轉載請注明出處!

知識點實操概要

CTF多種類型題目一應俱全

鏈接指路:

 
點擊上方鏈接馬上體驗

 

前言

在CTF中,md5的題目太常見了,雖然有很多這方面的文章,但相對來說比較零散,這里主要將自己學習和比賽時遇到的md5弱類型和強碰撞的題目從淺到深地梳理一下。

基本知識

php中有兩種比較的符號==與=== ==在進行比較的時候,如果比較一個數字和字符串或者比較涉及到數字內容的字符串,則字符串會被轉換為數值並且比較按照數值來進行。

===在進行比較的時候,會先判斷兩種字符串的類型是否相等,再比較。

0e開頭且都是數字的字符串,弱類型比較都等於0。

==比較

測試代碼

 <?php
if (isset($_POST['a']) and isset($_POST['b']))
{
    if ($_POST['a'] != $_POST['b'])
    {
     if (md5($_POST['a']) == md5($_POST['b']))
      echo 'flag';
     else
      echo 'you are wrong';
 }
 else echo "請輸入不同的a,b值";
}

解法1

由於md5不能加密數組,在加密數組的時候會返回NULL

所以,我們可以傳入兩個數組

解放2

可以傳入兩個md5加密后是0e開頭的字符串,需要注意的地方是,這個以0e開頭的字符串只能是純數字,這樣php在進行科學計算法的時候才會將它轉化為0。可以查找以0e開頭md5加密相等的字符串,也可以自己編寫代碼,提供以下腳本。

<?php
for($a=1;$a<=1000000000;$a++){
   $md5 = md5($a);
   if(preg_match('/^0e\d+$/',$md5)){
      echo $a;
      echo "\n";
      echo $md5;
      echo "\n";
   }
}
s1502113478a
0e861580163291561247404381396064
  
s1885207154a
0e509367213418206700842008763514
  
s1836677006a
0e481036490867661113260034900752
  
s155964671a
0e342768416822451524974117254469
  
s1184209335a
0e072485820392773389523109082030

  

===比較

<?php
if (isset($_POST['a']) and isset($_POST['b']))
{
    if ($_POST['a'] != $_POST['b'])
    {
     if (md5($_POST['a']) === md5($_POST['b']))
      echo 'flag';
     else
      echo 'you are wrong';
 }
 else echo "請輸入不同的a,b值";
}
?>

解法1:

也可以傳入兩個數組,但不再適合傳入兩個0e開頭的字符串,因為===是md5的強碰撞,進行了嚴格的過濾。

解法2:

使用md5加密后兩個完全相等的兩個字符串來繞過過濾。

如何生成兩個不一樣的字符串,但是MD5是一樣的呢。參考如何用不同的數值構建一樣的MD5后,我們可以使用快速MD5碰撞生成器來構建兩個MD5一樣,但內容完全不一樣的字符串。

fastcoll_v1.0.0.5.exe.zip

構造

創建一個文本文件,寫入任意的文件內容,命名為ywj.txt (源文件)

運行fastcoll輸出以下參數。-p 是源文件,-o是輸出文件

fastcoll_v1.0.0.5.exe -p ywj.txt -o 1.txt 2.txt

測試

對生產的1.txt和2.txt文件進行測試

<?php 
function  readmyfile($path){
    $fh = fopen($path, "rb");
    $data = fread($fh, filesize($path));
    fclose($fh);
    return $data;
}
echo '二進制md5加密 '. md5( (readmyfile("1.txt")));
echo "</br>";
echo  'url編碼 '. urlencode(readmyfile("1.txt"));
echo "</br>";
echo '二進制md5加密 '.md5( (readmyfile("2.txt")));
echo "</br>";
echo  'url編碼 '.  urlencode(readmyfile("2.txt"));
echo "</br>";
二進制md5加密 8e4ef6c69a337c0de0208455ee69a416
url編碼 1%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%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%A3njn%FD%1A%CB%3A%29Wr%02En%CE%89%9A%E3%8EF%F1%BE%E9%EE3%0E%82%2A%95%23%0D%FA%CE%1C%F2%C4P%C2%B7s%0F%C8t%F28%FAU%AD%2C%EB%1D%D8%D2%00%8C%3B%FCN%C9b4%DB%AC%17%A8%BF%3Fh%84i%F4%1E%B5Q%7B%FC%B9RuJ%60%B4%0D7%F9%F9%00%1E%C1%1B%16%C9M%2A%7D%B2%BBoW%02%7D%8F%7F%C0qT%D0%CF%3A%9DFH%F1%25%AC%DF%FA%C4G%27uW%CFNB%E7%EF%B0

 


二進制md5加密 8e4ef6c69a337c0de0208455ee69a416
url編碼 1%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%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%A3njn%FD%1A%CB%3A%29Wr%02En%CE%89%9A%E3%8E%C6%F1%BE%E9%EE3%0E%82%2A%95%23%0D%FA%CE%1C%F2%C4P%C2%B7s%0F%C8t%F28zV%AD%2C%EB%1D%D8%D2%00%8C%3B%FCN%C9%E24%DB%AC%17%A8%BF%3Fh%84i%F4%1E%B5Q%7B%FC%B9RuJ%60%B4%0D%B7%F9%F9%00%1E%C1%1B%16%C9M%2A%7D%B2%BBoW%02%7D%8F%7F%C0qT%D0%CF%3A%1DFH%F1%25%AC%DF%FA%C4G%27uW%CF%CEB%E7%EF%B0

可以看到,1.txt和2.txt文件二進制md5加密后的結果完全相同。由於1.txt和2.txt文件中含有不可見字符,所以需要將其url編碼后使用。可以看到url編碼后的兩個字符串不完全相同,滿足我們輸入兩個不同參數的需要。

當題目限制不能傳入數組,只能傳入字符串時,如下例題,就只能采用解法2.

<?php
if((string)$_GET['a'] !== (string)$_GET['b'] && md5($_GET['a'])===md5($_GET['b'])){
 echo "you are right";
}
else {
 echo "you are wrong";
}

HECTF  ezphp

源碼

<?php 
error_reporting(0);
highlight_file(__file__);
include('flag.php'); 
$string_1 = $_GET['str1']; 
$string_2 = $_GET['str2']; 

if($_GET['param1']!==$_GET['param2']&&md5($_GET['param1'])===md5($_GET['param2'])){
        if(is_numeric($string_1)){ 
            $md5_1 = md5($string_1); 
            $md5_2 = md5($string_2); 
            if($md5_1 != $md5_2){ 
                $a = strtr($md5_1, 'cxhp', '0123'); 
                $b = strtr($md5_2, 'cxhp', '0123'); 
                if($a == $b){
                    echo $flag;
                }
            }  
            else {
               die("md5 is wrong"); 
            }
            } 
        else {
        die('str1 not number'); 
        }
    }

?>

首先查看一些strtr()函數的用法:

strtr() 函數轉換字符串中特定的字符。

觀察源碼,要求傳入四個參數,首先param1===param2,因為沒有別的限制,所以我們可以傳入兩個數組。對於是str1和str2,首先str1只能是數字,且最后b,但md5_1 != md5_2,所以我們不能傳入兩個md5加密后以0e開頭的字符串。

又因為會將md5加密后的str1和str2中的cxhp替換成0123,也就是說c會被替換成0,所以一個ce開頭的字符串會被替換成0e開頭的字符串。

可以想到只要找到兩個md5加密后是ce開頭的字符串,或者一個md5加密后是ce開頭的字符串,一個md5加密后是0e開頭的字符串就可以繞過過濾。

構造腳本

這是一開始的腳本,返回值少,且執行速度慢。

<?php
for($a=1;$a<=1000000000;$a++){
   $md5 = md5($a);
   if(preg_match('/^ce\d+$/',$md5)){
      echo $a;
      echo "\n";
      echo $md5;
      echo "\n";
   }
}

這是進一步優化的腳本

<?php
for($a = 1; $a <= 100000000; $a++) {
   $md5 = strtr(md5($a),'cxhp', '0123');
   if(preg_match('/^0e\d+$/', $md5)) {
      echo $a;
      echo "\n";
      echo $md5;
      echo "\n";
   }
}
?>

實戰演練

<?php
function random() { 
    $a = rand(133,600)*78;
    $b = rand(18,195);
    return $a+$b;
}
$r = random();
    if((string)$_GET['a']==(string)md5($_GET['b'])){
        if($a.$r == $b) {
            print "Yes,you are right";
        }
        else {
            print "you are wrong";
        }
    }

?>

觀察代碼,有一個rondom方法,返回的是一個隨機數,在這道題中,不需要清楚返回的是什么內容,我們只要知道返回的是一串數字就可以了。傳入兩個參數a和b,要求傳入的是字符串,b會經過md5加密。最后要讓$a.$r == $b。因為是弱類型比較,且只能傳入字符串,想要的是兩個0e開頭的字符串進行比較,前面我們已經知道,以0e開頭的字符串只能是純數字,這樣php在進行科學計算法的時候才會將它轉化為0。所以保證$a以0e開頭就可以了,因為$r是一串數字,所以$a.$r在php中還是可以被解析為0。因為$b是參數b經過md5加密而來,所以我們傳入md5加密后是0e開頭的字符串即可。

 

 

點擊上方鏈接做靶場練習。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM