|
php.net | support | documentation | report a bug | advanced search | search howto | statistics | random bug | login |
[2012-08-29 12:30 UTC] ymaryshev at ptsecurity dot ru
Description:
------------
The specialists of the Positive Research center has discovered vulnerability in
PHP's implementation of Linear Congruential Generator seeding:
ext/standard/lcg.c:
LCG(s1) = tv.tv_sec ^ (tv.tv_usec<<11);
...
#ifdef ZTS
LCG(s2) = (long) tsrm_thread_id();
#else
LCG(s2) = (long) getpid();
#endif
...
LCG(s2) ^= (tv.tv_usec<<11);
The implemented seeding is weak because firslty the value of tv.tv_sec is known
to the attacker(for example through the Date HTTP-header), the time interval
between two time measurements (tv.tv_usec) on most systems does not exceed 5
microseconds, and finally process id or thread id on most systems present only
32768 different values.
Therefore an attacker is able to bruteforce the two seeds of LCG generator
having obtained a random value generated with it, and thus to predict all the
future random values. The internal function php_combined_lcg() is used in:
- lcg_value() which is its wrapper
- uniqid() with more_entropy argument set to true
- in PHPSESSID generation
- in the GENERATE_SEED function which generates seed for rand() and mt_rand()
PHPSESSID can be effectively bruteforced for seeds of LCG generator as was
described in:
http://crypto.di.uoa.gr/CRYPTO.SEC/Randomness_Attacks_files/paper.pdf
http://blog.ptsecurity.com/2012/08/not-so-random-numbers-take-two.html
As for GENERATE_SEED, the code is:
ext/standard/php_rand.h:
#ifdef PHP_WIN32
#define GENERATE_SEED() (((long) (time(0) * GetCurrentProcessId())) ^ ((long)
(1000000.0 * php_combined_lcg(TSRMLS_C))))
#else
#define GENERATE_SEED() (((long) (time(0) * getpid())) ^ ((long) (1000000.0 *
php_combined_lcg(TSRMLS_C))))
#endif
If an attacker manages to obtain a random number generated by rand() or
mt_rand() he is able to bruteforce seed (for example using precomputed rainbow
tables) and subsequently to recover the seeds of LCG generator even without
knowing any random values generated with LCG.
Our specialists has created a program to bruteforce seeds of LCG generator given
the seed of rand() or mt_rand(). The bruteforce of full range of microseconds
(0-999999), complete process IDs range (0-32768), and two deltas (both 0-5) can
be performed in less than 10 minutes on Nvidia GT540M.
PatchesPull RequestsHistoryAllCommentsChangesGit/SVN commits
|
|||||||||||||||||||||||||||
Copyright © 2001-2025 The PHP GroupAll rights reserved. |
Last updated: Sat Oct 25 21:00:01 2025 UTC |
-->important to highlight the arrows<-- >Is it specific for the usage of uniquid() by specific application to generate >password reset tokens or is it a generic problem in some function that is >supposed to be secure but is not? This is a generic problem as password token generation involving uniqid and/or (mt_)rand can be predicted if an attacker manages to obtain one of the pseudo random values produced either by the LCG generator or any PRNG seeded with the GENERATE_SEED macro, for example Mersenne Twister. 1. which function or functionality it is ext/standard/php_rand.h: #ifdef PHP_WIN32 #define GENERATE_SEED() (((long) (time(0) * GetCurrentProcessId())) ^ ((long) (1000000.0 * -->php_combined_lcg<--(TSRMLS_C)))) #else #define GENERATE_SEED() (((long) (time(0) * getpid())) ^ ((long) (1000000.0 * -- >php_combined_lcg<--(TSRMLS_C)))) #endif ext/standard/lcg.c: static void lcg_seed(TSRMLS_D) /* {{{ */ { struct timeval tv; if (gettimeofday(&tv, NULL) == 0) { LCG(s1) = -->tv.tv_sec<-- ^ (tv.tv_usec<<11); } else { LCG(s1) = 1; } #ifdef ZTS LCG(s2) = (long) -->tsrm_thread_id<--(); #else LCG(s2) = (long) -->getpid<--(); #endif /* -->Add entropy to s2 by calling gettimeofday() again<-- */ if (gettimeofday(&tv, NULL) == 0) { LCG(s2) ^= (tv.tv_usec<<11); } LCG(seeded) = 1; } 2. what is the specific problem in that code, in your opinion There are two problems: 1. The first is in php_rand.h as the GENERATE_SEED macro uses an output of php_combined_lcg function to produce a seed that is meant to be secure. 2. The second problem is in lcg.c as the lcg_seed function does weak seeding for the LCG generator. The seeding is weak because: a. the timestamp is known to the attacker b. the pid presents only 215 different values c. additional entropy via “calling gettimeofday() again” does not exceed on most systems 5 microseconds. 3. what is the code or the scenario that reproduces the security problem and what the nature of this problem is (e.g. easier guessing PHP session ID, etc.) If a web app leaks either mt_rand/rand number or a value produced by uniqid(more_entropy=true)/lcg_value, its password reset token generation mechanism employing one of these functions or a combination of them can be predicted. In the previous messages we demonstrated how it is possible to exploit a web app if it leaks a random number produced by mt_rand() and generates password reset token like this: sha1(uniqid(mt_rand(),1)) Consider an attack scenario when a web app leaks a value produced by uniqid(more_entropy=true) and generates password reset token using mt_rand. Let us assume that an attacker has obtained a uniqid(more_entropy=true) output: 5045cea645c8b-->3.57116457<-- The value marked in bold is produced by the LCG generator. With this value an attacker is able to recover the initial seeds as they are too weak. Having the seeds of the LCG generator he is now able to figure out the value produced by php_combined_lcg() that was used in the GENERATE_SEED macro. After this an attacker is able to bruteforce locally the seed of mt_rand assuming the fact that he has a token sent to an attacker’s email and subsequently to predict the administrator’s token. These attack scenarios suppose that the attacker is able to spawn fresh processes with newly seeded PRNGs. >As far as I can see it involves reversing random sha1 hashes, which I'm not >sure is very practical We conducted a series of experiments and the attack turned out to be highly practical. In the previous messages we explained how one can obtain constituent parts of a string that is passed to sha1: 1. mt_rand which can be predicted 2. php_combined_lcg which can be narrowed to about 100-200 possible outputs 3. only 215 values of pid Reversing this sha1 hash is a matter of a few seconds. Another thing is the bruteforce of microseconds in the administrator’s password token, but in practice it took us several thousand requests to match it.