COMMAND
myPhpAdmin
SYSTEMS AFFECTED
myPhpAdmin <= 2.2.0rc3
PROBLEM
Carl Livitt found following. Further to the excellent research
done by Shaun Clowes of Secure Reality Pty Ltd into remote
command execution on webservers running myPhpAdmin, it turns out
that there is another method of exploitation. For details of
what phpMyAdmin does, how it works and other security issues with
the product, please refer to article SRADV00008:
http://oliver.efri.hr/~crv/security/bugs/Others/phpadm.html
The new method involves an unchecked variable in the
'tbl_copy.php' and 'tbl_rename.php'scripts. By passing a
carefully crafted URL to these scripts, it is possible to insert
PHP instructions into an eval() function thereby enabling the
attacker to execute arbitrary commands on the webserver with the
privileges of the http daemon, typically 'nobody'.
The method works perfectly on a default installation of
phpMyAdmin, but has not been tested in an environment where the
advanced authentication mechanism has been enabled.
In addition, this method assumes that the 'test' table in the
mySQL database has not been removed; in order to successfully
exploit the vulnerability presented here, it is necessary to have
the ability to make changes to tables within a database. It
should be noted that any database that phpMyAdmin has access
rights to is sufficient: the 'test' database just happens to be
very handy.
In 'tbl_copy.php' and 'tbl_rename.php' are these lines of code:
tbl_copy.php: eval("\$message = \"$strCopyTableOK\";");
tbl_rename.php: eval("\$message = \"$strRenameTableOK\";");
These eval() functions are called if the rest of the code in the
script executed successfully. If it were possible to modify the
contents of $strCopyTableOK or $strRenameTableOK, it would be
possible to execute arbitrary eval() code.
Fortunately for an attacker, it is possible to control the
contents of either of these variables. For example, by passing a
URL of:
http://victim/phpmyadmin/tbl_copy.php?strCopyTableOK=".passthru('cat%20/etc/passwd')."
it is possible to set things up so that the script dumps
/etc/passwd. But it doesn't. Why? Because earlier code stops
execution before we get to the eval(): (Note that the code has
been edited for brevity)
if (isset($new_name) && $new_name!=""){
.
$result = mysql_query($sql_structure) or mysql_die();
.
$result = mysql_query($query) or mysql_die();
.
$result = mysql_query($sql_structure) or mysql_die();
}
else mysql_die($strTableEmpty);
If any of the mysql_query() calls fail, mysql_die() is called and
execution stops. This is no good to us, as we need the calls to
succeed in order for eval() to be executed with our commands in
it.
To make sure all the calls succeed, we need to make sure that we
have a database that we can create tables in. This can be done
by using 'tbl_create.php' script like so:
http://victim/phpmyadmin/tbl_create.php?db=test&table=haxor&query=dummy+integer+primary+key+auto_increment&submit=1
In the default installation, we don't need to specify a username
or password and will now have a table called 'haxor' in the
'test' database. We're now ready to exploit the 'tbl_copy.php'
script:
http://victim/phpmyadmin/tbl_copy.php?db=test&table=haxor&new_name=test.haxor2&strCopyTableOK=".passthru('cat%20/etc/passwd')."
Success! The contents of /etc/passwd are included in the webpage
that is returned by this URL. Of course, the choice of command to
execute is limited only by the imagination of the attacker.
SOLUTION
This is really simple to fix: just comment out the offending
eval() statements in the 'tbl_copy.php' and 'tbl_rename.php'
scripts. The calls to eval() are not used at any point in the
script because the $strCopyTableOK and $strRenameTableOK
variables are never normally defined. This means it is safe to
remove them.
This isn't so much a problem with phpMyAdmin as it is with PHP in
general. We would HIGHLY recommend turning off register_globals
in php.ini (which is the default in set in php.ini-dist for
php4+). With that option disabled, the only thing that passing
in extra parameters can do is create entries in the $HTTP_GET_VARS
array, and it's not possible to clobber global script variables.