深入解析PHP mtrand函数的安全隐患及phpmt_seed工具的应用场景

序言

深入解析PHP mtrand函数的安全隐患及phpmt_seed工具的应用场景

PHP 的 mtrand 函数在程序中被广泛当作随机数生成工具使用。然而,有一个事实被大家所忽略,那就是 mtrand 生成的随机数并非真正的随机数,而是伪随机数。由于它不能应用于生成安全令牌、核心加解密 key 等,所以许多知名程序都曾因对 mtrand 函数的错误使用而引发了安全问题。phpmtseed 是用于破解 mtrand 函数种子的工具。深刻理解它的应用场景,能够极大提升漏洞发现的可能性以及利用的成功率。本文会详细介绍 PHP mtrand 函数的安全问题以及 phpmt_seed 的应用场景。

PHP mt_rand函数简介

PHP 的 mtrand 函数在程序中被广泛用作随机数生成工具。此函数利用 Mersenne Twister 算法的特性作为随机数发生器。它产生随机数值的平均速度比 libc 提供的 rand()快四倍。mtrand 函数存在两个可选参数,分别是 min 和 max。若未提供可选参数,mtrand 函数会返回 0 到 mtgetrandmax()之间的伪随机数。比如,若想要获取 5 到 15(包含 5 和 15)之间的随机数,可使用 mt_rand(5, 15)。

常用的使用方式如下:

以上程序的输出结果如下:

160471601414786132786

伪随机数

伪随机数能够用计算机大量生成,在模拟研究中,为提升模拟效率,通常会采用伪随机数来替代真正的随机数。模拟中通常使用循环周期极长且能通过随机数检验的伪随机数,目的是确保计算结果具有随机性。伪随机数的生成方式包含线性同余法、单向散列函数法以及密码法等。

PHP mt_rand引起的安全问题

mt_rand 是一个伪随机数生成函数。它是由可确定的函数,借助一个种子而产生的伪随机数。这表明:倘若知晓了种子,或者已经生成的随机数,便有可能获取后续随机数序列的信息(具有可预测性)。

简单假设一下,mt_rand()内部生成随机数的函数是:rand 等于 seed 加上(i10)。这里的 seed 是随机数种子,i 则是第几次调用这个随机数函数。当我们既知道 i 的值,又知道 rand 的值时,就能够很轻易地算出 seed 的值。rand 等于 21 ,i 等于 2 ,将其代入函数 21 等于 seed 加上 210 ,由此可得出 seed 等于 1 。这是很简单的,当我们获得 seed 之后,就能够计算出当 i 为任意值时 rand 的值了。

我们已知随机数的生成依赖特定的函数,之前曾假设为 rand = seed+(i*10) 。对于这样一个简单的函数,我们能够直接计算出一个解。然而,mtrand实际使用的函数极为复杂且无法进行逆运算。有效的破解方法是穷举所有的种子,然后根据种子生成随机数序列,再将其与已知的随机数序列进行比对,以验证种子是否正确。phpmtseed 就是这样一个工具,它的速度很快。它能够依据单次 mtrand()的输出结果,直接破解出可能的种子,同时也可以破解像 mt_rand(1,100)这样限定了 MIN 和 MAX 输出的种子。

最后要提及 php 官网 manual 的一个情况,mt_rand 的中文版和英文版介绍存在差异,能够看到英文版中多了一块呈黄色的 Caution 警告:

很多国内开发者可能是通过看中文版的介绍,从而在程序中使用了 mt_rand()来生成安全令牌、核心加解密 key 等,进而导致了严重的安全问题。

这里我们要注意,每一次 mtrand 被调用时,会依据 seed 和当前调用的次数 i 计算出一个伪随机数。同时,seed 是自动播种的,自 PHP 4.2.0 起,不再需要用 srand() 或 mtsrand() 给随机数发生器播种,因为现在是由系统自动完成这一操作。那么问题随之而来,系统自动完成播种的具体时间是什么呢?倘若每次调用 mtrand()都会自动播种,那么破解 seed 就没有意义了。关于这一点,manual 并未给出详细的信息。通过对源代码进行分析能够发现,每次调用 mtrand()时,都会先对是否已经播种进行检查。如果已经进行了播种操作,那么就会直接生成随机数;倘若没有播种操作,就会调用 phpmtsrand 来进行播种。这意味着在每个 php cgi 进程期间,只有在第一次调用 mtrand()时才会自动进行播种。在此之后,都会依据这第一次播种所产生的种子来生成随机数。在 php 的几种运行模式当中,除了 CGI 模式(即每个请求启动一个 cgi 进程,在请求结束后关闭)之外。除了每次都要重新读取 php.ini 环境变量等会导致效率低下且现在用的应该不多这种情况以外,基本都是一个进程先处理完请求后处于 standby 等待下一个请求,只有处理多个请求之后才会回收,超时的话也会回收。总的来讲,就是 php 的自动播种是在 php cgi 进程中第一次调用 mtrand()的时候发生的。只要是同一个进程处理的请求,无论访问的页面是否相关,都会共享同一个最初自动播种的种子。

