之前在身边有很多学PHP的朋友写一些小程序的时候,很多时候会使用PHP随机数函数rand()和mt_rand()函数去生成随机数

可是,随机数真的随机吗?这篇文章讲从多个实例中探讨随机数,当然,有写作不当的地方,还望斧正!

关于随机函数rand()和mt_rand()

rand()和mt_rand()两个函数皆是PHP中生成随机数的函数,可相比之下,mt_rand()的生成速度缺是rand()的四倍!在没有参数的情况下,两者生成的数值范围也是不一样的。

  1. echo mt_getrandmax()." and ".getrandmax();

可以看到页面上的输出,mt_rand()相比较rand()默认取值的范围更大。而且经过测试,在不同的PHP版本中,rand随机数种子相同的时候,随机数缺不相同!

左边web页面使用的是PHP7.0.12,而右边的Powershell却是使用的PHP5.2.17。从上所述,只有mt_rand()所生成的随机数是无版本差异的!

而这两个生成随机数的时候,是可以通过srand()和mt_srand()设置随机数的种子

  1. <?php
  2. mt_srand(666);
  3. srand(666);
  4. echo "rand 函数在种子是666时产生的随机数序列:<br/>";
  5. for($i=1;$i<5;$i++){
  6. echo rand()."<br/>";
  7. }
  8. echo '<br/>';
  9. echo "mt_rand 函数在种子是666时产生的随机数序列:<br/>";
  10. for($i=1;$i<5;$i++){
  11. echo mt_rand()."<br/>";
  12. }
  13. ?>

运行如下图所示

当你发现,设置的种子为666的时候,不管你刷新多少次,页面中生成的随机数值都不会变!

所以,当种子泄露的时候,随机数的安全就不堪一击了。

可有什么方法能够得到种子?

目前我们唯一能知道的就是随机数是不会变的,而之前有说过mt_srand()生成的随机数范围在0-2147483647之间,我们就可以通过最普通的爆破方式

去获取随机数的种子,这里推荐使用php_mt_seed,项目地址:http://www.openwall.com/php_mt_seed/

这里踩到个坑,生成后的随机数分为PHP7.x版本的和5.x版本,具体原因还不知道,但是在php7.x版本以上,mt_srand()函数定义的种子将不能超过int,也就是2147483647,但可以等于该值

所以,为了更好的研究,下面将会使用PHP5.2.17的版本

首先通过mt_rand()创建一个随机数,然后利用脚本爆破

  1. php -r "echo mt_rand();"

爆破出来的种子所生成的随机数正好是一样的

那么,页面只播种一次,生成的多个随机数也会一样吗

事实证明,一次播种,全页不愁。

竟然已经了解了随机数的相关安全性,下面就放上一些CTF中,或者在某个CMS中出现的有关随机数的审计

审计案例①(mt_rand()安全性)

此案例是出自第三届陕西网络空间安全技术大赛

  1. <?php
  2. error_reporting(0);
  3. function cpassword($pw_length = 10){
  4. $randpwd = "";
  5. for ($i = 0; $i < $pw_length; $i++)
  6. {
  7. $randpwd .= chr(mt_rand(33, 126));
  8. }
  9. return $randpwd;
  10. }
  11.  
  12. session_start();
  13. mt_srand(time());
  14. $pwd=cpassword(10);
  15. if($pwd === $_GET['pwd'])
  16. {
  17. echo "Good job, you get the key is:".$pwd;
  18. }
  19. else{
  20. echo "Wrong!";
  21. }
  22.  
  23. $_SESSION['userLogin']=cpassword(32).rand();
  24. ?>

从给出的代码中可以看到

  1. $pwd=cpassword(10);

程序通过时间戳播种,然后通过cpassword建立一个密码,并验证密码是否正确,否则就输出flag

通过之前研究发现mt_rand()生成的随机数是有固定的,只需要知道mt_srand(time());的结果行了

写个脚本,直接用time函数生成的时间戳设置种子,再发送数据内容过去,就能获取到flag

