diff -u mediawiki-1.15.0/debian/changelog mediawiki-1.15.0/debian/changelog --- mediawiki-1.15.0/debian/changelog +++ mediawiki-1.15.0/debian/changelog @@ -1,3 +1,25 @@ +mediawiki (1:1.15.0-1.1ubuntu0.3) karmic-security; urgency=low + + * SECURITY UPDATE: A CSRF vulnerability was discovered in our login + interface. Although regular logins are protected as of 1.15.3, it was + discovered that the account creation and password reset features were not + protected from CSRF. This could lead to unauthorised access to private + wikis. (LP: #586773) + - debian/patches/CSRF-Special-Userlogin-no-CVE_rev-66991.patch + - patch from upstream SVN rev. 66991 + - http://lists.wikimedia.org/pipermail/mediawiki-announce/2010-May/000091.html + - https://bugzilla.wikimedia.org/show_bug.cgi?id=23371 + * SECURITY UPDATE: Noncompliant CSS parsing behaviour in Internet Explorer + allows attackers to construct CSS strings which are treated as safe by + previous versions of MediaWiki, but are decoded to unsafe strings by + Internet Explorer. (LP: #586773) + - debian/patches/XSS-IE-no-CVE_rev-66992.patch + - patch from upstream SVN rev. 66992 + - http://lists.wikimedia.org/pipermail/mediawiki-announce/2010-May/000091.html + - https://bugzilla.wikimedia.org/show_bug.cgi?id=23687 + + -- Andreas Wenning Mon, 31 May 2010 00:48:35 +0200 + mediawiki (1:1.15.0-1.1ubuntu0.2) karmic-security; urgency=low * SECURITY UPDATE: MediaWiki was found to be vulnerable to login CSRF. An diff -u mediawiki-1.15.0/debian/patches/series mediawiki-1.15.0/debian/patches/series --- mediawiki-1.15.0/debian/patches/series +++ mediawiki-1.15.0/debian/patches/series @@ -6,0 +7,2 @@ +CSRF-Special-Userlogin-no-CVE_rev-66991.patch +XSS-IE-no-CVE_rev-66992.patch only in patch2: unchanged: --- mediawiki-1.15.0.orig/debian/patches/CSRF-Special-Userlogin-no-CVE_rev-66991.patch +++ mediawiki-1.15.0/debian/patches/CSRF-Special-Userlogin-no-CVE_rev-66991.patch @@ -0,0 +1,191 @@ +Subject: Fixes CSRF vulnerability in "e-mail me my password", "create account" +and "create by e-mail" features of [[Special:Userlogin]] +Origin: http://svn.wikimedia.org/viewvc/mediawiki?view=rev&revision=66991 +Index: b/includes/specials/SpecialUserlogin.php +=================================================================== +--- a/includes/specials/SpecialUserlogin.php 2010-05-28 10:26:42.529429454 +0200 ++++ b/includes/specials/SpecialUserlogin.php 2010-05-28 10:26:52.410680218 +0200 +@@ -69,7 +69,7 @@ + $this->mRemember = $request->getCheck( 'wpRemember' ); + $this->mLanguage = $request->getText( 'uselang' ); + $this->mSkipCookieCheck = $request->getCheck( 'wpSkipCookieCheck' ); +- $this->mToken = $request->getVal( 'wpLoginToken' ); ++ $this->mToken = ($this->mType == 'signup' ) ? $request->getVal( 'wpCreateaccountToken' ) : $request->getVal( 'wpLoginToken' ); + + if ( $wgRedirectOnLogin ) { + $this->mReturnTo = $wgRedirectOnLogin; +@@ -246,6 +246,25 @@ + return false; + } + ++ # Request forgery checks. ++ if ( !self::getCreateaccountToken() ) { ++ self::setCreateaccountToken(); ++ $this->mainLoginForm( wfMsg( 'sessionfailure' ) ); ++ return false; ++ } ++ ++ # The user didn't pass a createaccount token ++ if ( !$this->mToken ) { ++ $this->mainLoginForm( wfMsg( 'sessionfailure' ) ); ++ return false; ++ } ++ ++ # Validate the createaccount token ++ if ( $this->mToken !== self::getCreateaccountToken() ) { ++ $this->mainLoginForm( wfMsg( 'sessionfailure' ) ); ++ return false; ++ } ++ + # Check permissions + if ( !$wgUser->isAllowed( 'createaccount' ) ) { + $this->userNotPrivilegedMessage(); +@@ -260,7 +279,7 @@ + $wgUser->inSorbsBlacklist( $ip ) ) + { + $this->mainLoginForm( wfMsg( 'sorbs_create_account_reason' ) . ' (' . htmlspecialchars( $ip ) . ')' ); +- return; ++ return false; + } + + # Now create a dummy user ($u) and check if it is valid +@@ -336,6 +355,7 @@ + return false; + } + ++ self::clearCreateaccountToken(); + return $this->initUser( $u, false ); + } + +@@ -638,13 +658,26 @@ + return; + } + +- # Check against blocked IPs +- # fixme -- should we not? ++ # Check against blocked IPs so blocked users can't flood admins ++ # with password resets + if( $wgUser->isBlocked() ) { + $this->mainLoginForm( wfMsg( 'blocked-mailpassword' ) ); + return; + } + ++ # If the user doesn't have a login token yet, set one. ++ if ( !self::getLoginToken() ) { ++ self::setLoginToken(); ++ $this->mainLoginForm( wfMsg( 'sessionfailure' ) ); ++ return; ++ } ++ ++ # If the user didn't pass a login token, tell them we need one ++ if ( !$this->mToken ) { ++ $this->mainLoginForm( wfMsg( 'sessionfailure' ) ); ++ return; ++ } ++ + # Check against the rate limiter + if( $wgUser->pingLimiter( 'mailpassword' ) ) { + $wgOut->rateLimited(); +@@ -665,6 +698,12 @@ + return; + } + ++ # Validate the login token ++ if ( $this->mToken !== self::getLoginToken() ) { ++ $this->mainLoginForm( wfMsg( 'sessionfailure' ) ); ++ return; ++ } ++ + # Check against password throttle + if ( $u->isPasswordReminderThrottled() ) { + global $wgPasswordReminderResendTime; +@@ -680,6 +719,7 @@ + $this->mainLoginForm( wfMsg( 'mailerror', $result->getMessage() ) ); + } else { + $this->mainLoginForm( wfMsg( 'passwordsent', $u->getName() ), 'success' ); ++ self::clearLoginToken(); + } + } + +@@ -911,11 +951,18 @@ + $template->set( 'canremember', ( $wgCookieExpiration > 0 ) ); + $template->set( 'remember', $wgUser->getOption( 'rememberpassword' ) or $this->mRemember ); + +- if ( !self::getLoginToken() ) { +- self::setLoginToken(); ++ if ( $this->mType == 'signup' ) { ++ if ( !self::getCreateaccountToken() ) { ++ self::setCreateaccountToken(); ++ } ++ $template->set( 'token', self::getCreateaccountToken() ); ++ } else { ++ if ( !self::getLoginToken() ) { ++ self::setLoginToken(); ++ } ++ $template->set( 'token', self::getLoginToken() ); + } +- $template->set( 'token', self::getLoginToken() ); +- ++ + # Prepare language selection links as needed + if( $wgLoginLanguageSelector ) { + $template->set( 'languages', $this->makeLanguageSelector() ); +@@ -974,7 +1021,7 @@ + } + + /** +- * Generate a new login token and attach it to the current session ++ * Randomly generate a new login token and attach it to the current session + */ + public static function setLoginToken() { + global $wgRequest; +@@ -986,12 +1033,36 @@ + /** + * Remove any login token attached to the current session + */ +- public static function clearLoginToken() { ++ public static function clearLoginToken() { + global $wgRequest; + $wgRequest->setSessionData( 'wsLoginToken', null ); + } + + /** ++ * Get the createaccount token from the current session ++ */ ++ public static function getCreateaccountToken() { ++ global $wgRequest; ++ return $wgRequest->getSessionData( 'wsCreateaccountToken' ); ++ } ++ ++ /** ++ * Randomly generate a new createaccount token and attach it to the current session ++ */ ++ public static function setCreateaccountToken() { ++ global $wgRequest; ++ $wgRequest->setSessionData( 'wsCreateaccountToken', User::generateToken() ); ++ } ++ ++ /** ++ * Remove any createaccount token attached to the current session ++ */ ++ public static function clearCreateaccountToken() { ++ global $wgRequest; ++ $wgRequest->setSessionData( 'wsCreateaccountToken', null ); ++ } ++ ++ /** + * @private + */ + function cookieRedirectCheck( $type ) { +Index: b/includes/templates/Userlogin.php +=================================================================== +--- a/includes/templates/Userlogin.php 2010-05-28 10:26:42.529429454 +0200 ++++ b/includes/templates/Userlogin.php 2010-05-28 10:26:52.410680218 +0200 +@@ -268,6 +268,7 @@ + + + haveData( 'uselang' ) ) { ?> ++haveData( 'token' ) ) { ?> + + +
msgWiki( 'signupend' ); ?>
only in patch2: unchanged: --- mediawiki-1.15.0.orig/debian/patches/XSS-IE-no-CVE_rev-66992.patch +++ mediawiki-1.15.0/debian/patches/XSS-IE-no-CVE_rev-66992.patch @@ -0,0 +1,83 @@ +Subject: Fixed XSS vulnerability affecting IE clients only, due to a CSS +validation issue. +Origin: http://svn.wikimedia.org/viewvc/mediawiki?view=rev&revision=66992 +Index: b/includes/Sanitizer.php +=================================================================== +--- a/includes/Sanitizer.php 2010-05-28 10:26:42.469431189 +0200 ++++ b/includes/Sanitizer.php 2010-05-28 10:32:04.100712956 +0200 +@@ -607,10 +607,6 @@ + # http://msdn.microsoft.com/workshop/author/dhtml/overview/recalc.asp + if( $attribute == 'style' ) { + $value = Sanitizer::checkCss( $value ); +- if( $value === false ) { +- # haxx0r +- continue; +- } + } + + if ( $attribute === 'id' ) { +@@ -664,10 +660,8 @@ + $value = StringUtils::delimiterReplace( '/*', '*/', ' ', $value ); + + // Decode escape sequences and line continuation +- // See the grammar in the CSS 2 spec, appendix D, Mozilla implements it accurately. +- // IE 8 doesn't implement it at all, but there's no way to introduce url() into +- // IE that doesn't hit Mozilla also. +- static $decodeRegex; ++ // See the grammar in the CSS 2 spec, appendix D. ++ static $decodeRegex, $reencodeTable; + if ( !$decodeRegex ) { + $space = '[\\x20\\t\\r\\n\\f]'; + $nl = '(?:\\n|\\r\\n|\\r|\\f)'; +@@ -676,29 +670,40 @@ + (?: + ($nl) | # 1. Line continuation + ([0-9A-Fa-f]{1,6})$space? | # 2. character number +- (.) # 3. backslash cancelling special meaning ++ (.) | # 3. backslash cancelling special meaning ++ () | # 4. backslash at end of string + )/xu"; + } +- $decoded = preg_replace_callback( $decodeRegex, ++ $value = preg_replace_callback( $decodeRegex, + array( __CLASS__, 'cssDecodeCallback' ), $value ); +- if ( preg_match( '!expression|https?://|url\s*\(!i', $decoded ) ) { +- // Not allowed +- return false; +- } else { +- // Allowed, return CSS with comments stripped +- return $value; ++ ++ // Reject problematic keywords and control characters ++ if ( preg_match( '/[\000-\010\016-\037\177]/', $value ) ) { ++ return '/* invalid control char */'; ++ } elseif ( preg_match( '! expression | filter\s*: | accelerator\s*: | url\s*\( !ix', $value ) ) { ++ return '/* insecure input */'; + } ++ return $value; + } + + static function cssDecodeCallback( $matches ) { + if ( $matches[1] !== '' ) { ++ // Line continuation + return ''; + } elseif ( $matches[2] !== '' ) { +- return codepointToUtf8( hexdec( $matches[2] ) ); ++ $char = codepointToUtf8( hexdec( $matches[2] ) ); + } elseif ( $matches[3] !== '' ) { +- return $matches[3]; ++ $char = $matches[3]; ++ } else { ++ $char = '\\'; ++ } ++ if ( $char == "\n" || $char == '"' || $char == "'" || $char == '\\' ) { ++ // These characters need to be escaped in strings ++ // Clean up the escape sequence to avoid parsing errors by clients ++ return '\\' . dechex( ord( $char ) ) . ' '; + } else { +- throw new MWException( __METHOD__.': invalid match' ); ++ // Decode unnecessary escape ++ return $char; + } + } +