md5相等及碰撞绕过

本文探讨了CTF竞赛中常见的MD5碰撞技巧,特别是在PHP弱类型环境下实现v1!=v2但md5(v1)==md5(v2)的情况。通过实例展示了如何利用特殊字符串使MD5值呈现特定形式,从而绕过安全检查。同时,提供了双MD5碰撞的脚本及方法,介绍了如何快速找到MD5值以0e开头的字符串。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在ctf中经常会遇到要求v1!=v2,但md5(v1)==md5(v2)的情况,所以上网搜集了一下资料,学习了一下大佬的思路,拓展思路。

在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,成功绕过

拓展 双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

拓展2 双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

拓展3 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开头的字符串。

sha碰撞,MD5碰撞实现,#!/usr/local/bin/perl # It was noted that Intel IA-32 C compiler generates code which # performs ~30% *faster* on P4 CPU than original *hand-coded* # SHA1 assembler implementation. To address this problem (and # prove that humans are still better than machines:-), the # original code was overhauled, which resulted in following # performance changes: # # compared with original compared with Intel cc # assembler impl. generated code # Pentium -25% +37% # PIII/AMD +8% +16% # P4 +85%(!) +45% # # As you can see Pentium came out as looser:-( Yet I reckoned that # improvement on P4 outweights the loss and incorporate this # re-tuned code to 0.9.7 and later. # ---------------------------------------------------------------- # Those who for any particular reason absolutely must score on # Pentium can replace this module with one from 0.9.6 distribution. # This "offer" shall be revoked the moment programming interface to # this module is changed, in which case this paragraph should be # removed. # ---------------------------------------------------------------- # $normal=0; push(@INC,"perlasm","../../perlasm"); require "x86asm.pl"; &asm_init($ARGV[0],"sha1-586.pl",$ARGV[$#ARGV] eq "386"); $A="eax"; $B="ecx"; $C="ebx"; $D="edx"; $E="edi"; $T="esi"; $tmp1="ebp"; $off=9*4; @K=(0x5a827999,0x6ed9eba1,0x8f1bbcdc,0xca62c1d6); &sha1_block_data("sha1_block_asm_data_order"); &asm_finish(); sub Nn { local($p)=@_; local(%n)=($A,$T,$B,$A,$C,$B,$D,$C,$E,$D,$T,$E); return($n{$p}); } sub Np { local($p)=@_; local(%n)=($A,$T,$B,$A,$C,$B,$D,$C,$E,$D,$T,$E); local(%n)=($A,$B,$B,$C,$C,$D,$D,$E,$E,$T,$T,$A); return($n{$p}); } sub Na { local($n)=@_; return( (($n )&0x0f), (($n+ 2)&0x0f), (($n+ 8)&0x0f), (($n+13)&0x0f), (($n+ 1)&0x0f)); } sub X_expand { local($in)=@_; &comment("First, load the words onto the stack in network byte order"); for ($i=0; $i<16; $i+=2) { &mov($A,&DWP(($i+0)*4,$in,"",0));# unless $i == 0; &mov($B,&DWP(($i+1)*4,$in,"
### 关于Java中绕过MD5严格相等比较的探讨 在Java中,`equals()`方法通常用于对象之间的比较。如果类定义了一个自定义的`compareTo(...)`方法而未重写继承自`Object`类的`equals()`方法,则可能会引发不可预测的行为[^1]。然而,在讨论如何绕过MD5哈希值的严格相等比较之前,需明确几个关键概念。 #### MD5哈希算法的特点 MD5是一种广泛使用的加密散列函数,它将任意长度的数据映射到固定大小的128位(16字节)输出。由于其设计特性,即使输入数据有微小变化也会导致完全不同的输出。因此,直接通过修改输入来获得相同的MD5哈希值是非常困难的。 尽管如此,存在一些间接方式可以实现所谓的“绕过”。以下是几种可能的技术: --- ### 方法一:利用碰撞攻击 理论上可以通过找到两个不同输入产生相同MD5摘要的方式来进行碰撞攻击。虽然现代计算能力使得针对短消息生成实际碰撞变得可行,但在大多数情况下这仍然非常耗时且复杂。需要注意的是,这种方法并不改变原始数据本身,而是创建另一个具有相同哈希的新实体。 ```java // 假设已知某些特定条件下能够制造冲突的例子 String originalMessage = "example"; String collidingMessage = generateCollision(originalMessage); // 需要专门工具或库支持 if (md5Hash(originalMessage).equals(md5Hash(collidingMessage))) { System.out.println("Collision detected!"); } ``` 上述代码片段展示了当发现一对相互匹配的消息时的情景。不过请注意,这种做法违反了许多安全协议的规定,并不推荐应用于合法场景之外的地方[^4]。 --- ### 方法二:调整逻辑判断条件 另一种更实用也更为道德的选择是重新审视应用程序中的业务需求,看看是否存在其他替代方案满足功能目标而不必依赖严格的字符串对比操作。例如,允许一定范围内的误差或者采用模糊匹配技术代替精确一致性的检验标准。 考虑下面这个例子,其中我们引入了时间戳作为额外参数参与最终结果判定过程的一部分: ```java public boolean compareWithTimestamp(String inputA, String inputB) { long timestampDifferenceInSeconds = Math.abs(getCreationTime(inputA) - getCreationTime(inputB)); return md5Hash(inputA).equalsIgnoreCase(md5Hash(inputB)) || (timestampDifferenceInSeconds <= ALLOWED_TIME_DRIFT); } private static final int ALLOWED_TIME_DRIFT = 30; // 秒数差异容忍度设定为半分钟以内 ``` 在这里,除了简单的hash值对照外还加入了基于时间差别的考量因素,从而增加了灵活性并减少了误报的可能性。 --- ### 方法三:使用弱化版验证机制 对于那些确实无法承受性能开销却又希望维持一定程度安全保障的应用场合来说,可以选择实施简化形式的身份认证流程。比如仅保留部分字符序列进行快速筛查而非整个digest全部逐一核验。 ```java public boolean fastCompareMd5Sum(String str1, String str2){ byte[] digest1Bytes = computeDigest(str1.getBytes()); byte[] digest2Bytes = computeDigest(str2.getBytes()); // 只取前四个字节做简单比对 for(int i=0;i<Math.min(4,digest1Bytes.length && digest2Bytes.length);i++) { if(digest1Bytes[i]!=digest2Bytes[i])return false; } return true; } ``` 此策略牺牲了一定程度上的准确性换取更快的速度表现,适用于低敏感度环境下的初步筛选作业。 --- ### 注意事项 无论采取何种措施都应当谨慎行事,确保不会损害系统的整体安全性架构。同时也要意识到任何规避正常校验手段的做法都有潜在风险伴随其间,务必权衡利弊后再作决定。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值