php / phplib session-id generation

From: Jarno Huuskonen (Jarno.Huuskonenat_private)
Date: Thu Jul 05 2001 - 02:41:05 PDT

  • Next message: Thornton, Simon (Simon)** CTR **: "RE: Cisco device HTTP exploit..."

    Hi,
    
    After reading Shaun Clowes' "A Study In Scarlet"
    (http://www.securereality.com.au/studyinscarlet.txt)
    I decided to take a peek at how php / phplib create the session ids.
    
    I just had a quick peek so the following 'information' is based on first
    impressions and is probably full of errors. I hope this could stir up
    some discussion about session id generation / using timeofday as random
    seed/value etc. (or could somebody point me to some references).
    
    --------- php-4.0.6 session generation ---------
    Here's a snippet from php-4.0.6 session.c (_php_create_id):
    
            gettimeofday(&tv, NULL);
            PHP_MD5Init(&context);
    
            sprintf(buf, "%ld%ld%0.8f", tv.tv_sec, tv.tv_usec,
            php_combined_lcg() *\
     10);
            PHP_MD5Update(&context, buf, strlen(buf));
            if (PS(entropy_length) > 0) {
                    int fd;
                    fd = VCWD_OPEN((PS(entropy_file), O_RDONLY));
                    if (fd >= 0) {
                            char *p;
                            int n;
     
                            p = emalloc(PS(entropy_length));
                            n = read(fd, p, PS(entropy_length));
                            if (n > 0) {
                                    PHP_MD5Update(&context, p, n);
                            }
                            efree(p);
                            close(fd);
                    }
            }
            PHP_MD5Final(digest, &context);
    
    From this it looks like if you don't have entropy_file/entropy_length
    defined(in php.ini) the created session id is not very random: md5hash
    of timeofday and php_combined_lcg (I assume that ...lcg is just a simple
    pseudorandom gen).
    Using /dev/{u,a}random as entropy_file should make the session id quite
    hard to guess.
    
    --------- php-4.0.6 lcg function  ---------
    Here's the code snippet for the lcg (lcg.c):
    double php_combined_lcg(void)
    {
            long q;
            long z;
            LCGLS_FETCH();
    
            MODMULT(53668,40014,12211,2147483563L, LCG(s1));
            MODMULT(52774,40692,3791, 2147483399L, LCG(s2));
    
            z = LCG(s1) - LCG(s2);
            if(z < 1) {
                    z += 2147483562;
            }
    
            return z * 4.656613e-10;
    }
    
    static void lcg_init_globals(LCGLS_D)
    {
            LCG(s1) = 1;
    #ifdef ZTS
            LCG(s2) = (long) tsrm_thread_id();
    #else
            LCG(s2) = (long) getpid();
    #endif
    }
    
    If I understand the code somewhat right it uses getpid() and 1 as the
    initial values. Getpid is probably not that hard to obtain/guess: If you
    have local access or you know the server is eg. default redhat-xx box
    with only apache running --> guess/test what pid (range of pids) apache
    can get.
    
    
    --------- phplib-7.2c session generation  ---------
    Here's a snippet from phplib-7.2c session.inc (get_id):
        if ( "" == $id ) {
          $newid=true;
          $id = $this->that->ac_newid(md5(uniqid($this->magic)),
          $this->name);
        }
    
    Here the session id is md5hash of uniqid($this->magic)). php's uniqid
    creates a string from (uniqid.c):
    
            gettimeofday((struct timeval *) &tv, (struct timezone *) NULL);
            sec = (int) tv.tv_sec;
            usec = (int) (tv.tv_usec % 1000000);
    
            /* The max value usec can have is 0xF423F, so we use only five
            hex
             * digits for usecs.
             */
            if (MORE_ENTROPY) {
                    sprintf(uniqid, "%s%08x%05x%.8f",
            (*prefix)->value.str.val, sec,
     usec, php_combined_lcg() * 10);
            } else {
                    sprintf(uniqid, "%s%08x%05x", (*prefix)->value.str.val,
            sec, use
    c);
            }
    
            RETURN_STRING(uniqid,1);
    
    So from this it appears that phplib creates the session id from a
    $this->magic and timeofday. (Phplib doesn't use the more entropy
    parameter when calling uniqid). So if you know the second when session
    id was created and $this->magic then there're only 1000000 possible
    values. I would think that with timing and (since some computers don't
    have usec timers) there would be a lot less? than million values to try.
    
    Has anyone done more research on this ?
    If I remember correctly using timeofday as 'random' (seed?) was one of
    the reasons why netscape's ssl and kerberos4 keys were cracked ?
    
    And for the phplib's session id generation: Why not read some extra
    entropy from entropy_file (if defined in php.ini) and create the session
    id as md5(uniqid(...) . $entropy_from_file) ?
    
    What methods could attacker use to determine the time on the server ?
    Use ntp if the server has ntp-server... What about tcp-timestamps could
    they be used for determining the time ?
    
    -Jarno
    
    -- 
    Jarno Huuskonen <Jarno.Huuskonenat_private>
    



    This archive was generated by hypermail 2b30 : Thu Jul 05 2001 - 08:13:21 PDT