脚本如下

  1. <?php
  2. function do_get($url, $params) {
  3. $url = "{$url}?" . http_build_query ( $params );
  4. $ch = curl_init ();
  5. curl_setopt ( $ch, CURLOPT_URL, $url );
  6. curl_setopt ( $ch, CURLOPT_RETURNTRANSFER, 1 );
  7. curl_setopt ( $ch, CURLOPT_CUSTOMREQUEST, 'GET' );
  8. curl_setopt ( $ch, CURLOPT_TIMEOUT, 60 );
  9. curl_setopt ( $ch, CURLOPT_POSTFIELDS, $params );
  10. $result = curl_exec ( $ch );
  11. curl_close ( $ch );
  12. return $result;
  13. }
  14.  
  15. function cpassword($pw_length = 10){
  16. $randpwd = "";
  17. for ($i = 0; $i < $pw_length; $i++)
  18. {
  19. $randpwd .= chr(mt_rand(33, 126));
  20. }
  21. return $randpwd;
  22. }
  23.  
  24. mt_srand(time());
  25. $pwd=cpassword(10);
  26. echo 'send:'.$pwd.'<br/>';
  27. $url="http://localhost/index.php";
  28. $params=array('pwd'=>$pwd);
  29. $result=do_get($url,$params);
  30. echo 'result:'.json_encode($result);
  31. ?>

审计案例① POC

发送的密码与服务器所生成的密码是一样的!

假如按照刚才的生成密码的函数

  1. function cpassword($pw_length = 10){
  2. $randpwd = "";
  3. for ($i = 0; $i < $pw_length; $i++)
  4. {
  5. $randpwd .= chr(mt_rand(33, 126));
  6. }
  7. return $randpwd;
  8. }

这次给出生成后的密码,现在怎样才能得到mt_srand()设置的种子

生成的密码:

  1. {|M}2x0:kW

按照程序执行流程,应该先得到每个字符在chr(33)~chr(126)中的位置索引

  1. <?php
  2. $str = "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~";
  3. $ss = "{|M}2x0:kW";
  4. for($i=0;$i<strlen($ss);$i++){
  5. $pos = strpos($str,$ss[$i]);
  6. echo $pos." ".$pos." "."0 ".(strlen($str)-1)." ";
  7. //整理成方便 php_mt_seed 测试的格式
  8. //php_mt_seed VALUE_OR_MATCH_MIN [MATCH_MAX [RANGE_MIN RANGE_MAX]]
  9. }
  10. ?>

PHP code

通过这个脚本换成方便php_mt_seed测试的格式

  1. 90 90 0 93 91 91 0 93 44 44 0 93 92 92 0 93 17 17 0 93 87 87 0 93 15 15 0 93 25 25 0 93 74 74 0 93 54 54 0 93

最终得到种子数1555747516

审计案例②(rand()函数安全性)

  1. <?php
  2. include('config.php');
  3. session_start();
  4.  
  5. if($_SESSION['time'] && time() - $_SESSION['time'] > 60){
  6. session_destroy();
  7. die('timeout');
  8. } else {
  9. $_SESSION['time'] = time();
  10. }
  11.  
  12. echo rand();
  13. if(isset($_GET['go'])){
  14. $_SESSION['rand'] = array();
  15. $i = 5;
  16. $d = '';
  17. while($i--){
  18. $r = (string)rand();
  19. $_SESSION['rand'][] = $r;
  20. $d .= $r;
  21. }
  22. echo md5($d);
  23. }else if(isset($_GET['check'])){
  24. if($_GET['check'] === $_SESSION['rand']){
  25. echo $flag;
  26. } else {
  27. echo 'die';
  28. session_destroy();
  29. }
  30. } else {
  31. show_source(__FILE__);
  32. }
  33. ?>

PHP code

这是出自0ctf的一道赛题

首先我们先了解rand()生成的随机数是有规律可循的

可以参考这篇文章:http://www.sjoerdlangkemper.nl/2016/02/11/cracking-php-rand/

  1. state[i] = state[i-3] + state[i-31]
  2. return state[i] >> 1

下面这个程序形象的预测了rand()的随机数

  1. <?php
  2. $randStr = array();
  3. for($i=0;$i<60;$i++){ //先产生 32个随机数
  4. $randStr[$i]=rand(0,30);
  5. if($i>=31) {
  6. //echo "$randStr[$i]=(".$randStr[$i-31]."+".$randStr[$i-3].") mod 31"."<br/>";
  7. if ($randStr[$i] == $randStr[$i-31]+$randStr[$i-3]){
  8. echo "$randStr[$i]=(".$randStr[$i-31]."+".$randStr[$i-3].") mod 31"."<br/>";
  9. }
  10. }
  11. }
  12. ?>

PHP code

