第一题


第二题

image-20260506220131126

首先看过改代码后,发现这只是一个简单的MD5问题,对于该问题它只有俩个量·,一个a一个b,然后下面说a不等于b,但是a和b的MD5值要相等,在MD5的比较中,都是用0e进行绕过,所以总共俩种方法进行绕过

第一种是通过科学计数法进行绕过,一般方法就是构造俩个MD5都为0e的字符串,下面我已经找到俩个常用的,平时有网的话可以用AI进行找到。最后用URL即可得到flag。

第二种方法通过数组进行绕过,这是一个简单暴力的方法。由于MD5()函数无法处理数组,如果传入一个数组的话,MD5()会返回null并警告,所以可以构造URL:?a[]=1&b[]=2 (这里a是数组[1],b是数组[2],他们本身不相同,但是·MD5的结果都是null,所以最后是相等的)

a = QNKCDZO (MD5: 0e830400451993494058024219903391) b = s878926199a (MD5: 0e545993274517709034328855841020)

image-20260506221117490

第三题

image-20260508204957216

这个题没什么好说的,根据代码判断,其a和b依旧相等,MD5的值也要相等,但是题目中限制了不能通过0e的方式绕过,我在题目二中讲出了可以使用数组绕过,所以直接用数组绕过就行了。 如果不用数组的话只能使用MD5的强碰撞.

通常我们不会手工计算(这需要极大的计算量),而是利用现成的工具。最常用的是 Fastcoll。

  1. 准备一个原始文件(比如一个简单的文本文件 prefix.txt)。

  2. 运行工具:

    1
    fastcoll -p prefix.txt -o msg1.bin msg2.bin

    第二步:转换为 URL 编码

    由于强碰撞生成的是二进制数据,直接复制粘贴到浏览器地址栏会损坏数据。你需要将这些二进制数据转换成 URL 编码(%形式)。

    你可以使用 Python 来快速获取编码后的 Payload:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    import urllib.parse

    # 假设你已经生成了两个文件
    with open("msg1.bin", "rb") as f:
    a = f.read()
    with open("msg2.bin", "rb") as f:
    b = f.read()

    print("a=" + urllib.parse.quote(a))
    print("b=" + urllib.parse.quote(b))

    如果你决定硬碰硬,不使用数组绕过,而是利用MD5 强碰撞(Strong Collision),你的目标是找到两个内容不同但 MD5 值完全一致的二进制字符串。

    既然代码限制了 MD5 值不能以 0e 开头,强碰撞是完美的解决方案,因为强碰撞产生的 MD5 值通常是随机的十六进制字符,极大概率不会以 0e 开头。

    1. 核心原理

    MD5 强碰撞是指找到两段不同的数据 $M_1$ 和 $M_2$,使得 $MD5(M_1) = MD5(M_2)$。

    在 PHP 中:

    • $a != $b 条件成立(因为数据内容不同)。
    • $md5_a == $md5_b 条件成立(因为哈希值完全一样)。

    2. 具体操作步骤

    第一步:生成碰撞对

    通常我们不会手工计算(这需要极大的计算量),而是利用现成的工具。最常用的是 Fastcoll。

    1. 准备一个原始文件(比如一个简单的文本文件 prefix.txt)。

    2. 运行工具:

      1
      fastcoll -p prefix.txt -o msg1.bin msg2.bin
    3. 这会生成两个文件 msg1.bin 和 msg2.bin,它们的内容在前一部分是一样的,但在末尾会有几处微小的位差异,而它们的 MD5 哈希值将完全相同。

    第二步:转换为 URL 编码

    由于强碰撞生成的是二进制数据,直接复制粘贴到浏览器地址栏会损坏数据。你需要将这些二进制数据转换成 URL 编码(%形式)。

    你可以使用 Python 来快速获取编码后的 Payload:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    import urllib.parse

    # 假设你已经生成了两个文件
    with open("msg1.bin", "rb") as f:
    a = f.read()
    with open("msg2.bin", "rb") as f:
    b = f.read()

    print("a=" + urllib.parse.quote(a))
    print("b=" + urllib.parse.quote(b))

    第三步:提交 Payload

    将生成的两段长字符串拼接在 URL 后面:

    1
    http://.../?a=[编码后的a]&b=[编码后的b]

参数 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%43%36%58%e1%ff%7b%30%ac%27%ca%af%cc%de%db%a1%d8%50%ed%bf%72%b8%ad%70%fa%f2%10%70%3b%1f%46%a3%3c%d0%00%11%af%02%1b%a5%9a%43%f1

参数 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%43%36%58%e1%ff%7b%30%ac%27%ca%af%cc%de%db%a1%d8%50%ed%bf%72%b8%ad%70%fa%f2%12%70%3b%1f%46%a3%3c%d0%00%11%af%02%1b%a5%9a%43%f1

验证:

  • 这两段数据的 MD5 值均为:008ee33a9d58b51cfeb425b0959121c9
  • 该哈希值不以 0e 开头,满足代码中 substr(..., 0, 2) !== '0e' 的限制。
  • 由于 MD5 完全相同,== 比较必然通过。

image-20260508205351814

第四题

image-20260508210354406

该题分析后得知a和b的值不同,但是MD5的值严格相等,所以还是用数组直接拿到flag。1

image-20260508210906708

第五题

image-20260508211249724

本题并没有向上面题一样使用MD5弱类型的比较,而是让我们去找QC它的MD5的值最后六位为d54e23的字符串,我们可以通过ai或者自己编写一个python脚本找到这么一个值。输入拿到flag。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import hashlib

# 目标后缀
target = "d54e23"

# 开始爆破(尝试从 1 开始的数字,直到找到匹配的)
for i in range(100000000):
# 将数字转为字符串并计算 md5
s = str(i)
md5_hash = hashlib.md5(s.encode()).hexdigest()

# 检查最后 6 位是否匹配
if md5_hash[-6:] == target:
print(f"找到匹配字符串: {s}")
print(f"对应 MD5: {md5_hash}")
break

image-20260508211802402

第六题

image-20260508212903783

通过对代码的分析

1
$admin_hash = '0e830400451993494058024219903391';

看到该代码hash后面为0e+一堆数字,并且只有一个等号,php代码中这个值就为0了,qc的值为0,即可得到true,所以只要找一个字符串,下面为几个常用的字符串,代入找到flag。

原始字符串 (QC) MD5 结果 (hp)
QNKCDZO 0e830400451993494058024219903391
s878926199a 0e545993274517709034328855841020
s155964671a 0e342768416822451524974117254469

image-20260508212925151

第七题

image-20260508213940202
这里涉及到一个新的函数sha1() 是 PHP 中的一个内置函数,全称是 Secure Hash Algorithm 1(安全哈希算法 1)。

它的作用和 md5() 非常相似:将任意长度的字符串“粉碎”并转化成一个固定长度(40位十六进制字符)的哈希值(指纹)。

SHA-1 的特点

  • 输出长度:MD5 输出 32 位,而 SHA-1 输出 40 位。
  • 不可逆性:你不能从哈希值反推出原始数据。
  • 碰撞性:虽然理论上不同输入对应不同输出,但由于输出长度有限,存在“碰撞”的可能(即两个不同输入产生相同输出)。

该题可以直接用数组绕过,但是它涉及了sha1我们可以找到其中的值,下面是一组常用的payload,当然也可以用脚本找到值,或者让AI帮你,喵喵喵。

常用 Payload:

  • a = aaroZmOk (SHA-1: 0e66507019969427134894567494305185566735)
  • b = aaK1STpo (SHA-1: 0e76658526655756207688271159624026011393)

image-20260508214229138