飞道的博客

浅谈md5弱类型比较和强碰撞

290人阅读  评论(0)

知识点实操概要

CTF多种类型题目一应俱全

链接指路:

https://www.hetianlab.com/cour.do?w=1&c=CCID2d51-5e95-4c58-8fc9-13b1659c1356&pk_campaign=csdn-wemedia    

复制上方链接马上体验

前言

在CTF中,md5的题目太常见了,虽然有很多这方面的文章,但相对来说比较零散,这里主要将自己学习和比赛时遇到的md5弱类型和强碰撞的题目从浅到深地梳理一下。

基本知识

php中有两种比较的符号==与=== ==在进行比较的时候,如果比较一个数字和字符串或者比较涉及到数字内容的字符串,则字符串会被转换为数值并且比较按照数值来进行。

===在进行比较的时候,会先判断两种字符串的类型是否相等,再比较。

0e开头且都是数字的字符串,弱类型比较都等于0。

==比较

测试代码


   
  1.  <?php
  2. if (isset($_POST[ 'a']) and isset($_POST[ 'b']))
  3. {
  4.      if ($_POST[ 'a'] != $_POST[ 'b'])
  5.     {
  6.       if (md5($_POST[ 'a']) == md5($_POST[ 'b']))
  7.       echo  'flag';
  8.       else
  9.       echo  'you are wrong';
  10.  }
  11.   else echo  "请输入不同的a,b值";
  12. }

解法1

由于md5不能加密数组,在加密数组的时候会返回NULL

所以,我们可以传入两个数组

解放2

可以传入两个md5加密后是0e开头的字符串,需要注意的地方是,这个以0e开头的字符串只能是纯数字,这样php在进行科学计算法的时候才会将它转化为0。可以查找以0e开头md5加密相等的字符串,也可以自己编写代码,提供以下脚本。


   
  1. <?php
  2. for($a= 1;$a<= 1000000000;$a++){
  3.    $md5 = md5($a);
  4.     if(preg_match( '/^0e\d+$/',$md5)){
  5.       echo $a;
  6.       echo  "\n";
  7.       echo $md5;
  8.       echo  "\n";
  9.    }
  10. }


   
  1. s1502113478a
  2. 0e861580163291561247404381396064
  3.   
  4. s1885207154a
  5. 0e509367213418206700842008763514
  6.   
  7. s1836677006a
  8. 0e481036490867661113260034900752
  9.   
  10. s155964671a
  11. 0e342768416822451524974117254469
  12.   
  13. s1184209335a
  14. 0e072485820392773389523109082030
  15.   

===比较


   
  1. <?php
  2. if (isset($_POST[ 'a']) and isset($_POST[ 'b']))
  3. {
  4.      if ($_POST[ 'a'] != $_POST[ 'b'])
  5.     {
  6.       if (md5($_POST[ 'a']) === md5($_POST[ 'b']))
  7.       echo  'flag';
  8.       else
  9.       echo  'you are wrong';
  10.  }
  11.   else echo  "请输入不同的a,b值";
  12. }
  13. ?>

解法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文件进行测试


   
  1. <?php 
  2. function  readmyfile($path){
  3.     $fh = fopen($path,  "rb");
  4.     $data = fread($fh, filesize($path));
  5.     fclose($fh);
  6.      return $data;
  7. }
  8. echo  '二进制md5加密 '. md5( (readmyfile( "1.txt")));
  9. echo  "</br>";
  10. echo   'url编码 '. urlencode(readmyfile( "1.txt"));
  11. echo  "</br>";
  12. echo  '二进制md5加密 '.md5( (readmyfile( "2.txt")));
  13. echo  "</br>";
  14. echo   'url编码 '.  urlencode(readmyfile( "2.txt"));
  15. echo  "</br>";

   
  1. 二进制md5加密  8e4ef6c69a337c0de0208455ee69a416
  2. 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
  3. 二进制md5加密  8e4ef6c69a337c0de0208455ee69a416
  4. 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.


   
  1. <?php
  2. if(( string)$_GET[ 'a'] !== ( string)$_GET[ 'b'] && md5($_GET[ 'a'])===md5($_GET[ 'b'])){
  3.  echo  "you are right";
  4. }
  5. else {
  6.  echo  "you are wrong";
  7. }

HECTF  ezphp

源码


   
  1. <?php 
  2. error_reporting( 0);
  3. highlight_file(__file__);
  4. include( 'flag.php'); 
  5. $string_1 = $_GET[ 'str1']; 
  6. $string_2 = $_GET[ 'str2']; 
  7. if($_GET[ 'param1']!==$_GET[ 'param2']&&md5($_GET[ 'param1'])===md5($_GET[ 'param2'])){
  8.          if(is_numeric($string_1)){ 
  9.             $md5_1 = md5($string_1); 
  10.             $md5_2 = md5($string_2); 
  11.              if($md5_1 != $md5_2){ 
  12.                 $a = strtr($md5_1,  'cxhp''0123'); 
  13.                 $b = strtr($md5_2,  'cxhp''0123'); 
  14.                  if($a == $b){
  15.                     echo $flag;
  16.                 }
  17.             }  
  18.              else {
  19.                die( "md5 is wrong"); 
  20.             }
  21.             } 
  22.          else {
  23.         die( 'str1 not number'); 
  24.         }
  25.     }
  26. ?>

首先查看一些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开头的字符串就可以绕过过滤。

构造脚本

这是一开始的脚本,返回值少,且执行速度慢。


   
  1. <?php
  2. for($a= 1;$a<= 1000000000;$a++){
  3.    $md5 = md5($a);
  4.     if(preg_match( '/^ce\d+$/',$md5)){
  5.       echo $a;
  6.       echo  "\n";
  7.       echo $md5;
  8.       echo  "\n";
  9.    }
  10. }
 

这是进一步优化的脚本


   
  1. <?php
  2. for($a =  1; $a <=  100000000; $a++) {
  3.    $md5 = strtr(md5($a), 'cxhp''0123');
  4.     if(preg_match( '/^0e\d+$/', $md5)) {
  5.       echo $a;
  6.       echo  "\n";
  7.       echo $md5;
  8.       echo  "\n";
  9.    }
  10. }
  11. ?>

实战演练


   
  1. <?php
  2. function random() { 
  3.     $a = rand( 133, 600)* 78;
  4.     $b = rand( 18, 195);
  5.      return $a+$b;
  6. }
  7. $r = random();
  8.      if(( string)$_GET[ 'a']==( string)md5($_GET[ 'b'])){
  9.          if($a.$r == $b) {
  10.              print  "Yes,you are right";
  11.         }
  12.          else {
  13.              print  "you are wrong";
  14.         }
  15.     }
  16. ?>

观察代码,有一个rondom方法,返回的是一个随机数,在这道题中,不需要清楚返回的是什么内容,我们只要知道返回的是一串数字就可以了。传入两个参数a和b,要求传入的是字符串,b会经过md5加密。最后要让$a.$r == $b。因为是弱类型比较,且只能传入字符串,想要的是两个0e开头的字符串进行比较,前面我们已经知道,以0e开头的字符串只能是纯数字,这样php在进行科学计算法的时候才会将它转化为0。所以保证$a以0e开头就可以了,因为$r是一串数字,所以$a.$r在php中还是可以被解析为0。因为$b是参数b经过md5加密而来,所以我们传入md5加密后是0e开头的字符串即可。

 


转载:https://blog.csdn.net/qq_38154820/article/details/113750318
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场