CVE-2020-27996: Insufficient Information (NVD-CWE-noinfo)
value: An issue was discovered in SmartStoreNET before 4.0.1. It does not properly consider the need for a CustomModelPartAttribute decoration in certain ModelBase.CustomProperties situations.
CWE description: There is insufficient information about the issue to classify it; details are unkown or unspecified.
CVSS v3 base score: 8.8
Published: 2020-10-29T18:15Z
Repository: https://github.com/smartstore/SmartStoreNET
Language: C#
Files changed: src/Plugins/SmartStore.DevTools/Models/BackendExtensionModel.cs, src/Plugins/SmartStore.GoogleMerchantCenter/Models/FeedGoogleMerchantCenterModel.cs, src/Presentation/SmartStore.Web.Framework/Modelling/ModelBase.cs, src/Presentation/SmartStore.Web.Framework/Modelling/SmartModelBinder.cs
Commit date: 2020-08-26 03:47:06 +0200
Commit message: For security reasons complex types transported via in 'ModelBase.CustomProperties' must be decorated with 'CustomModelPartAttribute' now.

Diff: diff --git a/src/Plugins/SmartStore.DevTools/Models/BackendExtensionModel.cs b/src/Plugins/SmartStore.DevTools/Models/BackendExtensionModel.cs
index 7de7bbe5e..949444fe0 100644
--- a/src/Plugins/SmartStore.DevTools/Models/BackendExtensionModel.cs
+++ b/src/Plugins/SmartStore.DevTools/Models/BackendExtensionModel.cs
@@ -1,11 +1,11 @@
 ﻿using SmartStore.Web.Framework.Modelling;
 
 namespace SmartStore.DevTools.Models
 {
+	[CustomModelPart]
 	public class BackendExtensionModel : ModelBase
 	{
 		public string Welcome { get; set; }
-
         public int ProductId { get; set; }
     }
 }
\ No newline at end of file
diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/Models/FeedGoogleMerchantCenterModel.cs b/src/Plugins/SmartStore.GoogleMerchantCenter/Models/FeedGoogleMerchantCenterModel.cs
index 973d44f29..530d8bea2 100644
--- a/src/Plugins/SmartStore.GoogleMerchantCenter/Models/FeedGoogleMerchantCenterModel.cs
+++ b/src/Plugins/SmartStore.GoogleMerchantCenter/Models/FeedGoogleMerchantCenterModel.cs
@@ -17,10 +17,11 @@ namespace SmartStore.GoogleMerchantCenter.Models
 
 		[SmartResourceDisplayName("Plugins.Feed.Froogle.SearchIsTouched")]
 		public string SearchIsTouched { get; set; }
 	}
 
+	[CustomModelPart]
 	public class GoogleProductModel : ModelBase
 	{
 		public int TotalCount { get; set; }
 
 		// This attribute is required to disable editing.
diff --git a/src/Presentation/SmartStore.Web.Framework/Modelling/ModelBase.cs b/src/Presentation/SmartStore.Web.Framework/Modelling/ModelBase.cs
index 68dfb6bdf..2d81d39f8 100644
--- a/src/Presentation/SmartStore.Web.Framework/Modelling/ModelBase.cs
+++ b/src/Presentation/SmartStore.Web.Framework/Modelling/ModelBase.cs
@@ -4,11 +4,16 @@ using System.Web.Mvc;
 using Newtonsoft.Json;
 using SmartStore.Core.Infrastructure;
 
 namespace SmartStore.Web.Framework.Modelling
 {
-    [Serializable]
+    [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
+	public sealed class CustomModelPartAttribute : Attribute
+    {
+    }
+	
+	[Serializable]
 	public sealed class CustomPropertiesDictionary : Dictionary<string, object>
 	{
 	}
 
 	[Serializable]
@@ -39,13 +44,12 @@ namespace SmartStore.Web.Framework.Modelling
 		public TProperty Get<TProperty>(string key)
 		{
 			Guard.NotEmpty(key, nameof(key));
 
 			IDictionary<string, object> dict;
-			object value;
 
-			if (TryGetCustomThreadProperties(false, out dict) && dict.TryGetValue(key, out value))
+			if (TryGetCustomThreadProperties(false, out dict) && dict.TryGetValue(key, out var value))
 			{
 				return (TProperty)value;
 			}
 
 			if (CustomProperties.TryGetValue(key, out value))
diff --git a/src/Presentation/SmartStore.Web.Framework/Modelling/SmartModelBinder.cs b/src/Presentation/SmartStore.Web.Framework/Modelling/SmartModelBinder.cs
index fef277e4d..95e98bdc8 100644
--- a/src/Presentation/SmartStore.Web.Framework/Modelling/SmartModelBinder.cs
+++ b/src/Presentation/SmartStore.Web.Framework/Modelling/SmartModelBinder.cs
@@ -1,9 +1,10 @@
 ﻿using System;
 using System.Collections.Generic;
 using System.ComponentModel;
 using System.Linq;
+using System.Security;
 using System.Web.Mvc;
 using SmartStore.Core.Html;
 using SmartStore.Web.Framework.Security;
 
 namespace SmartStore.Web.Framework.Modelling
@@ -80,10 +81,17 @@ namespace SmartStore.Web.Framework.Modelling
 				else
 				{
 					// Is Complex type
 					var modelName = key.Substring(0, key.Length - subPropertyName.Length - 1);
 					var valueType = GetValueType(keys, modelName, bindingContext.ValueProvider);
+					if (!valueType.HasAttribute<CustomModelPartAttribute>(false))
+                    {
+						throw new SecurityException("For security reasons complex types in '{0}' must be decorated with the '{1}' attribute.".FormatInvariant(
+							typeof(CustomPropertiesDictionary).AssemblyQualifiedNameWithoutVersion(), 
+							typeof(CustomModelPartAttribute).AssemblyQualifiedNameWithoutVersion()));
+                    }
+
 					valueBinder = this.Binders.GetBinder(valueType);
 					var complexBindingContext = new ModelBindingContext
 					{
 						ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, valueType),
 						ModelName = key.Substring(0, key.Length - subPropertyName.Length - 1),

Vulnerable code: 
    [Serializable]
			object value;
			if (TryGetCustomThreadProperties(false, out dict) && dict.TryGetValue(key, out value))
Fixed code: 	[CustomModelPart]
	[CustomModelPart]
    [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
	public sealed class CustomModelPartAttribute : Attribute
    {
    }
	
	[Serializable]
			if (TryGetCustomThreadProperties(false, out dict) && dict.TryGetValue(key, out var value))
using System.Security;
					if (!valueType.HasAttribute<CustomModelPartAttribute>(false))
                    {
						throw new SecurityException("For security reasons complex types in '{0}' must be decorated with the '{1}' attribute.".FormatInvariant(
							typeof(CustomPropertiesDictionary).AssemblyQualifiedNameWithoutVersion(), 
							typeof(CustomModelPartAttribute).AssemblyQualifiedNameWithoutVersion()));
                    }