PT-2012-34: Multiple Vulnerabilities in Random Numbers Generation in OpenCart

Vulnerable software

OpenCart
Version: 1.5.4.1 and earlier

Application link:
http://www.opencart.com/

Severity level

Severity level: High
Impact: Password Reset Token Prediction
Access Vector: Remote  

CVSS v2:
Base Score: 9.3
Vector: (AV:N/AC:M/Au:N/C:C/I:C/A:C)

CVE: not assign

Software description

OpenCart is a powerful open source shopping cart system that is designed feature rich and user friendly.

Vulnerability description

The specialists of the Positive Research center have detected multiple vulnerabilities in OpenCart caused by insecure random numbers generation:

1. Prediction of a token used to reset admin password

Vulnerable code: admin/controller/common/forgotten.php

$code = sha1(uniqid(mt_rand(), true));

To reset the administrator's password an attacker needs to know his or her email. If an attacker manages to learn the email which often has the format admin@host.com he is able to set the new administrator's password.
To generate password reset token OpenCart uses the algorithm where three sources of entropy are employed:
- pseudo random number generated with Mersenne Twister pseudo random number generator
- microseconds (via uniqid)
- pseudo random number generated with linear congruential generator (via uniqid with second argument being true)
An attacker is able to obtain sha1 hash of these values being concatenated. It is evident that it is infeasible to recover these values by bruteforcing sha1 hash. However the vulnerability is still possible offering two potential attacks:

i)
OpenCart leaks mt_rand() outputs through CSRF md5 tokens:

 

POST /admin/index.php?route=user/user HTTP/1.0
Host: host
Content-Length: 0
Connection: keep-alive

 

HTTP/1.1 302
...

Location: http://host/admin/index.php?route=common/home&token=2ead018af57862dda59cb67b77f19075

Having obtained the md5 hash it is possible to recover the random number, and then to obtain a 32-bit seed used to generate that number, and thus predict all the future numbers generated by it. In PHP if the web application does not seed the Mersenne Twister generator, it is seeded automatically the following way:

 (timestamp x pid) XOR (10^6 x php_combined_lcg())

LCG is a linear congruential pseudo random generator which is seeded this way:

S1 = timestamp XOR (microseconds2 << 11)
S2 = pid XOR (microseconds3 << 11)

By bruteforcing pid, and microseconds2 and microseconds3 which are characterized by positive linear correlation an attacker is able to obtain the two seeds used by the LCG generator and thus predict all the future numbers generated by it. Now, if an attacker sends three Keep-Alive requests to a fresh porcess, firstly to get CSRF token, secondly to reset his own password, and finally that of an administrator, he is able to bruteforce the administator's password reset token by reversing his own sha1 token with the following values:

- the output of Mersenne Twister generator seeded with the same value when the CSRF token was generated.
- the output of LCG generator seeded with s1 and s2 obtained via Mersenne Twister seed bruteforce (the first php_combined_lcg() value was used for generating PHPSESSID)
- microseconds

Since an attacker can predict outputs of both Mersenne Twister and LCG generators, the only remaining value to bruteforce are microseconds which requires only 1000000 sha1 calculations. Having obtained the microseconds and being able to predict mt_rand() and php_combined_lcg() an attacker is able to effectively bruteforce the microseonds contained in the administrator's password reset token as the two requests were sent in close succession thus narrowing search space to a few thousand requests.

ii)
OpenCart uses standard PHP session mechanism, that is why it possible to predict random numbers generated with both Mersenne Twister and LCG by bruteforcing PHPSESSID set by a fresh porcess. More info:
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

With these values an attacker can use the same 'Request Twins' technique described in the first part.

2. Prediction of a newly generated customer password:

Vulnerable code: admin/controller/common/forgotten.php

$password = substr(sha1(uniqid(mt_rand(), true)), 0, 10);

To reset a customer's password one should know his or her email address. Using the techniques previously described it is possible to effectively bruteforce the newly generated passwords of customers.

3. Prediction of a newly generated affiliate password:

Vulnerable code: catalog/controller/affiliate/forgotten.php

$password = substr(md5(mt_rand()), 0, 10);

To reset an affiliate's password an attacker needs to know his or her email. Since an attacker has the output of mt_rand() obtained via md5 CSRF token bruteforce he can learn the seed and thus to predict all the future numbers. Using Keep-Alive requests an attacker is able to learn newly generated password of an affiliate.

How to fix

Update your software up to the latest version

Advisory status

28.08.2012 - Vulnerability details were sent to CERT
29.11.2012 - Vendor releases fixed version and details
08.02.2013 - Public disclosure

Credits

The vulnerabilities has discovered by Arseny Reutov, Positive Research Center (Positive Technologies Company)

References

http://en.securitylab.ru/lab/PT-2012-34
https://github.com/opencart/opencart/commit/07a605a8185e8daddaadf462a79d6442dd73e331#upload/admin/controller/common/forgotten.php

Reports on the vulnerabilities previously discovered by Positive Research:

http://ptsecurity.com/research/advisory/
http://en.securitylab.ru/lab/