Vulnerability: Cross-site scripting (XSS) vulnerability in plugins/markdown_plugin/_markdown.plugin.php in b2evolution before 6.8.5 allows remote authenticated users to inject arbitrary web script or HTML via a javascript: URL.
Vulnerability ID: CVE-2017-5553
CWE: CWE-79
Severity score: 2.9
Project: https://github.com/b2evolution/b2evolution
Published: 2017-01-23
File: plugins/markdown_plugin/_parsedown.inc.php
Commit message: Ignore wrong urls on markdown plugin
Commit author: yurabakhtin

Vulnerable version (before fix):
 	9. Don't apply <p> around list and already existing paragraph tags 	10. Don't convert HTML entities inside <code> html tags because the "Escape code" plugin does this 	11. Fix the missed empty lines in code blocks which are started and ended with ``` */  class Parsedown function parse($text)  		# Extracts link references.  		if (preg_match_all('/^[ ]{0,3}\[(.+)\][ ]?:[ ]*\n?[ ]*(.+)$/m', $text, $matches, PREG_SET_ORDER)) 		{ 			foreach ($matches as $matches) 			{ private function parse_inline_elements($text) 			if( strpos($text, '](') !== FALSE ) # inline 			{ 				$text = str_replace( '&quot;', '"', $text ); // revert from html entity 				if( preg_match_all( '/(!?)(\[((?:[^][]+|(?2))*)\])\(([^"]*?)( "([^"]+)")?\)/', $text, $matches, PREG_SET_ORDER ) ) 				{ 					foreach ($matches as $matches) 					{ 						if ($matches[1]) # image 						{ 							if( $this->parse_images ) 							{ // Parse images only if it is enabled 								$element = '<img src="'.$matches[4].'" alt="'.$matches[3].'"'.( ! empty( $matches[6] ) ? ' title="'.$matches[6].'"' : '' ).'>'; 							} 						} 						else 						{ 							if( $this->parse_links ) 							{ // Parse links only if it is enabled 								$element_text = $this->parse_inline_elements($matches[3]); 								$element = '<a href="'.$matches[4].'"'.( ! empty( $matches[6] ) ? ' title="'.$matches[6].'"' : '' ).'>'.$element_text.'</a>'; 							} 						} 

Fixed version (after fix):
 	9. Don't apply <p> around list and already existing paragraph tags 	10. Don't convert HTML entities inside <code> html tags because the "Escape code" plugin does this 	11. Fix the missed empty lines in code blocks which are started and ended with ``` 	12. Ignore wrong URLs for links and images; Allow only which begin with http://, https:// or / */  class Parsedown function parse($text)  		# Extracts link references.  		if (preg_match_all('/^[ ]{0,3}\[(.+)\][ ]?:[ ]*\n?[ ]*((https?:\/\/|\/).+)$/m', $text, $matches, PREG_SET_ORDER)) 		{ 			foreach ($matches as $matches) 			{ private function parse_inline_elements($text) 			if( strpos($text, '](') !== FALSE ) # inline 			{ 				$text = str_replace( '&quot;', '"', $text ); // revert from html entity 				if( preg_match_all( '/(!?)(\[((?:[^][]+|(?2))*)\])\((https?:\/\/|\/)([^"]*?)( "([^"]+)")?\)/', $text, $matches, PREG_SET_ORDER ) ) 				{ 					foreach ($matches as $matches) 					{ 						if ($matches[1]) # image 						{ 							if( $this->parse_images ) 							{ // Parse images only if it is enabled 								$element = '<img src="'.$matches[4].$matches[5].'" alt="'.$matches[3].'"'.( ! empty( $matches[7] ) ? ' title="'.$matches[7].'"' : '' ).'>'; 							} 						} 						else 						{ 							if( $this->parse_links ) 							{ // Parse links only if it is enabled 								$element_text = $this->parse_inline_elements($matches[3]); 								$element = '<a href="'.$matches[4].$matches[5].'"'.( ! empty( $matches[7] ) ? ' title="'.$matches[7].'"' : '' ).'>'.$element_text.'</a>'; 							} 						} 

Patch:
patch: @@ -29,6 +29,7 @@
 	9. Don't apply <p> around list and already existing paragraph tags
 	10. Don't convert HTML entities inside <code> html tags because the "Escape code" plugin does this
 	11. Fix the missed empty lines in code blocks which are started and ended with ```
+	12. Ignore wrong URLs for links and images; Allow only which begin with http://, https:// or /
 */
 
 class Parsedown
@@ -100,7 +101,7 @@ function parse($text)
 
 		# Extracts link references.
 
-		if (preg_match_all('/^[ ]{0,3}\[(.+)\][ ]?:[ ]*\n?[ ]*(.+)$/m', $text, $matches, PREG_SET_ORDER))
+		if (preg_match_all('/^[ ]{0,3}\[(.+)\][ ]?:[ ]*\n?[ ]*((https?:\/\/|\/).+)$/m', $text, $matches, PREG_SET_ORDER))
 		{
 			foreach ($matches as $matches)
 			{
@@ -589,23 +590,23 @@ private function parse_inline_elements($text)
 			if( strpos($text, '](') !== FALSE ) # inline
 			{
 				$text = str_replace( '&quot;', '"', $text ); // revert from html entity
-				if( preg_match_all( '/(!?)(\[((?:[^][]+|(?2))*)\])\(([^"]*?)( "([^"]+)")?\)/', $text, $matches, PREG_SET_ORDER ) )
+				if( preg_match_all( '/(!?)(\[((?:[^][]+|(?2))*)\])\((https?:\/\/|\/)([^"]*?)( "([^"]+)")?\)/', $text, $matches, PREG_SET_ORDER ) )
 				{
 					foreach ($matches as $matches)
 					{
 						if ($matches[1]) # image
 						{
 							if( $this->parse_images )
 							{ // Parse images only if it is enabled
-								$element = '<img src="'.$matches[4].'" alt="'.$matches[3].'"'.( ! empty( $matches[6] ) ? ' title="'.$matches[6].'"' : '' ).'>';
+								$element = '<img src="'.$matches[4].$matches[5].'" alt="'.$matches[3].'"'.( ! empty( $matches[7] ) ? ' title="'.$matches[7].'"' : '' ).'>';
 							}
 						}
 						else
 						{
 							if( $this->parse_links )
 							{ // Parse links only if it is enabled
 								$element_text = $this->parse_inline_elements($matches[3]);
-								$element = '<a href="'.$matches[4].'"'.( ! empty( $matches[6] ) ? ' title="'.$matches[6].'"' : '' ).'>'.$element_text.'</a>';
+								$element = '<a href="'.$matches[4].$matches[5].'"'.( ! empty( $matches[7] ) ? ' title="'.$matches[7].'"' : '' ).'>'.$element_text.'</a>';
 							}
 						}
path: plugins/markdown_plugin/_parsedown.inc.php