CVE-2020-15139: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') (CWE-79)
value: In MyBB before version 1.8.24, the custom MyCode (BBCode) for the visual editor doesn't escape input properly when rendering HTML, resulting in a DOM-based XSS vulnerability. The weakness can be exploited by pointing a victim to a page where the visual editor is active (e.g. as a post or Private Message) and operates on a maliciously crafted MyCode message. This may occur on pages where message content is pre-filled using a GET/POST parameter, or on reply pages where a previously saved malicious message is quoted. After upgrading MyBB to 1.8.24, make sure to update the version attribute in the `codebuttons` template for non-default themes to serve the latest version of the patched `jscripts/bbcodes_sceditor.js` file.
CWE description: The product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.
CVSS v3 base score: 6.1
Published: 2020-08-10T22:15Z
Repository: https://github.com/mybb/mybb
Language: JavaScript
Files changed: jscripts/bbcodes_sceditor.js
Commit date: 2020-08-09 13:15:17 +0200
Commit message: Fix MyCode message formatting XSS in visual editor

Diff: diff --git a/jscripts/bbcodes_sceditor.js b/jscripts/bbcodes_sceditor.js
index 07a7e8ab9..b77871443 100644
--- a/jscripts/bbcodes_sceditor.js
+++ b/jscripts/bbcodes_sceditor.js
@@ -56,11 +56,11 @@ $(function ($) {
 
 	// Update editor to use align= as alignment
 	$.sceditor.formats.bbcode
 		.set('align', {
 			html: function (element, attrs, content) {
-				return '<div align="' + (attrs.defaultattr || 'left') + '">' + content + '</div>';
+				return '<div align="' + ($.sceditor.escapeEntities(attrs.defaultattr) || 'left') + '">' + content + '</div>';
 			},
 			isInline: false
 		});
 	$.each(mybbCmd.align, function (i, val) {
 		$.sceditor.formats.bbcode.set(val, {
@@ -166,11 +166,11 @@ $(function ($) {
 					size = 1;
 			}
 			if (size < 0) {
 				size = 0;
 			}
-			return '<font data-scefontsize="' + attrs.defaultattr + '" size="' + size + '">' + content + '</font>';
+			return '<font data-scefontsize="' + $.sceditor.escapeEntities(attrs.defaultattr) + '" size="' + size + '">' + content + '</font>';
 		}
 	});
 
 	$.sceditor.command.set('size', {
 		_dropDown: function (editor, caller, callback) {
@@ -216,11 +216,10 @@ $(function ($) {
 	$.sceditor.formats.bbcode.set('quote', {
 		format: function (element, content) {
 			var author = '',
 				$elm = $(element),
 				$cite = $elm.children('cite').first();
-			$cite.html($cite.text());
 
 			if ($cite.length === 1 || $elm.data('author')) {
 				author = $cite.text() || $elm.data('author');
 
 				$elm.data('author', author);
@@ -242,17 +241,17 @@ $(function ($) {
 		},
 		html: function (token, attrs, content) {
 			var data = '';
 
 			if (attrs.pid)
-				data += ' data-pid="' + attrs.pid + '"';
+				data += ' data-pid="' + $.sceditor.escapeEntities(attrs.pid) + '"';
 
 			if (attrs.dateline)
-				data += ' data-dateline="' + attrs.dateline + '"';
+				data += ' data-dateline="' + $.sceditor.escapeEntities(attrs.dateline) + '"';
 
 			if (typeof attrs.defaultattr !== "undefined")
-				content = '<cite>' + attrs.defaultattr.replace(/ /g, '&nbsp;') + '</cite>' + content;
+				content = '<cite>' + $.sceditor.escapeEntities(attrs.defaultattr).replace(/ /g, '&nbsp;') + '</cite>' + content;
 
 			return '<blockquote' + data + '>' + content + '</blockquote>';
 		},
 		quoteType: function (val, name) {
 			var quoteChar = val.indexOf('"') !== -1 ? "'" : '"';
@@ -278,11 +277,11 @@ $(function ($) {
 			}
 		},
 		html: function (token, attrs, content) {
 			if (typeof attrs.defaultattr == 'string' && attrs.defaultattr != '' && attrs.defaultattr != '{defaultattr}') {
 				return '<font face="' +
-					attrs.defaultattr +
+					$.sceditor.escapeEntities(attrs.defaultattr) +
 					'">' + content + '</font>';
 			} else {
 				return content;
 			}
 		}

Vulnerable code: 				return '<div align="' + (attrs.defaultattr || 'left') + '">' + content + '</div>';
			return '<font data-scefontsize="' + attrs.defaultattr + '" size="' + size + '">' + content + '</font>';
			$cite.html($cite.text());
				data += ' data-pid="' + attrs.pid + '"';
				data += ' data-dateline="' + attrs.dateline + '"';
				content = '<cite>' + attrs.defaultattr.replace(/ /g, '&nbsp;') + '</cite>' + content;
					attrs.defaultattr +
Fixed code: 				return '<div align="' + ($.sceditor.escapeEntities(attrs.defaultattr) || 'left') + '">' + content + '</div>';
			return '<font data-scefontsize="' + $.sceditor.escapeEntities(attrs.defaultattr) + '" size="' + size + '">' + content + '</font>';
				data += ' data-pid="' + $.sceditor.escapeEntities(attrs.pid) + '"';
				data += ' data-dateline="' + $.sceditor.escapeEntities(attrs.dateline) + '"';
				content = '<cite>' + $.sceditor.escapeEntities(attrs.defaultattr).replace(/ /g, '&nbsp;') + '</cite>' + content;
					$.sceditor.escapeEntities(attrs.defaultattr) +