Does PHP use the rand () function to generate token security? Web applications often need to create a token that is difficult to guess, for example, a session token, a CSRF token, or a token used to reset the password in the email in the forgot password function. These tokens should be encrypted and protected, but they are often used to call the rand function multiple times and output as strings. This article will show how difficult it is to predict the token generated by using rand.
How does rand work?
In PHP, the rand function is used to generate pseudo-random numbers, and the seed for random numbers initialization is generated by srand. If you do not choose to call srand, PHP uses a number that is hard to guess as the seed of the random number generator. The seed generated by srand completely determines the random number generated by the rand function.
The random number generator will remain in the state after srand initialization, and change after each rand call. This status is related to the process status, so the two processes usually do not return the same rand random number.
Sample program
The instance program we use is EZChatter, a small program used to generate the CSRF token, but the security is not well considered during creation:
public static function gen($len = 5){ $token = ''; while($len--){ $choose = rand(0, 2); if ($choose === 0) $token .= chr(rand(ord('A'), ord('Z'))); else if($choose === 1) $token .= chr(rand(ord('a'), ord('z'))); else $token .= chr(rand(ord('0'), ord('9'))); } return $token;}
As you can see, the above code first calls rand to determine whether to use uppercase letters, lowercase letters, or numbers, and then selects a specific letter or number. Every time we request index. php, a new CSRF token is generated, so we can send a request at will to generate the token we need. Our goal is to predict the token assigned to the user so that we can launch a CSRF attack.
Guess seeds
As mentioned above, the random number sequence is completely determined by the seed, so we can simply try every possible number as the srand parameter to guess the correct random number generator state. However, please note that this will only work in Linux when the server process is newly created. if the server has generated many rand calls, then we need to repeat the same number of calls in the cracking program to get the same status. In Windows, the status of the random number generator is only related to the srand parameter, so there is no need to repeat the process.
If you want to obtain a token from a newly created process, you can use the following PHP script to crack it:
for ($i = 0; $i < PHP_INT_MAX; $i++) { srand($i); if (Token::gen(10) == "2118Jx9w3e") { die("Found: $i \n"); }}
It takes about 12 hours to search for a total of 4294967295 possible srand parameter values. However, since PHP only calls the glibc rand function, we can re-convert the PHP code to C to speed up the operation. Here we provide two versions of code: glibc rand and Windows rand. It is based on the php code in token. PHP and uses the macro ext/standard/rand. c in PHP to repeatedly find possible seeds. It takes about 10 minutes on Windows and about several hours on Linux.
Once the attack is completed, a random number generator is in the same state as the server, so that the server can generate the same token. By comparing the token generated by yourself with the token returned by the server, you can know which tokens have been assigned to the user and then start the attack.
Status attacks on Linux
In Windows, the state of the srand parameter is the same as that of the random number generator, but it is different in Linux. The rand () of glibc maintains a series of numbers and determines the next state as follows:
state[i] = state[i-3] + state[i-31]return state[i] >> 1
Therefore, each output is the sum of the results of 3 and 31. Consider the following token:
- 6ZF5kNgonV
- 9h3byovpGR
- GGt0A94U92
Now, it determines whether the next random number is an uppercase letter, lowercase letter, or number. This will be determined by the results of the previous 3rd and 31 times, 9 in gGt0A94U92 and y in 9h3byovpGR. Therefore, we expect the next rand (0, 2) output to be similar to limit 10/10 + 25/26 × 3 then = 2 mod 3, which means we will get a number. Let's assume that we can predict this number. the number obtained by the next call of rand is determined by the number obtained by the first 3rd calls of rand and the lower-case letter obtained by the previous 31st calls of rand. This number is between limit 2/3 + 1/3 × 10 limit = 0 mod 10 and limit 3/3 + 2/3 × 10 limit = 6 mod 10. Therefore, the expected value is between 0 and 6, and the final result is 4:
As you can see, we cannot accurately predict the next random number through this method, but we can also clearly predict the approximate range, and you can no longer call it a random number. In addition, you can use the same method to guess the status of glibc's random number generator, although there is no attempt.
Summary
Secure and encrypted random number generator should be used. If rand is used to generate a random number, the random number generator can be guessed in many cases, so that the token can be predicted. It is relatively difficult to predict tokens in Linux, but this is still not safe. The random number generator on Windows is relatively easy to use, because its random number generator status can be guessed out within several minutes.
* Original article: sjoerdlangkemper. nl, compiled by xiaix in FB,