Browse Source

2.0.4 security fixes:
! Joshua's fix for validatePasswordFlood logic error (reported by Raz0r)
! Quick fix for Admin Password Reset vulnerability reported by Raz0r
! Directory traversal vulnerability in the function ViewFile (thanks yan.uniko.102 for reporting, Arantor for proposing the fix and Spuds for spotting the undefined variable)
! active users cannot change anymore the email from action activate without deactivation/confirmation (thanks BarteX for reporting the issueand suggesting a fix)
! Change default language from the admin panel could allow XSS, path disclosure and code injection (thanks Jakub Galczyk for reporting the issue)
! Directory listing and editing of arbitrary files from the theme editing page in the admin panel

Signed-off-by: emanuele <[email protected]>

emanuele 12 years ago
parent
commit
5ed0020c24
6 changed files with 40 additions and 11 deletions
  1. 14 4
      Sources/LogInOut.php
  2. 9 4
      Sources/ManageErrors.php
  3. 12 1
      Sources/ManageLanguages.php
  4. 1 1
      Sources/Register.php
  5. 1 1
      Sources/Reminder.php
  6. 3 0
      Sources/Themes.php

+ 14 - 4
Sources/LogInOut.php

@@ -739,14 +739,24 @@ function validatePasswordFlood($id_member, $password_flood_value = false, $was_c
 	if ($password_flood_value !== false)
 		@list ($time_stamp, $number_tries) = explode('|', $password_flood_value);
 
-	// Timestamp invalid or non-existent?
-	if (empty($number_tries) || $time_stamp < (time() - 10))
+	// Timestamp or number of tries invalid?
+	if (empty($number_tries) || empty($time_stamp))
 	{
-		// If it wasn't *that* long ago, don't give them another five goes.
-		$number_tries = !empty($number_tries) && $time_stamp < (time() - 20) ? 2 : 0;
+		$number_tries = 0;
 		$time_stamp = time();
 	}
 
+	// They've failed logging in already
+	if (!empty($number_tries))
+	{
+		// Give them less chances if they failed before
+		$number_tries = $time_stamp < time() - 20 ? 2 : $number_tries;
+
+		// They are trying too fast, make them wait longer
+		if ($time_stamp < time() - 10)
+			$time_stamp = time();
+	}
+
 	$number_tries++;
 
 	// Broken the law?

+ 9 - 4
Sources/ManageErrors.php

@@ -332,16 +332,21 @@ function deleteErrors()
  */
 function ViewFile()
 {
-	global $context, $txt, $boarddir, $sourcedir;
+	global $context, $txt, $boarddir, $sourcedir, $cachedir;
 	// Check for the administrative permission to do this.
 	isAllowedTo('admin_forum');
 
-	// decode the file and get the line
-	$file = base64_decode($_REQUEST['file']);
+	// Decode the file and get the line
+	$file = realpath(base64_decode($_REQUEST['file']));
+	$real_board = realpath($boarddir);
+	$real_source = realpath($sourcedir);
+	$real_cache = realpath($cachedir);
+	$basename = strtolower(basename($file));
+	$ext = strrchr($basename, '.');
 	$line = isset($_REQUEST['line']) ? (int) $_REQUEST['line'] : 0;
 
 	// Make sure the file we are looking for is one they are allowed to look at
-	if (!is_readable($file) || (strpos($file, '../') !== false && ( strpos($file, $boarddir) === false || strpos($file, $sourcedir) === false)))
+	if ($ext != '.php' || (strpos($file, $real_board) === false && strpos($file, $real_source) === false) || ($basename == 'settings.php' || $basename == 'settings_bak.php') || strpos($file, $real_cache) !== false || !is_readable($file))
 		fatal_lang_error('error_bad_file', true, array(htmlspecialchars($file)));
 
 	// get the min and max lines

+ 12 - 1
Sources/ManageLanguages.php

@@ -574,7 +574,18 @@ function ModifyLanguages()
 		checkSession();
 		validateToken('admin-lang');
 
-		if ($_POST['def_language'] != $language)
+		getLanguages(true, false);
+		$lang_exists = false;
+		foreach ($context['languages'] as $lang)
+		{
+			if ($_POST['def_language'] == $lang['filename'])
+			{
+				$lang_exists = true;
+				break;
+			}
+		}
+
+		if ($_POST['def_language'] != $language && $lang_exists)
 		{
 			require_once($sourcedir . '/Subs-Admin.php');
 			updateSettingsFile(array('language' => '\'' . $_POST['def_language'] . '\''));

+ 1 - 1
Sources/Register.php

@@ -572,7 +572,7 @@ function Activate()
 	$smcFunc['db_free_result']($request);
 
 	// Change their email address? (they probably tried a fake one first :P.)
-	if (isset($_POST['new_email'], $_REQUEST['passwd']) && sha1(strtolower($row['member_name']) . $_REQUEST['passwd']) == $row['passwd'])
+	if (isset($_POST['new_email'], $_REQUEST['passwd']) && sha1(strtolower($row['member_name']) . $_REQUEST['passwd']) == $row['passwd'] && ($row['is_activated'] == 0 || $row['is_activated'] == 2))
 	{
 		if (empty($modSettings['registration_method']) || $modSettings['registration_method'] == 3)
 			fatal_lang_error('no_access', false);

+ 1 - 1
Sources/Reminder.php

@@ -241,7 +241,7 @@ function setPassword2()
 	require_once($sourcedir . '/LogInOut.php');
 
 	// Quit if this code is not right.
-	if (empty($_POST['code']) || substr($realCode, 0, 10) != substr(md5($_POST['code']), 0, 10))
+	if (empty($_POST['code']) || substr($realCode, 0, 10) !== substr(md5($_POST['code']), 0, 10))
 	{
 		// Stop brute force attacks like this.
 		validatePasswordFlood($_POST['u'], $flood_value, false);

+ 3 - 0
Sources/Themes.php

@@ -1804,6 +1804,9 @@ function EditTheme()
 	list ($theme_dir, $context['theme_id']) = $smcFunc['db_fetch_row']($request);
 	$smcFunc['db_free_result']($request);
 
+	if (!file_exists($theme_dir . '/index.template.php') && !file_exists($theme_dir . '/css/index.css'))
+		fatal_lang_error('theme_edit_missing', false);
+
 	if (!isset($_REQUEST['filename']))
 	{
 		if (isset($_GET['directory']))