php隨機函數mt_rand()產生的小問題大漏洞

**說到隨機函數的應用,作爲一個菜鳥,理解的也不是很深刻,在這裏之作爲一個筆記來記錄,以後慢慢將其掌握之後,再在內容上面進行加深。

隨機函數的作用,常常是用來生成驗證碼、隨機文件名、訂單號,如果用來做安全驗證的話常常用來生成加密key、token等等。**

一、常見的隨機函數

1、rand()
常用的隨機函數,默認生成0-getrandmax()之間的隨機數,不過因爲性能問題,已經被mt_rand()函數替代
相關函數:
rand(int $min,int $max)
srand(int $seed),生成時間種子,同一個時間種子下隨機生成的隨機數值是相同的。
getrandmax()獲取最大隨機數,這裏獲取的隨機數會隨系統的不同而不同。如linux最大2147483647
2、mt_rand
常用的隨機函數,默認生成0-mt_getrandmax()之間的隨機數, Mersenne Twister 算法生成隨機整數
相關函數:
mt_srand(),生成種子,同一個種子下隨機生成的隨機數值是相同的。
該函數是產生隨機值的更好選擇,返回結果的速度是 rand() 函數的 4 倍(手冊是是這麼寫的),我個人並不認同的,我感覺他說的4倍是很多年前的事了。因爲mt_rand()使用的Mersenne Twister algorythm是1997的事,所以在很多年前,和rand()在速度上的差異可能是(4倍),自2004年,rand()已經開始使用algorythm,所以現在它們速度上沒有太大的區別.
有時候手冊也是騙人的,就像一會要說的這個函數造成的問題。
3、uniqid()
生成唯一ID的函數,精確到了微妙,較mt_rand精確。適用場景生成token和生成uuid
具體細節沒做研究
4、openssl_random_pseudo_bytes()
適用於生成token,具體詳情沒做研究

二、mt_rand()函數造成的問題所在

--
今天重點記錄一下mt_rand帶來的問題,和在CTF中的具體解法。對於這個函數的介紹是有兩個版本的,一個是英文版,一個是中文版,去對比一下,在英文版中會多出一個警告:

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()函數的時候,都會檢查一下系統有沒有播種。(播種是由mt_srand()函數完成的),當隨機種子生成後,後面生成的隨機數都會根據這個隨機種子生成。所以前面也說到,同一個種子下隨機生成的隨機數值是相同的。同時,也解釋了我們破解隨機種子的可行性。如果每次調用mt_rand()函數都需要生成一個隨機種子的話,那根本就沒辦法破解。
做一個簡單的測試,測試隨機數種子相同,後面的每次執行的隨機數也相同
腳本:

<?php
mt_srand(45678913);
echo mt_rand()."<br/>";
echo mt_rand()."<br/>";
echo mt_rand()."<br/>";
echo mt_rand()."<br/>";
echo mt_rand()."<br/>";
echo mt_rand()."<br/>";
echo mt_rand()."<br/>";
echo mt_rand()."<br/>";
echo mt_rand()."<br/>";
?>

php隨機函數mt_rand()產生的小問題大漏洞
兩次執行結果:
php隨機函數mt_rand()產生的小問題大漏洞
可以看到得到的隨機數是相同的。
同時我們應該注意,mt_srand()函數播種的時候,只有在第一次調用mt_rand()函數的時候纔會使用。所以如果我們知道了第一次生成的隨機數值,就可能爆破出隨機數種子。

--
接下來就來驗證一下
首先爆破出隨機數種子,利用工具php_mt_srand
工具鏈接:https://github.com/lepiaf/php_mt_seed
這個工具的具體用法,也不再着解釋,說起來還是挺複雜的,在這裏推薦一個文章吧,看完就看一瞭解個大概,同時還有助於理解隨機數

https://×××w.openwall.com/php_mt_seed/README
爆破隨機數種子,我們將得到的第一組數進行爆破
php隨機函數mt_rand()產生的小問題大漏洞
得到三組seed值,裏面就有我們使用的隨機數種子,當然在正常情況下我們是不知道這個數值的,所以還需要去驗證。

--
如何驗證?
1、只需要將這個種子通過mt_srand()函數生成數值後再調用幾組mt_rand()函數生成幾組隨機數\
2、然後將隨機數和我們剛開始得到的隨機數對比即可。
測試第一組
php隨機函數mt_rand()產生的小問題大漏洞

php隨機函數mt_rand()產生的小問題大漏洞
測試第二組
php隨機函數mt_rand()產生的小問題大漏洞

php隨機函數mt_rand()產生的小問題大漏洞
測試第三組
php隨機函數mt_rand()產生的小問題大漏洞

php隨機函數mt_rand()產生的小問題大漏洞
可以看到只有第二組的隨機數和原來的相同,到這裏便成功獲取到了seed值。

三、CTF中的題目

題目來自於成都大學網絡***演練平臺--隨機數
題目鏈接:http://ctf.cdusec.org/challenges
題目非常簡單,直接就給出了題目隨機數的源碼以及前幾組隨機數:

<?php
echo "PHP 5.4.26";
mt_srand(xxxxxxxx);
#We can't tell you what is xxxxxxxx!
echo mt_rand()."<br/>";
echo mt_rand()."<br/>";
echo mt_rand()."<br/>";
echo mt_rand()."<br/>";
echo mt_rand()."<br/>";
echo mt_rand()."<br/>";
echo mt_rand()."<br/>";
echo mt_rand()."<br/>";
echo mt_rand()."<br/>";
echo mt_rand()."<br/>";
echo "echo flag{".mt_rand()."}"
?>
984489752
619387123
2070958802
2105559368
1909473866
1679323715
1910332168
640569646
1103001695
1871111424
flag
So, Please guess the flag!

理解了上面所說的爆破步驟的話,對這個題目簡直太容易了。
1、因爲我們不知道××××代表那些數字,但是給出了第一組隨機數。上面也說了,只有第一次調用mt_rand()函數的時候纔會自動播種,接下來的就會根據這個種子生成隨機數。所以我們來利用第一組爆破
php隨機函數mt_rand()產生的小問題大漏洞
2、將seed數值帶入到腳本mt_srand()函數當中,去跑flag,因爲只要得到seed,接下來不管怎麼跑,跑幾次,得到的幾組數值都是相同的。這裏得到兩組seed值,測試兩次,將得到的flag遞交看一下那個正確即可。
php隨機函數mt_rand()產生的小問題大漏洞

四、應當注意的問題。

隨機數這個東西在系統之間和php版本之間是有削微的區別的,比如就CTF這道題目而言,開始我並沒有注意php版本問題,當時的測試環境是php7的版本,同樣的做法,答案卻是不同的,所以結果也一直出不來。
同時也告訴我們,細節決定成敗。

因爲是剛剛學習這一知識點,可能講解的也不夠詳細或者有些許錯誤。哪位大佬看到不準確的希望給指出,謝謝!

相關鏈接:
http://php.net/manual/en/function.mt-rand.php
http://php.net/manual/zh/function.mt-rand.php

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章