To fix the main problem search for:
modtask.php wrote:
// Set class
if ((isset($_POST['class'])) && (($class = $_POST['class']) != $user['class']))
{
if (($CURUSER['class'] < UC_SYSOP) && ($user['class'] >= $CURUSER['class'])) die ();
or:
modtask.php wrote:
// Set class
if ((isset($_POST['class'])) && (($class = $_POST['class']) != $user['class']))
{
if (($CURUSER['class'] < UC_SYSOP) && ($user['class'] >= $CURUSER['class'])) stderr("{$lang['modtask_user_error']}", "{$lang['modtask_try_again']}" );
and replace the red line with:
modtask.php wrote:
// can't make sysops, can't promote user to your rank or higher, can't edit users at your rank or higher
if ($class >= UC_SYSOP || ($class >= $CURUSER['class']) || ($user['class'] >= $CURUSER['class'])) die();
The secondary problem is that even after this check, valid actions are still vulnerable to CSRF issues, for example someone might trick an a Admin to click a link and unknowingly promote him to Moderator - this is how our attacker exploited the privilege escalation issue without being a staffer. Since it's legal for Admins to create Mods, the check above is not sufficient, and we must make sure they create them knowingly.
To mitigate CSRF holes there are many available solutions, ex. captchas, referrer checking, session tokens etc. Bellow is our solution that we believe is portable, non-intrusive and makes minimal assumptions about the run-time environment.
Add the red lines to your userdetails.php:
userdetails.php wrote:
begin_frame("Edit User", true);
print("<form method=post action=modtask.php>\n" );
require "validator.php";
print(validatorForm("ModTask_$user[id]" ));
Add the red lines to your modtask.php:
modtask.php wrote:
// and verify...
if (!is_valid_id($userid)) stderr("Error", "Bad user ID." );
// Handle CSRF (modtask posts form other domains, especially to update class)
require "validator.php";
if (!validate($_POST[validator], "ModTask_$userid" )) die ("Invalid" );
validator.php wrote:
<?
function validator($context){
global $CURUSER;
$timestamp=time();
$hash=hash_hmac("sha1", $CURUSER[secret], $context.$timestamp);
return substr($hash, 0, 20).dechex($timestamp);
}
function validatorForm($context){
return "<input type=\"hidden\" name=\"validator\" value=\"".validator($context)."\"/>";
}
function validate($validator, $context, $seconds=0){
global $CURUSER;
$timestamp=hexdec(substr($validator, 20));
if($seconds && time() > $timestamp + $seconds)
return False;
$hash=substr(hash_hmac("sha1", $CURUSER[secret], $context.$timestamp), 0, 20);
if (substr($validator, 0, 20) != $hash)
return False;
return True;
}
?>
Bookmarks