0

Does anybody know if there was a time or event where somebody used rand()'s weakness in order to predict exploit it? Something like generating tokens or cheating in video games?

Since prior to PHP 7, rand() was very easy to crack. In fact here is some C code, credit to Peter Selinger, that predicts the values given a seed:

#include <stdio.h>

#define MAX 1000
#define seed 1

main() {
  int r[MAX];
  int i;

  r[0] = seed;
  for (i=1; i<31; i++) {
    r[i] = (16807LL * r[i-1]) % 2147483647;
    if (r[i] < 0) {
      r[i] += 2147483647;
    }
  }
  for (i=31; i<34; i++) {
    r[i] = r[i-31];
  }
  for (i=34; i<344; i++) {
    r[i] = r[i-31] + r[i-3];
  }
  for (i=344; i<MAX; i++) {
    r[i] = r[i-31] + r[i-3];
    printf("%d\n", ((unsigned int)r[i]) >> 1);
  }
}

So once again, was there a time when this weakness was used in order to predict the next random number and exploit something?

Thanks!

Redseb
  • 199
  • 10
  • Maybe... But keep in mind, that when it should be not possible to predict numbers, nobody will use these kind of PRNGs (incl. MersenneTwister, which is very easy to predict too after not much observed bits). Most desktop-games might use it and don't care much (actually speed-runners exploit this in some older games, yes, no PHP), but i can guarantee you, that every critical application will use cryptoPRNGs and careful entropy-based seeding (online casinos and co). – sascha Jul 08 '17 at 20:41

1 Answers1

0

Before PHP 7, PHP use Linear Congruential Generator algorithm to generate a random number or in short LCG. The algorithm works as follow:

 next_random = (previous_random * a + c) % m
 previous_random = next_random

When you first make a random, obviously, there is no previous_random number. That's why we provide seed. So, seed is just a first previous_random value.

Now, we know the algorithm, but we need to know what the value of a, c, and m that PHP use. I believe that each version of PHP use different value for those. But let say we do not know those value, how do we guess this value. In my case, I am using PHP 5.6.15 Windows.

srand(0);
var_dump(rand());  // 12345
var_dump(rand());  // 5758

So, m = getrandmax () + 1. Since our seed is 0, so our c = 12345. To get value a, we can use simple loop to guess a.

$m  = getrandmax () + 1;
for($a = 0; $a < $m; $a++)
   if ((($a * 12345 + 12345) % $m) == 5758) 
       var_dump($a);  // in my case, 20077

or you can get value a like this

srand(0); rand(); // 12345
srand(1); rand(); // 32422
// so a = 32422 - 12345 = 20077

Now, I am able to write the same random function as my current PHP version.

class visal_rnd 
{
    function __construct($seed = 0) {
        $this->seed = $seed;
    }

    function rnd() {
        $this->seed = ($this->seed * 20077 + 12345) % 32768;
        return $this->seed;
    }
}

However

I was able to predict my own PHP version because I have so much knowledge about my current environment, I know a few previous random, I know the seed. If the attacker has almost zero knowledge, it would not be easy to attack.

Mersenne Twister

PHP 7.0+, by default, use Mersenne Twister. There are more parameters to be guessed than Linear Congruential Generator. So, it requires more knowledge.

Is Linear Congruential Generator Bad?

Depends on how much information you have exposed to the public. If you generate only one random number and attacker has no knowledge of a, previous_random, c and m. It is impossible for attackers to predict the next random number.

invisal
  • 11,075
  • 4
  • 33
  • 54