前言:

在CTF中可以说是经常碰到md5加密了,一般都是进行强比较抑或是弱比较,考法非常多,但是万变不离其中。只要我们掌握了原理,一切问题便迎刃而解了。

简单了解MD5:

  • md5是一种加密算法,并且不能防止碰撞破解
  • md5加密是不可逆的,这就意味着有两串不同的字符串``加密出来的内容却是相同的
  • 加密过程简单,碰撞还原字符难

字符串与int类型比较:

PHP规定当进行字符串与数字的弱比较时,会进行如下步骤:先看字符串开头是否为数字,如果为数字,则截止到连续数字的最后一个数字,即"123abc456"=>123如果开头不为数字,则判断为false,即0。因此("aaa123"==0) =>true("123a"==123) =>true

字符串与字符串比较:

正如上面所言:

1
var_dump("123a"=="123");  //False

因为这个是字符串之间进行比较,想要绕过这个弱比较只能用0e的方式。在PHP中”0e”判断为科学计数法,0e123就是0的10123次方

不难推出:0e123456789==0e1 // 因为0的任意次方都为0

不过有一个注意点:

1
2
3
4
5
6
7
"0e123456"=="0e345"  //True
"0e12adfc"=="0e345" //False

0e后面不能含有字母!!!
0e后面不能含有字母!!!
0e后面不能含有字母!!!
否则判断为False

#实例

1
2
3
4
5
6
<?php
if("0e23253"=="0e2345")
{
echo 'yes';
}
?>

输出:yes

CTF的MD5弱比较

在CTF中,会遇到如下的MD5弱比较题目

1.md5($a)==md5($b) & $a != $b

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
include "flag.php";
$md5_1=$_GET['md5_1'];
$md5_2=$_GET['md5_2'];
if(md5($md5_1)==md5($md5_2) & $md5_1 != $md5_2)
{
echo $flag;
}
else
{
echo "try harder";
}
?>

这个时候就要利用0e的形式来解题,找到两个不同字符,md5加密后却都是0e324234的形式。如何寻找这样的字符串?

1.脚本寻找

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# -*- coding: utf-8 -*-
import multiprocessing
import hashlib
import random
import string
import sys

CHARS = string.ascii_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()

用法:

1
2
3
4
输入命令
python md5.py "0e" 0
"0e" =>要跑的字符
0 =>要跑的字符的起始位置

脚本寻找要浪费大概十分钟左右的时间才能找出一个,可以用网上现成的,如果题目要求比较特殊的话,再利用自己的脚本跑

2.百度

MMHUWUV 0e701732711630150438129209816536
MAUXXQC 0e478478466848439040434801845361
IHKFRNS 0e256160682445802696926137988570
GZECLQZ 0e537612333747236407713628225676
GGHMVOE 0e362766013028313274586933780773
GEGHBXL 0e248776895502908863709684713578
EEIZDOI 0e782601363539291779881938479162
DYAXWCA 0e424759758842488633464374063001

CTF的MD5强比较

1.md5($a)===md5($b) & $a != $b

方法一:

数组绕过

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
include "flag.php";
$md5_1=$_GET['md5_1'];
$md5_2=$_GET['md5_2'];
if(md5($md5_1)===md5($md5_2) & $md5_1 != $md5_2)
{
echo $flag;
}
else
{
echo "try harder";
}
?>
1
md5_1[]=1&md5_2[]=2

因为PHP对无法md5加密的东西不加密,结果为NULL,虽然会报错,但是null=null,逻辑关系为True。所以可以输出flag

方法二:

两串不一样的字符,加密结果却相同:

1
2
$a=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%02%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1%D5%5D%83%60%FB_%07%FE%A2
$b=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%00%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1U%5D%83%60%FB_%07%FE%A2