MRCTF2020——Ezaudit题思考,WP:https://www.yuque.com/lola39/ax672a/nrlpcx


前言

PHP 的 mt_rand 函数作为一个随机数生成工具在程序中被广泛使用,但是 mt_rand 生成的随机数不是一个真正的随机数,而是一个伪随机数,不能应用于生成安全令牌、核心加解密key等等,所以很多知名程序都出现过对 mt_rand 函数的错误使用而导致的安全问题。php_mt_seed 是一个破解 mt_rand 函数种子的工具,对它应用场景的深刻理解和应用能极大的提升漏洞发现的可能和利用的成功率。

提一下php官网manual的一个坑,看下关于mt_rand() 的介绍:中文版1、英文版2,可以看到英文版多了一块黄色的

Caution警告:

This function does not generate cryptographically secure values, and should not be used for cryptographic purposes. If you need a cryptographically secure value, consider using random_int(), random_bytes(), or openssl_random_pseudo_bytes() instead.

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

函数简介

PHP 的 mt_rand 函数作为一个随机数生成工具在程序中被广泛使用,该函数用了 Mersenne Twister 算法的特性作为随机数发生器,它产生随机数值的平均速度比 libc 提供的 rand() 快四倍。mt_rand 函数有两个可选参数 min 和 max,如果没有提供可选参数,mt_rand 函数将返回返回 0 到 mt_getrandmax() 之间的伪随机数。例如想要 5 到 15(包括 5 和 15)之间的随机数,用 mt_rand(5, 15)。

mt_rand(void) : int

mt_rand(int $min, int $max) : int

常用的使用方式如下:

<?php
echo mt_rand() . "\n";
echo mt_rand() . "\n";
echo mt_rand(5, 15);
?>

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

1604716014
1478613278
6

提到了 mt_rand() 就要说一下 mt_srand() 函数:

mt_srand([int $seed]) : void

用 seed 来给随机数发生器播种。 没有设定 seed 参数时,会被设为随时数。使用者在进行一次 mt_srand() 操作后,seed 数值将被固定下来,给接下来的 mt_rand() 函数使用。

mt_rand() 和 mt_srand() 比 rand() 和 srand() “更好更快”

自 PHP 4.2.0 起,不再需要用 srand() 或 mt_srand() 给随机数发生器播种 ,因为现在是由系统自动完成的。

伪随机数

伪随机数是用确定性的算法计算出来的随机数序列,它并不真正的随机,但具有类似于随机数的统计特征,如均匀性、独立性等。在计算伪随机数时,若使用的初值(种子)不变,那么伪随机数的数序也不变。伪随机数可以用计算机大量生成,在模拟研究中为了提高模拟效率,一般采用伪随机数代替真正的随机数。模拟中使用的一般是循环周期极长并能通过随机数检验的伪随机数,以保证计算结果的随机性。伪随机数的生成方法有线性同余法、单向散列函数法、密码法等。

简单的利用思路

简单假设一下 mt_rand() 内部生成随机数的函数为:rand = seed+(i*10) 其中seed是随机数种子,i是第几次调用这个随机数函数。当我们同时知道irand两个值的时候,就能很容易的算出seed的值来。比如rand=21,i=2代入函数 21=seed+(2*10)得到seed=1。当我们拿到seed之后,就能计算出当i为任意值时候的rand的值了。所以当你的 seed 数值被他人知道后,就可以预测出你接下来的数值是多少,这就是该函数的一个问题,他并不能起到一个真随机数的作用。

php_mt_seed 的说明

> 简介

php_mt_seed 是一个破解mt_rand函数seed的工具,在最简单的调用模式下,它能通过mt_rand第一次输出的值寻找mt_rand的seed,在更高级的模式中它能匹配不是第一次输出的和不明确具体输出的情况。

mt_rand的算法从PHP 3.0.6开始就一直在变化,php_mt_seed 4.0 支持以下几个大的版本: PHP 3.0.7 to 5.2.0,PHP 5.2.1 to 7.0.x, and PHP 7.1.0+

php_mt_seed基于命令行运行,命令行可以使用1,2,4或者更多的参数。这些参数需要详细说明mt_rand()的输出。

> 参数模式

  1.  一个参数

当只有一个参数的时候,这个参数代表mt_rand第一次输出的值。

  1.  两个参数

当有两个参数的时候,他们代表mt_rand第一次输出应该位于什么区间内。

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

  1.  四个参数(高级模式)

前两个参数表示mt_rand第一次输出的区间,后两个参数表示mt_rand输出的区间。

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

每四个参数一组,但是最后一组可以是1,2或4个参数。每一组引用对应的输出。

> 下载及使用方法

php_mt_seed 各个版本下载地址:https://www.openwall.com/php_mt_seed/

将压缩包扔到 kali 里解压后在该目录下打开终端输入 make 即可安装

使用指令 ./php_mt_seed xxxxx

php_mt_seed 应用场景

> 直接使用 mt_rand 生成的随机数

图片.png
图片.png

          seed.php                                              三个随机出的用户密码(PHP Version 5.4.45)

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

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

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

./php_mt_seed 1412203388
图片.png

执行结果 seed 为 2078089285

用一段 PHP 代码运行测试一下:

图片.png

手工播种

图片.png

运行结果

可以看到完全解密了其它两个用户的密码。

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

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

图片.png

随机代码 

图片.png

输出结果

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

我们要写一个程序,先是把字母还原成为生成的随机数,然后在拼接成 php_mt_seed 需要的参数。代码如下:

图片.png

破解seed代码

图片.png

运行结果

图片.png

用 php_mt_seed 爆破出 seed=1585925527

图片.png

带入seed值

图片.png

运行结果

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

> Discuz X3.3 authkey 生成算法的安全性漏洞及后台任意代码执行漏洞

漏洞详情链接:https://www.seebug.org/vuldb/ssvid-96371

Discuz官方于2017年8月1号发布最新版X3.4版本,在最新版本中修复了多个安全问题。

用户在初次安装软件时,系统会自动生成一个 authkey 写入全局配置文件和数据库,之后安装文件会被删除。该 authkey 用于对普通用户的 cookie 进行加密等密码学操作,但是由于生成算法过于简单,可以利用公开信息进行本地爆破。

在Discuz_X3.3_SC_UTF8\upload\install\index.php中 authkey 的生成方法如下:

<?php
$authkey = substr(md5($_SERVER['SERVER_ADDR'].$_SERVER['HTTP_USER_AGENT'].$dbhost.$dbuser.$dbpw.$dbname.$username.$password.$pconnect.substr($timestamp, 0, 6)), 8, 6).random(10);
?>

可以看出 authkey 主要由两部分组成:

MD5 的一部分(前6位) + random 生成的10位

爆破 authkey 的流程:

  1. 通过cookie前缀爆破随机数的seed。使用php_mt_seed工具。
  2. 用seed生成random(10),得到所有可能的authkey后缀。
  3. 给自己的账号发送一封找回密码邮件,取出找回密码链接。
  4. 用生成的后缀爆破前6位,范围是0x000000-0xffffff,和找回密码url拼接后做MD5求出sign。
  5. 将求出的sign和找回密码链接中的sign对比,相等即停止,获取当前的authkey。


本网站博主