-----/ 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 /----