下面我们来看一下mtrand的克星phpmt_srand。

phpmtseed简介及使用说明

phpmtseed 是一个用于破解 mtrand 函数 seed 的工具。在最简单的调用模式下,它能够凭借 mtrand 第一次输出的值去探寻 mt_rand 的 seed。在更高级的模式里,它可以匹配并非第一次输出的情况以及不明确具体输出的情况。

mtrand 的算法从 PHP 3.0.6 起一直在发生变化。phpmt_seed 4.0 支持以下这些大的版本范围:PHP 3.0.7 到 5.2.0;PHP 5.2.1 到 7.0.x;以及 PHP 7.1.0 及之后的版本。

phpmtseed 是通过命令行来运行的。在命令行中,可以使用 1 个参数,也可以使用 2 个参数,还可以使用 4 个参数或者更多的参数。这些参数需要对 mt_rand()的输出进行详细的说明。

一个参数

当仅有一个参数时,此参数意味着 mt_rand 首次输出的值。

两个参数

当存在两个参数时,这两个参数代表着 mt_rand 第一次输出应当处于的区间范围。

第一个参数为最小值,第二个参数为最大值。

四个参数(高级模式)

第一个参数和第二个参数表明了 mtrand 首次输出的区间;第三个参数和第四个参数则表示 mtrand 输出的区间。

多于五个参数(高级模式)

四个参数为一组,然而最后一组参数的数量可以是 1 个、2 个或 4 个。每一组都引用相应的输出。

phpmtseed 的应用场景直接采用 mt_rand 所生成的随机数。

假设下面的代码为用户密码的随机生成代码:

运行后我们可以得到三个用户的密码

假设我们现在得到了第一个用户的密码:1412203388

通过这个密码我们可以猜测出后面两个用户的密码。

下面我们运行phpmtseed找出seed,命令如下:

./php_mt_seed.exe 1412203388

运行截图如下:

我用于测试的服务器的 PHP 版本是 5.4.45,在这种情况下,seed 有可能是 2078089285,接下来我将写一段 PHP 代码来进行测试。

运行结果如下:

完全解密了其它两个用户的密码。

使用经过转换后的mt_rand随机序列

下面是我们更常见到的生成随机数的代码:

运行后我们可以得到三个用户的密码:

假设我们现在得到了第一个用户的密码:paJHuuKv3H

我们要写一个程序,首先将字母还原为生成的随机数,接着把这些随机数拼接成 phpmtseed 需要的参数。代码如下:

运行结果如下:

执行如下命令:

./php_mt_seed.exe 14 14 0 56 0 0 0 56 33 33 0 56 32 32 0 56 19 19 0 56 19 19 0 56 34 34 0 56 20 20 0 56 50 50 0 56 32 32 0 56 ,此为一个特定的执行命令,包含了一系列数字参数,分别作用于不同的操作或程序设置 。

运行结果如下:

得到seed为1544796235,写解密代码如下:

运行截图如下:

完全解密了其它两个用户的密码。

下面列出一个实际的例子:

Discuz X3.3 authkey生成算法的安全性漏洞

你可能想看:
« 上一篇
下一篇 »

相关推荐

php实时推送,php实地培训

2026年10月05日

53阅读

php乱码出现问号的原因,php乱码出现问号的原因有哪些

2026年10月05日

44阅读

php一句话木马,一句话木马ctf

2026年10月05日

48阅读

php获取数组长度,php中如何获取数组的长度

2026年10月05日

50阅读

php面试题库及答案,php程序员面试真题库

2026年10月05日

47阅读

phpcontent-type修改,php源码二次修改需要多少钱

2026年10月05日

42阅读