PHP uniqid() and LCG non-randomness write-up

In twitter conversations with OSVDB the question arose whether the Front Accounting 2.3.13 Predictable File Name and Public Path vulnerability I disclosed yesterday had a connection with a previously reported LCG vulnerability in 2010 since the LCG in PHP apparently uses the uniqid() function.

Although there is a loose connection, the upshot is that they are different. Here’s why.

After checking though the PHP SVN this morning, it seems like the connection is tenuous at best. If we begin with the LCG revisions we notice that in branches 5.2 and 5.3 that we are adding entropy through tv.tv as shown below:

But from our standpoint — determining the connection between LCG and uniqid() — this doesn’t matter much. It’s the fact that it uses the following structure and call:

The call to uniqid() also uses the timeval structure:

Unlike LCG, uniqid() adds it’s randomness in the following way:

Since randomness is added differently, the gettimeofday() function is in fact the only overlap between uniqid.c and lcg.c that concerns us to understand the connection and the difference for this analysis.

For our purposes to answer OSVDB’s initial inquiry the answer comes down to this:

  • Both LCG and the uniqid() function use the same gettimeofday call as the initial seed for randomness, whether through returning a string for uniqid() or as part of PHPSESSIONID
  • LCG and uniqid() are totally separate and use independent calls to fill data structures defined in separate areas of memory
  • the functions do not call one another at all
  • LCG uses the php_combined_lcg() function which does not use uniqid() in any functions below it for session token creation

From an attackers point of view, the key in the LCG attack is the ability to use a function such as uniqid() — which is much more likely to be used by a developer — to get the server’s seed (actual time on the box) and off of which we will base our attack on the PHPSESSIONID since neither offer enough entropy.

Conclusion 1: LCG does not use uniqid() as it’s seed generator. We can see this from the SVN of the committed LCG code linked to earlier: uniqid was neither removed or added. A quick search of the actual LCG code will show that this header or function is not included in the PHPSESSION generating code. The description in NVD and OSVDB is is in fact incorrect for the 2010 attack*.

Conclusion 2: The non-randomness I described in my Front Accounting vuln release is based solely off the uniqid() function and is independent from the LCG functions although both have a similar but not exact non-randomness issue.

I cannot take full credit for this as some of the critical pieces of the LCG attack analysis come from Andreas Bogk in this post at Full-Disclosure.

* The original post of the LCG issue in 2001 shows that PHPLIB session.c called uniqid() but code apparently is for PHP3 and is no longer maintained since 2007. This is most likely where the NVD / OSVBD confusion comes from. Note that the write-up and tool released by samy kamkar for the 2010 disclosure does not mention uniqid(). At no point is uniqid() used in session.c as described in the original 2001 post as of PHP4.