浅谈PHP随机数安全的分析的更多相关文章

  1. 浅谈NLP 文本分类/情感分析 任务中的文本预处理工作

    目录 浅谈NLP 文本分类/情感分析 任务中的文本预处理工作 前言 NLP相关的文本预处理 浅谈NLP 文本分类/情感分析 任务中的文本预处理工作 前言 之所以心血来潮想写这篇博客,是因为最近在关注N ...

  2. 浅谈C#随机数发生器

    我们在做能自动生成试卷的考试系统时,常常需要随机生成一组不重复的题目,在.net Framework中提供了一个专门用来产生随机数的类System.Random. 对于随机数,大家都知道,计算机不 可 ...

  3. 浅谈java性能分析

    浅谈java性能分析,效能分析 在老师强烈的要求下做了效能分析,对上次写过的词频统计的程序进行分析以及改进. 对于效能分析:我个人很浅显的认为就是程序的运行效率,代码的执行效率等等. java做性能测 ...

  4. 浅谈ELK日志分析平台

    作者:珂珂链接:https://zhuanlan.zhihu.com/p/22104361来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. 小编的话 “技术干货”系列文章 ...

  5. 【分析】浅谈C#中Control的Invoke与BeginInvoke在主副线程中的执行顺序和区别(SamWang)

    [分析]浅谈C#中Control的Invoke与BeginInvoke在主副线程中的执行顺序和区别(SamWang) 今天无意中看到有关Invoke和BeginInvoke的一些资料,不太清楚它们之间 ...

  6. 浅谈Unity的渲染优化(1): 性能分析和瓶颈判断(上篇)

    http://www.taidous.com/article-667-1.html 前言 首先,这个系列文章做个大致的介绍,题目"浅谈Unity",因为公司和国内大部分3D手游开发 ...

  7. 浅谈c#的三个高级参数ref out 和Params C#中is与as的区别分析 “登陆”与“登录”有何区别 经典SQL语句大全(绝对的经典)

    浅谈c#的三个高级参数ref out 和Params   c#的三个高级参数ref out 和Params 前言:在我们学习c#基础的时候,我们会学习到c#的三个高级的参数,分别是out .ref 和 ...

  8. 【转】 浅谈Radius协议

    浅谈Radius协议 2013-12-03 16:06 5791人阅读 评论(0) 收藏 举报  分类: Radius协议分析(6)  从事Radius协议开发有段时间了,小弟不怕才疏学浅,卖弄一下, ...

  9. 转:浅谈Radius协议 -来自CSDN:http://blog.csdn.net/wangpengqi/article/details/17097221

    浅谈Radius协议 2013-12-03 16:06 5791人阅读 评论(0) 收藏 举报  分类: Radius协议分析(6)  从事Radius协议开发有段时间了,小弟不怕才疏学浅,卖弄一下, ...

随机推荐

  1. 汇编语言DOSBox软件使用方法

    https://wenku.baidu.com/view/e63b8b46ccbff121dc368305.html

  2. 【概率论】5-6:正态分布(The Normal Distributions Part III)

    title: [概率论]5-6:正态分布(The Normal Distributions Part III) categories: - Mathematic - Probability keywo ...

  3. C#求任意两整数之和

    2019.9.11 作业要求: 求出任意两整数之和 解决方案: using System; using System.Collections.Generic; using System.Linq; u ...

  4. 小福bbs-冲刺总结

    [小福bbs-冲刺总结] 这个作业属于哪个课程 班级链接 这个作业要求在哪里 作业要求的链接 团队名称 小福bbs 这个作业的目标 冲刺总结 作业的正文 小福bbs-冲刺总结 其他参考文献 面向百度和 ...

  5. php手记之06-tp5验证器

    # 创建验证器 php think make:validate 模块名/验证器名(首字母大写) # 验证器 namespace app\index\validate; use think\Valida ...

  6. PHP用strtotime()函数比较两个时间的大小实例详解

    在PHP开发中,我们经常会对两个时间的大小进行判断,但是,在PHP中,两个时间是不可以直接进行比较,因为时间是由年.月.日.时.分.秒组成的,所以,如果需要将两个时间进行比较的话,我们首先要做的就是将 ...

  7. How to: Secure Connection Strings When Using Data Source Controls

    https://docs.microsoft.com/en-us/previous-versions/dotnet/netframework-3.0/dx0f3cf2(v=vs.85) When wo ...

  8. Understanding glibc malloc

    https://wooyun.js.org/drops/深入理解%20glibc%20malloc.html https://sploitfun.wordpress.com/2015/02/10/un ...

  9. win cmd 设置代理

    windows: HTTP(S)代理服务器:127.0.0.1:5783 SOCKS代理服务器:127.0.0.1:5789 set 2 set http_proxy=socks5://127.0.0 ...

  10. 详解 C++11 lambda表达式

    详解 C++11 lambda表达式   lambda表达式是函数式编程的基础.咱对于函数式编程也没有足够的理解,因此这里不敢胡言乱语,有兴趣的可以自己查找相关资料看下.这里只是介绍C++11中的la ...