zoukankan      html  css  js  c++  java
  • 浅谈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('/^0ed+$/',$md5)){
          echo $a;
          echo "
    ";
          echo $md5;
          echo "
    ";
       }
    }
    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('/^ced+$/',$md5)){
          echo $a;
          echo "
    ";
          echo $md5;
          echo "
    ";
       }
    }

    这是进一步优化的脚本

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

    实战演练

    <?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开头的字符串即可。

    点击上方链接做靶场练习。

     

    合天智汇:合天网络靶场、网安实战虚拟环境
  • 相关阅读:
    假期周总结报告(五)
    java 原码反码及补码 总结
    java ==与equals()方法的总结
    java String 类型总结
    java 全局变量 的小结
    java static语句的总结
    ATM开学测试(未完成)
    第七周学习进度总结
    第六周学习进度总结
    第五周学习进度总结
  • 原文地址:https://www.cnblogs.com/hetianlab/p/14385563.html
Copyright © 2011-2022 走看看