1、弁言
西湖论剑杯线上预选赛线上赌场一题,明文攻击出来的hint中给了/flag/seed.txt以及一个字符串code,这里须要轻微脑洞一点想到seed是指随机数种子,以及Web页面上的code值是每小时改换的\公众随机数\"大众:
我们利用phpmtseed工具(C编写,速率很快)可以根据随机数碰撞出随机数的种子,从而获取flag:
以下是事理剖析:
2、随机数的安全毛病
随机数广泛运用于天生验证码、Token、密钥等场景中,分为真随机数和伪随机数。我们通过算法(常用线性同余)和种子(常用时钟)得到的随机数属于伪随机数:当知道种子或已产生的随机数时,随机数序列是可以被预测的。
可以看到PHP Manual实在提示了天生随机数用于加密是不屈安的,但是这个Caution不知为何只存在于英文版的PHP Manual中,中文版被遗漏了...这可能也是很多海内的开拓运用涌现过此毛病的一个缘故原由。
PHP中天生随机数的函数有rand()和mtrand(),它们分别对应srand()和mtstrand()两个用于播种随机数种子的函数。我们建立rand.php进行测试:
<?phpmt_srand(2333);srand(2333);echo \公众seed=2333,rand()产生的随机数序列:\n\"大众;for($i=1;$i<=3;$i++){ echo rand().\"大众\n\"大众;}echo \公众seed=2333,mt_rand()产生的随机数序列:\n\"大众;for($i=1;$i<=3;$i++){ echo mt_rand().\"大众\n\"大众;}?>
实行:
可以看出当随机数种子相同时,不管是rand()还是mtrand()产生的随机数序列都是相同的,如果seed透露则会导致随机数序列的透露。当种子值为固定如mtsrand(1000)时,随机数形同虚设;而利用动态种子也未必安全,如:
//seed值较小,直接遍历爆破mt_srand(mt_rand(0,1000));//用公开的time()作为种子,和静态种子一样危险mt_srand(time());//破解时要把稳做事器韶光可能存在偏差,须要设定一个较小的范围
自PHP 4.2.0 起,随机数发生器会自动完成播种,不再须要手工调用srand()或mt_srand(),但是这样仍旧不屈安,我们分别对两个函数进行谈论
3、rand()
rand()在产生随机数时不会自动调用srand(),产生的随机数序列可以通过这个式子预测:
state[i] = state[i-3] + state[i-31]
以是我们可以网络rand()天生的32位以上的随机序列,以预测后面的随机序列。
详细参考:Cracking-Php-Rand
(http://www.sjoerdlangkemper.nl/2016/02/11/cracking-php-rand/)
并且在某些平台下rand()最大值为32767,非常随意马虎遭到爆破。
4、mt_rand()
根据PHP Manual,mtrand()产生随机数值的均匀速率比libc供应的rand()快四倍,rand() 函数默认利用 libc 随机数发生器,mtrand() 函数是非正式用来更换它的。
mtrand()函数的安全毛病紧张涌如今,所谓\公众自动播种\"大众实在是PHP在同一个要求进程中只会进行一次播种,也便是说纵然多次调用mtrand()函数,也只会根据第一次播种的种子天生随机数。这一结论的证明可以通过mt_rand()的源码剖析或写个小脚本测试来完成,不再展开,函数的核心实当代码是这一部分:
PHPAPI void php_mt_srand(uint32_t seed){ / Seed the generator with a simple uint32 / php_mt_initialize(seed, BG(state)); php_mt_reload(); / Seed only once / BG(mt_rand_is_seeded) = 1;}/ }}} // {{{ php_mt_rand /PHPAPI uint32_t php_mt_rand(void){ / Pull a 32-bit integer from the generator state Every other access function simply transforms the numbers extracted here / register uint32_t s1; if (UNEXPECTED(!BG(mt_rand_is_seeded))) { php_mt_srand(GENERATE_SEED()); } if (BG(left) == 0) { php_mt_reload(); } --BG(left); s1 = BG(next)++; s1 ^= (s1 >> 11); s1 ^= (s1 << 7) & 0x9d2c5680U; s1 ^= (s1 << 15) & 0xefc60000U; return ( s1 ^ (s1 >> 18) );}
由于根据种子天生随机数序列的打算并不可逆,有效的破解方法该当是穷举种子并天生随机数序列,与已知的随机数(序列)作比较,这也是文章开头提到的phpmtseed工具的实现逻辑。
5、安全建议
涉及到加密/权限/CSRF Token等敏感操作时:
不要利用韶光函数作为种子或直接作为随机数:time()/microtime()不要直策应用rand()``mt_rand()这样的弱伪随机数天生器随机数要足够长以防御暴力破解6、干系题目学习
0CTF 2016 Rand2:http://www.vuln.cn/6004
NJCTF 2017 Guess:
https://www.cnblogs.com/afanti/p/8722760.html
7、干系操作演习
PHP常见危险函数:理解PHP常见的危险函数,利用这些函数可能带来的程序薄弱性;
参考链接:http://wonderkun.cc/index.html/?p=585
本文为合天原创,未经许可,严禁转载。