Activity Feed

3
Ratings
Technical Analysis

As per Microsoft’s blog post on Exchange Server 0day use by the HAFNIUM actors, CVE-2021-26857 is a deserialization vulnerability in Exchange Server’s Unified Messaging (voicemail) service. Exploiting the vulnerability reportedly requires admin access or chaining with another vuln (likely CVE-2021-26855), but successful exploitation results in RCE as the SYSTEM account. This vulnerability would ideally be combined with an auth bypass, which CVE-2021-26855 may very well provide.

I took a look at CVE-2021-26857 last night and came up with the following patch diff:

--- exchange.unpatched/Microsoft.Exchange.UM.UMCore/UMCore/PipelineContext.cs	2021-03-02 19:54:18.000000000 -0600
+++ exchange.patched/Microsoft.Exchange.UM.UMCore/UMCore/PipelineContext.cs	2021-03-02 19:55:19.000000000 -0600
@@ -1,742 +1,886 @@
 using System;
+using System.Collections.Generic;
 using System.Globalization;
 using System.IO;
+using System.Runtime.Serialization;
+using Microsoft.Exchange.Compliance.Serialization.Formatters;
+using Microsoft.Exchange.Data;
+using Microsoft.Exchange.Data.Common;
 using Microsoft.Exchange.Data.Directory;
 using Microsoft.Exchange.Data.Directory.Recipient;
 using Microsoft.Exchange.Data.Directory.SystemConfiguration;
 using Microsoft.Exchange.Data.Storage;
 using Microsoft.Exchange.Diagnostics;
 using Microsoft.Exchange.Diagnostics.Components.UnifiedMessaging;
 using Microsoft.Exchange.ExchangeSystem;
 using Microsoft.Exchange.TextProcessing.Boomerang;
 using Microsoft.Exchange.UM.UMCommon;
+using Microsoft.Mapi;

 namespace Microsoft.Exchange.UM.UMCore
 {
 	internal abstract class PipelineContext : DisposableBase, IUMCreateMessage
 	{
 		internal PipelineContext()
 		{
 		}

 		internal PipelineContext(SubmissionHelper helper)
 		{
 			bool flag = false;
 			try
 			{
 				this.helper = helper;
 				this.cultureInfo = new CultureInfo(helper.CultureInfo);
 				flag = true;
 			}
 			finally
 			{
 				if (!flag)
 				{
 					this.Dispose();
 				}
 			}
 		}

 		public MessageItem MessageToSubmit
 		{
 			get
 			{
 				return this.messageToSubmit;
 			}
 			protected set
 			{
 				this.messageToSubmit = value;
 			}
 		}

 		public string MessageID
 		{
 			get
 			{
 				return this.messageID;
 			}
 			protected set
 			{
 				this.messageID = value;
 			}
 		}

 		internal abstract Pipeline Pipeline { get; }

 		internal Microsoft.Exchange.UM.UMCommon.PhoneNumber CallerId
 		{
 			get
 			{
 				return this.helper.CallerId;
 			}
 		}

 		internal Guid TenantGuid
 		{
 			get
 			{
 				return this.helper.TenantGuid;
 			}
 		}

 		internal int ProcessedCount
 		{
 			get
 			{
 				return this.processedCount;
 			}
 		}

 		internal ExDateTime SentTime
 		{
 			get
 			{
 				return this.sentTime;
 			}
 			set
 			{
 				this.sentTime = value;
 			}
 		}

 		internal CultureInfo CultureInfo
 		{
 			get
 			{
 				return this.cultureInfo;
 			}
 		}

 		protected internal string HeaderFileName
 		{
 			get
 			{
 				if (string.IsNullOrEmpty(this.headerFileName))
 				{
 					Guid guid = Guid.NewGuid();
 					this.headerFileName = Path.Combine(Utils.VoiceMailFilePath, guid.ToString() + ".txt");
 				}
 				return this.headerFileName;
 			}
 			protected set
 			{
 				this.headerFileName = value;
 			}
 		}

 		protected internal string CallerAddress
 		{
 			get
 			{
 				return this.helper.CallerAddress;
 			}
 			protected set
 			{
 				this.helper.CallerAddress = value;
 			}
 		}

 		protected internal string CallerIdDisplayName
 		{
 			get
 			{
 				return this.helper.CallerIdDisplayName;
 			}
 			protected set
 			{
 				this.helper.CallerIdDisplayName = value;
 			}
 		}

 		protected internal string MessageType
 		{
 			internal get
 			{
 				return this.messageType;
 			}
 			set
 			{
 				this.messageType = value;
 			}
 		}

 		public virtual void PrepareUnProtectedMessage()
 		{
 			CallIdTracer.TraceDebug(ExTraceGlobals.VoiceMailTracer, this.GetHashCode(), "PipelineContext:PrepareUnProtectedMessage.", Array.Empty<object>());
 			using (DisposeGuard disposeGuard = default(DisposeGuard))
 			{
 				this.messageToSubmit = MessageItem.CreateInMemory(StoreObjectSchema.ContentConversionProperties);
 				disposeGuard.Add<MessageItem>(this.messageToSubmit);
 				this.SetMessageProperties();
 				disposeGuard.Success();
 			}
 		}

 		public virtual void PrepareProtectedMessage()
 		{
 			throw new InvalidOperationException();
 		}

 		public virtual void PrepareNDRForFailureToGenerateProtectedMessage()
 		{
 			throw new InvalidOperationException();
 		}

 		public virtual PipelineDispatcher.WIThrottleData GetThrottlingData()
 		{
 			return new PipelineDispatcher.WIThrottleData
 			{
 				Key = this.GetMailboxServerId(),
 				RecipientId = this.GetRecipientIdForThrottling(),
 				WorkItemType = PipelineDispatcher.ThrottledWorkItemType.NonCDRWorkItem
 			};
 		}

 		public virtual void PostCompletion()
 		{
 			CallIdTracer.TraceDebug(ExTraceGlobals.VoiceMailTracer, 0, "PipelineContext - Deleting header file '{0}'", new object[]
 			{
 				this.headerFileName
 			});
 			Util.TryDeleteFile(this.headerFileName);
 		}

 		internal static PipelineContext FromHeaderFile(string headerFile)
 		{
 			PipelineContext pipelineContext = null;
 			PipelineContext result;
 			try
 			{
 				ContactInfo contactInfo = null;
 				string text = null;
 				int num = 0;
 				ExDateTime exDateTime = default(ExDateTime);
 				string text2 = null;
 				SubmissionHelper submissionHelper = new SubmissionHelper();
 				uint num2;
 				using (StreamReader streamReader = File.OpenText(headerFile))
 				{
 					string text3;
 					while ((text3 = streamReader.ReadLine()) != null)
 					{
 						string[] array = text3.Split(" : ".ToCharArray(), 2, StringSplitOptions.RemoveEmptyEntries);
 						if (array != null && array.Length == 2)
 						{
 							string text4 = array[0];
 							num2 = <PrivateImplementationDetails>.ComputeStringHash(text4);
 							if (num2 <= 872212143U)
 							{
 								if (num2 <= 134404218U)
 								{
 									if (num2 != 77294025U)
 									{
 										if (num2 != 111122938U)
 										{
-											if (num2 == 134404218U)
+											if (num2 != 134404218U)
 											{
-												if (text4 == "ProcessedCount")
-												{
-													num = Convert.ToInt32(array[1], CultureInfo.InvariantCulture) + 1;
-													continue;
-												}
+												goto IL_409;
+											}
+											if (!(text4 == "ProcessedCount"))
+											{
+												goto IL_409;
 											}
+											num = Convert.ToInt32(array[1], CultureInfo.InvariantCulture) + 1;
+											continue;
 										}
-										else if (text4 == "RecipientObjectGuid")
+										else
 										{
+											if (!(text4 == "RecipientObjectGuid"))
+											{
+												goto IL_409;
+											}
 											submissionHelper.RecipientObjectGuid = new Guid(array[1]);
 											continue;
 										}
 									}
-									else if (text4 == "CallerNAme")
+									else
 									{
+										if (!(text4 == "CallerNAme"))
+										{
+											goto IL_409;
+										}
 										submissionHelper.CallerName = array[1];
 										continue;
 									}
 								}
 								else if (num2 <= 507978139U)
 								{
 									if (num2 != 152414519U)
 									{
-										if (num2 == 507978139U)
+										if (num2 != 507978139U)
 										{
-											if (text4 == "RecipientName")
-											{
-												submissionHelper.RecipientName = array[1];
-												continue;
-											}
+											goto IL_409;
 										}
+										if (!(text4 == "RecipientName"))
+										{
+											goto IL_409;
+										}
+										submissionHelper.RecipientName = array[1];
+										continue;
 									}
-									else if (text4 == "ContactInfo")
+									else
 									{
-										contactInfo = (CommonUtil.Base64Deserialize(array[1]) as ContactInfo);
-										continue;
+										if (!(text4 == "ContactInfo"))
+										{
+											goto IL_409;
+										}
+										Exception ex = null;
+										try
+										{
+											try
+											{
+												using (MemoryStream memoryStream = new MemoryStream(Convert.FromBase64String(array[1])))
+												{
+													contactInfo = (ContactInfo)TypedBinaryFormatter.DeserializeObject(memoryStream, PipelineContext.contactInfoDeserializationAllowList, null, true);
+												}
+											}
+											catch (ArgumentNullException ex)
+											{
+											}
+											catch (SerializationException ex)
+											{
+											}
+											catch (Exception ex)
+											{
+											}
+											continue;
+										}
+										finally
+										{
+											if (ex != null)
+											{
+												CallIdTracer.TraceDebug(ExTraceGlobals.VoiceMailTracer, 0, "Failed to get contactInfo from header file {0} with Error={1}", new object[]
+												{
+													headerFile,
+													ex
+												});
+											}
+										}
 									}
 								}
 								else if (num2 != 707084238U)
 								{
-									if (num2 == 872212143U)
+									if (num2 != 872212143U)
 									{
-										if (text4 == "CallerId")
-										{
-											submissionHelper.CallerId = Microsoft.Exchange.UM.UMCommon.PhoneNumber.Parse(array[1]);
-											continue;
-										}
+										goto IL_409;
 									}
+									if (!(text4 == "CallerId"))
+									{
+										goto IL_409;
+									}
+									submissionHelper.CallerId = Microsoft.Exchange.UM.UMCommon.PhoneNumber.Parse(array[1]);
+									continue;
 								}
-								else if (text4 == "SentTime")
+								else
 								{
+									if (!(text4 == "SentTime"))
+									{
+										goto IL_409;
+									}
 									DateTime dateTime = Convert.ToDateTime(array[1], CultureInfo.InvariantCulture);
 									exDateTime = new ExDateTime(ExTimeZone.CurrentTimeZone, dateTime);
 									continue;
 								}
 							}
 							else if (num2 <= 2593661420U)
 							{
 								if (num2 <= 1526417836U)
 								{
 									if (num2 != 978885386U)
 									{
-										if (num2 == 1526417836U)
+										if (num2 != 1526417836U)
 										{
-											if (text4 == "MessageType")
-											{
-												text = array[1];
-												continue;
-											}
+											goto IL_409;
+										}
+										if (!(text4 == "MessageType"))
+										{
+											goto IL_409;
 										}
+										text = array[1];
+										continue;
 									}
-									else if (text4 == "CallerAddress")
+									else
 									{
+										if (!(text4 == "CallerAddress"))
+										{
+											goto IL_409;
+										}
 										submissionHelper.CallerAddress = array[1];
 										continue;
 									}
 								}
 								else if (num2 != 1850847732U)
 								{
-									if (num2 == 2593661420U)
+									if (num2 != 2593661420U)
 									{
-										if (text4 == "CallId")
-										{
-											submissionHelper.CallId = array[1];
-											continue;
-										}
+										goto IL_409;
 									}
+									if (!(text4 == "CallId"))
+									{
+										goto IL_409;
+									}
+									submissionHelper.CallId = array[1];
+									continue;
 								}
-								else if (text4 == "CallerIdDisplayName")
+								else
 								{
+									if (!(text4 == "CallerIdDisplayName"))
+									{
+										goto IL_409;
+									}
 									submissionHelper.CallerIdDisplayName = array[1];
 									continue;
 								}
 							}
 							else if (num2 <= 3342616108U)
 							{
 								if (num2 != 2975106116U)
 								{
-									if (num2 == 3342616108U)
+									if (num2 != 3342616108U)
 									{
-										if (text4 == "TenantGuid")
-										{
-											submissionHelper.TenantGuid = new Guid(array[1]);
-											continue;
-										}
+										goto IL_409;
 									}
+									if (!(text4 == "TenantGuid"))
+									{
+										goto IL_409;
+									}
+									submissionHelper.TenantGuid = new Guid(array[1]);
+									continue;
 								}
-								else if (text4 == "SenderAddress")
+								else
 								{
+									if (!(text4 == "SenderAddress"))
+									{
+										goto IL_409;
+									}
 									string text5 = array[1];
 									continue;
 								}
 							}
 							else if (num2 != 3581765001U)
 							{
-								if (num2 == 4186841001U)
+								if (num2 != 4186841001U)
 								{
-									if (text4 == "CultureInfo")
-									{
-										submissionHelper.CultureInfo = array[1];
-										continue;
-									}
+									goto IL_409;
+								}
+								if (!(text4 == "CultureInfo"))
+								{
+									goto IL_409;
 								}
+								submissionHelper.CultureInfo = array[1];
+								continue;
 							}
-							else if (text4 == "MessageID")
+							else if (!(text4 == "MessageID"))
 							{
-								text2 = array[1];
-								continue;
+								goto IL_409;
 							}
+							text2 = array[1];
+							continue;
+							IL_409:
 							submissionHelper.CustomHeaders[array[0]] = array[1];
 						}
 					}
 				}
 				num2 = <PrivateImplementationDetails>.ComputeStringHash(text);
 				if (num2 <= 894870128U)
 				{
 					if (num2 <= 360985808U)
 					{
 						if (num2 != 356120169U)
 						{
 							if (num2 == 360985808U)
 							{
 								if (text == "Fax")
 								{
 									pipelineContext = new FaxPipelineContext(submissionHelper);
-									goto IL_62E;
+									goto IL_694;
 								}
 							}
 						}
 						else if (text == "IncomingCallLog")
 						{
 							pipelineContext = new IncomingCallLogPipelineContext(submissionHelper);
-							goto IL_62E;
+							goto IL_694;
 						}
 					}
 					else if (num2 != 438908515U)
 					{
 						if (num2 != 466919760U)
 						{
 							if (num2 == 894870128U)
 							{
 								if (text == "CDR")
 								{
 									pipelineContext = CDRPipelineContext.Deserialize((string)submissionHelper.CustomHeaders["CDRData"]);
-									goto IL_62E;
+									goto IL_694;
 								}
 							}
 						}
 						else if (text == "MissedCall")
 						{
 							pipelineContext = new MissedCallPipelineContext(submissionHelper);
-							goto IL_62E;
+							goto IL_694;
 						}
 					}
 					else if (text == "OCSNotification")
 					{
 						pipelineContext = OCSPipelineContext.Deserialize((string)submissionHelper.CustomHeaders["OCSNotificationData"]);
 						text2 = pipelineContext.messageID;
 						exDateTime = pipelineContext.sentTime;
-						goto IL_62E;
+						goto IL_694;
 					}
 				}
 				else if (num2 <= 1086454342U)
 				{
 					if (num2 != 995233564U)
 					{
 						if (num2 == 1086454342U)
 						{
 							if (text == "XSOVoiceMail")
 							{
 								pipelineContext = new XSOVoiceMessagePipelineContext(submissionHelper);
-								goto IL_62E;
+								goto IL_694;
 							}
 						}
 					}
 					else if (text == "PartnerTranscriptionRequest")
 					{
 						pipelineContext = new PartnerTranscriptionRequestPipelineContext(submissionHelper);
-						goto IL_62E;
+						goto IL_694;
 					}
 				}
 				else if (num2 != 1356218075U)
 				{
 					if (num2 != 2525024257U)
 					{
 						if (num2 == 3974407582U)
 						{
 							if (text == "SMTPVoiceMail")
 							{
 								if (num < PipelineWorkItem.ProcessedCountMax - 1)
 								{
 									pipelineContext = new VoiceMessagePipelineContext(submissionHelper);
-									goto IL_62E;
+									goto IL_694;
 								}
 								pipelineContext = new MissedCallPipelineContext(submissionHelper);
-								goto IL_62E;
+								goto IL_694;
 							}
 						}
 					}
 					else if (text == "HealthCheck")
 					{
 						pipelineContext = new HealthCheckPipelineContext(Path.GetFileNameWithoutExtension(headerFile));
-						goto IL_62E;
+						goto IL_694;
 					}
 				}
 				else if (text == "OutgoingCallLog")
 				{
 					pipelineContext = new OutgoingCallLogPipelineContext(submissionHelper);
-					goto IL_62E;
+					goto IL_694;
 				}
 				throw new HeaderFileArgumentInvalidException(string.Format(CultureInfo.InvariantCulture, "{0}: {1}", "MessageType", text));
-				IL_62E:
+				IL_694:
 				if (text2 == null)
 				{
 					text2 = Guid.NewGuid().ToString();
 					exDateTime = ExDateTime.Now;
 				}
 				pipelineContext.HeaderFileName = headerFile;
 				pipelineContext.processedCount = num;
 				if (contactInfo != null)
 				{
 					IUMResolveCaller iumresolveCaller = pipelineContext as IUMResolveCaller;
 					if (iumresolveCaller != null)
 					{
 						iumresolveCaller.ContactInfo = contactInfo;
 					}
 				}
 				pipelineContext.sentTime = exDateTime;
 				pipelineContext.messageID = text2;
 				pipelineContext.WriteHeaderFile(headerFile);
 				result = pipelineContext;
 			}
-			catch (IOException ex)
+			catch (IOException ex2)
 			{
 				CallIdTracer.TraceDebug(ExTraceGlobals.VoiceMailTracer, 0, "Failed to parse the header file {0} because its not closed by thread creating the file.  Error={1}", new object[]
 				{
 					headerFile,
-					ex
+					ex2
 				});
 				if (pipelineContext != null)
 				{
 					pipelineContext.Dispose();
 					pipelineContext = null;
 				}
 				result = null;
 			}
-			catch (InvalidObjectGuidException ex2)
+			catch (InvalidObjectGuidException ex3)
 			{
 				CallIdTracer.TraceWarning(ExTraceGlobals.VoiceMailTracer, 0, "Couldn't find the recipient for this message. Error={0}", new object[]
 				{
-					ex2
+					ex3
 				});
 				if (pipelineContext != null)
 				{
 					pipelineContext.Dispose();
 					pipelineContext = null;
 				}
 				throw;
 			}
-			catch (InvalidTenantGuidException ex3)
+			catch (InvalidTenantGuidException ex4)
 			{
 				CallIdTracer.TraceWarning(ExTraceGlobals.VoiceMailTracer, 0, "Couldn't find the tenant for this message. Error={0}", new object[]
 				{
-					ex3
+					ex4
 				});
 				if (pipelineContext != null)
 				{
 					pipelineContext.Dispose();
 					pipelineContext = null;
 				}
 				throw;
 			}
-			catch (NonUniqueRecipientException ex4)
+			catch (NonUniqueRecipientException ex5)
 			{
 				CallIdTracer.TraceWarning(ExTraceGlobals.VoiceMailTracer, 0, "Multiple objects found for the recipient. Error={0}", new object[]
 				{
-					ex4
+					ex5
 				});
 				if (pipelineContext != null)
 				{
 					pipelineContext.Dispose();
 					pipelineContext = null;
 				}
 				throw;
 			}
 			return result;
 		}

 		internal abstract void WriteCustomHeaderFields(StreamWriter headerStream);

 		public abstract string GetMailboxServerId();

 		public abstract string GetRecipientIdForThrottling();

 		internal virtual void SaveMessage()
 		{
 			this.WriteHeaderFile(this.HeaderFileName);
 		}

 		protected override void InternalDispose(bool disposing)
 		{
 			if (disposing)
 			{
 				CallIdTracer.TraceDebug(ExTraceGlobals.VoiceMailTracer, this.GetHashCode(), "PipelineContext.Dispose() called", Array.Empty<object>());
 			}
 		}

 		protected override DisposeTracker InternalGetDisposeTracker()
 		{
 			return DisposeTracker.Get<PipelineContext>(this);
 		}

 		protected virtual void SetMessageProperties()
 		{
 			IUMResolveCaller iumresolveCaller = this as IUMResolveCaller;
 			if (iumresolveCaller != null)
 			{
 				ExAssert.RetailAssert(iumresolveCaller.ContactInfo != null, "ResolveCallerStage should always set the ContactInfo.");
 				UMSubscriber umsubscriber = ((IUMCAMessage)this).CAMessageRecipient as UMSubscriber;
 				UMDialPlan dialPlan = (umsubscriber != null) ? umsubscriber.DialPlan : null;
 				Microsoft.Exchange.UM.UMCommon.PhoneNumber pstnCallbackTelephoneNumber = this.CallerId.GetPstnCallbackTelephoneNumber(iumresolveCaller.ContactInfo, dialPlan);
 				this.messageToSubmit.From = iumresolveCaller.ContactInfo.CreateParticipant(pstnCallbackTelephoneNumber, this.CultureInfo);
 				XsoUtil.SetVoiceMessageSenderProperties(this.messageToSubmit, iumresolveCaller.ContactInfo, dialPlan, this.CallerId);
 				this.messageToSubmit.InternetMessageId = BoomerangHelper.FormatInternetMessageId(this.MessageID, Utils.GetHostFqdn());
 				this.messageToSubmit[ItemSchema.SentTime] = this.SentTime;
 			}
 			this.messageToSubmit.AutoResponseSuppress = AutoResponseSuppress.All;
 			this.messageToSubmit[MessageItemSchema.CallId] = this.helper.CallId;
 			IUMCAMessage iumcamessage = this as IUMCAMessage;
 			if (iumcamessage != null)
 			{
 				this.MessageToSubmit.Recipients.Add(new Participant(iumcamessage.CAMessageRecipient.ADRecipient));
 				IADSystemConfigurationLookup iadsystemConfigurationLookup = ADSystemConfigurationLookupFactory.CreateFromOrganizationId(iumcamessage.CAMessageRecipient.ADRecipient.OrganizationId);
 				this.MessageToSubmit.Sender = new Participant(iadsystemConfigurationLookup.GetMicrosoftExchangeRecipient());
 			}
 		}

 		protected void WriteHeaderFile(string headerFileName)
 		{
 			using (FileStream fileStream = File.Open(headerFileName, FileMode.Create, FileAccess.Write, FileShare.None))
 			{
 				using (StreamWriter streamWriter = new StreamWriter(fileStream))
 				{
 					if (this.MessageType != null)
 					{
 						streamWriter.WriteLine("MessageType : " + this.MessageType);
 					}
 					streamWriter.WriteLine("ProcessedCount : " + this.processedCount.ToString(CultureInfo.InvariantCulture));
 					if (this.messageID != null)
 					{
 						streamWriter.WriteLine("MessageID : " + this.messageID);
 					}
 					if (this.sentTime.Year != 1)
 					{
 						streamWriter.WriteLine("SentTime : " + this.sentTime.ToString(CultureInfo.InvariantCulture));
 					}
 					this.WriteCommonHeaderFields(streamWriter);
 					this.WriteCustomHeaderFields(streamWriter);
 				}
 			}
 		}

 		protected virtual void WriteCommonHeaderFields(StreamWriter headerStream)
 		{
 			if (!this.CallerId.IsEmpty)
 			{
 				headerStream.WriteLine("CallerId : " + this.CallerId.ToDial);
 			}
 			if (this.helper.RecipientName != null)
 			{
 				headerStream.WriteLine("RecipientName : " + this.helper.RecipientName);
 			}
 			if (this.helper.RecipientObjectGuid != Guid.Empty)
 			{
 				headerStream.WriteLine("RecipientObjectGuid : " + this.helper.RecipientObjectGuid.ToString());
 			}
 			if (this.helper.CallerName != null)
 			{
 				headerStream.WriteLine("CallerNAme : " + this.helper.CallerName);
 			}
 			if (!string.IsNullOrEmpty(this.helper.CallerIdDisplayName))
 			{
 				headerStream.WriteLine("CallerIdDisplayName : " + this.helper.CallerIdDisplayName);
 			}
 			if (this.CallerAddress != null)
 			{
 				headerStream.WriteLine("CallerAddress : " + this.CallerAddress);
 			}
 			if (this.helper.CultureInfo != null)
 			{
 				headerStream.WriteLine("CultureInfo : " + this.helper.CultureInfo);
 			}
 			if (this.helper.CallId != null)
 			{
 				headerStream.WriteLine("CallId : " + this.helper.CallId);
 			}
 			IUMResolveCaller iumresolveCaller = this as IUMResolveCaller;
 			if (iumresolveCaller != null && iumresolveCaller.ContactInfo != null)
 			{
 				headerStream.WriteLine("ContactInfo : " + CommonUtil.Base64Serialize(iumresolveCaller.ContactInfo));
 			}
 			headerStream.WriteLine("TenantGuid : " + this.helper.TenantGuid.ToString());
 		}

 		protected UMRecipient CreateRecipientFromObjectGuid(Guid objectGuid, Guid tenantGuid)
 		{
 			return UMRecipient.Factory.FromADRecipient<UMRecipient>(this.CreateADRecipientFromObjectGuid(objectGuid, tenantGuid));
 		}

 		protected ADRecipient CreateADRecipientFromObjectGuid(Guid objectGuid, Guid tenantGuid)
 		{
 			if (objectGuid == Guid.Empty)
 			{
 				throw new HeaderFileArgumentInvalidException("ObjectGuid is empty");
 			}
 			ADRecipient adrecipient = ADRecipientLookupFactory.CreateFromTenantGuid(tenantGuid).LookupByObjectId(new ADObjectId(objectGuid));
 			if (adrecipient == null)
 			{
 				CallIdTracer.TraceDebug(ExTraceGlobals.VoiceMailTracer, 0, "Could not find recipient {0}", new object[]
 				{
 					objectGuid.ToString()
 				});
 				throw new InvalidObjectGuidException(objectGuid.ToString());
 			}
 			return adrecipient;
 		}

 		protected UMDialPlan InitializeCallerIdAndTryGetDialPlan(UMRecipient recipient)
 		{
 			UMDialPlan umdialPlan = null;
 			if (this.CallerId.UriType == UMUriType.E164 && recipient.ADRecipient.UMRecipientDialPlanId != null)
 			{
 				umdialPlan = ADSystemConfigurationLookupFactory.CreateFromADRecipient(recipient.ADRecipient).GetDialPlanFromId(recipient.ADRecipient.UMRecipientDialPlanId);
 				if (umdialPlan != null && umdialPlan.CountryOrRegionCode != null)
 				{
 					this.helper.CallerId = this.helper.CallerId.Clone(umdialPlan);
 				}
 			}
 			return umdialPlan;
 		}

 		protected string GetMailboxServerIdHelper()
 		{
 			IUMCAMessage iumcamessage = this as IUMCAMessage;
 			if (iumcamessage != null)
 			{
 				UMMailboxRecipient ummailboxRecipient = iumcamessage.CAMessageRecipient as UMMailboxRecipient;
 				if (ummailboxRecipient != null)
 				{
 					return ummailboxRecipient.ADUser.ServerLegacyDN;
 				}
 			}
 			return "af360a7e-e6d4-494a-ac69-6ae14896d16b";
 		}

 		protected string GetRecipientIdHelper()
 		{
 			IUMCAMessage iumcamessage = this as IUMCAMessage;
 			if (iumcamessage != null)
 			{
 				UMMailboxRecipient ummailboxRecipient = iumcamessage.CAMessageRecipient as UMMailboxRecipient;
 				if (ummailboxRecipient != null)
 				{
 					return ummailboxRecipient.ADUser.DistinguishedName;
 				}
 			}
 			return "455e5330-ce1f-48d1-b6b1-2e318d2ff2c4";
 		}

 		private MessageItem messageToSubmit;

 		private SubmissionHelper helper;

 		private string messageType;

 		private CultureInfo cultureInfo;

 		private string headerFileName;

 		private int processedCount;

 		private string messageID;

 		private ExDateTime sentTime;
+
+		private static Type[] contactInfoDeserializationAllowList = new Type[]
+		{
+			typeof(Version),
+			typeof(Guid),
+			typeof(PropTag),
+			typeof(ContactInfo),
+			typeof(ADContactInfo),
+			typeof(FoundByType),
+			typeof(ADUser),
+			typeof(ADPropertyBag),
+			typeof(ValidationError),
+			typeof(ADPropertyDefinition),
+			typeof(ADObjectId),
+			typeof(ExchangeObjectVersion),
+			typeof(ExchangeBuild),
+			typeof(MultiValuedProperty<string>),
+			typeof(LocalizedString),
+			typeof(ProxyAddressCollection),
+			typeof(SmtpAddress),
+			typeof(RecipientDisplayType),
+			typeof(RecipientTypeDetails),
+			typeof(ElcMailboxFlags),
+			typeof(UserAccountControlFlags),
+			typeof(ObjectState),
+			typeof(DirectoryBackendType),
+			typeof(MServPropertyDefinition),
+			typeof(MbxPropertyDefinition),
+			typeof(MbxPropertyDefinitionFlags),
+			typeof(OrganizationId),
+			typeof(PartitionId),
+			typeof(SmtpProxyAddress),
+			typeof(SmtpProxyAddressPrefix),
+			typeof(ByteQuantifiedSize),
+			typeof(Unlimited<ByteQuantifiedSize>),
+			typeof(List<ValidationError>),
+			typeof(ADMultiValuedProperty<TextMessagingStateBase>),
+			typeof(ADMultiValuedProperty<ADObjectId>),
+			typeof(StoreObjectId),
+			typeof(StoreObjectType),
+			typeof(EntryIdProvider),
+			typeof(SimpleContactInfoBase),
+			typeof(MultipleResolvedContactInfo),
+			typeof(CallerNameDisplayContactInfo),
+			typeof(PersonalContactInfo),
+			typeof(DefaultContactInfo),
+			typeof(UMDialPlan),
+			typeof(UMEnabledFlags),
+			Type.GetType("Microsoft.Exchange.Data.ByteQuantifiedSize+QuantifierProvider, Microsoft.Exchange.Data"),
+			Type.GetType("System.UnitySerializationHolder, mscorlib"),
+			Type.GetType("Microsoft.Exchange.Data.ByteQuantifiedSize+Quantifier,Microsoft.Exchange.Data"),
+			Type.GetType("Microsoft.Exchange.Data.PropertyBag+ValuePair, Microsoft.Exchange.Data"),
+			Type.GetType("System.Collections.Generic.List`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]"),
+			typeof(DialByNamePrimaryEnum),
+			typeof(DialByNameSecondaryEnum),
+			typeof(AudioCodecEnum),
+			typeof(UMUriType),
+			typeof(UMSubscriberType),
+			typeof(UMGlobalCallRoutingScheme),
+			typeof(UMVoIPSecurityType),
+			typeof(SystemFlagsEnum),
+			typeof(EumProxyAddress),
+			typeof(EumProxyAddressPrefix)
+		};
 	}
 }

The patch appears to add and use a typed allowlist for deserialization of a voicemail’s contact info, which is found in a header file alongside the voicemail itself. Other seemingly unprotected deserializations can be seen in the same class. (I think it’s just XML parsing.) My suspicion is that CVE-2021-26858 or CVE-2021-27065 could be used to write a malicious header file to C:\Program Files\Microsoft\Exchange Server\V15\UnifiedMessaging\voicemail, but it’s entirely possible a crafted voicemail could be sent instead. While I haven’t developed a PoC yet, I do have a good idea how to, assuming the patch analysis is correct. Better-resourced attackers should be able to exploit this issue in considerably less time.

The specifically patched code can be seen below:

[snip]
									else
									{
										if (!(text4 == "ContactInfo"))
										{
											goto IL_409;
										}
										Exception ex = null;
										try
										{
											try
											{
												using (MemoryStream memoryStream = new MemoryStream(Convert.FromBase64String(array[1])))
												{
													contactInfo = (ContactInfo)TypedBinaryFormatter.DeserializeObject(memoryStream, PipelineContext.contactInfoDeserializationAllowList, null, true);
												}
											}
											catch (ArgumentNullException ex)
											{
											}
											catch (SerializationException ex)
											{
											}
											catch (Exception ex)
											{
											}
											continue;
										}
										finally
										{
											if (ex != null)
											{
												CallIdTracer.TraceDebug(ExTraceGlobals.VoiceMailTracer, 0, "Failed to get contactInfo from header file {0} with Error={1}", new object[]
												{
													headerFile,
													ex
												});
											}
										}
									}
[snip]
[snip]
		private static Type[] contactInfoDeserializationAllowList = new Type[]
		{
			typeof(Version),
			typeof(Guid),
			typeof(PropTag),
			typeof(ContactInfo),
			typeof(ADContactInfo),
			typeof(FoundByType),
			typeof(ADUser),
			typeof(ADPropertyBag),
			typeof(ValidationError),
			typeof(ADPropertyDefinition),
			typeof(ADObjectId),
			typeof(ExchangeObjectVersion),
			typeof(ExchangeBuild),
			typeof(MultiValuedProperty<string>),
			typeof(LocalizedString),
			typeof(ProxyAddressCollection),
			typeof(SmtpAddress),
			typeof(RecipientDisplayType),
			typeof(RecipientTypeDetails),
			typeof(ElcMailboxFlags),
			typeof(UserAccountControlFlags),
			typeof(ObjectState),
			typeof(DirectoryBackendType),
			typeof(MServPropertyDefinition),
			typeof(MbxPropertyDefinition),
			typeof(MbxPropertyDefinitionFlags),
			typeof(OrganizationId),
			typeof(PartitionId),
			typeof(SmtpProxyAddress),
			typeof(SmtpProxyAddressPrefix),
			typeof(ByteQuantifiedSize),
			typeof(Unlimited<ByteQuantifiedSize>),
			typeof(List<ValidationError>),
			typeof(ADMultiValuedProperty<TextMessagingStateBase>),
			typeof(ADMultiValuedProperty<ADObjectId>),
			typeof(StoreObjectId),
			typeof(StoreObjectType),
			typeof(EntryIdProvider),
			typeof(SimpleContactInfoBase),
			typeof(MultipleResolvedContactInfo),
			typeof(CallerNameDisplayContactInfo),
			typeof(PersonalContactInfo),
			typeof(DefaultContactInfo),
			typeof(UMDialPlan),
			typeof(UMEnabledFlags),
			Type.GetType("Microsoft.Exchange.Data.ByteQuantifiedSize+QuantifierProvider, Microsoft.Exchange.Data"),
			Type.GetType("System.UnitySerializationHolder, mscorlib"),
			Type.GetType("Microsoft.Exchange.Data.ByteQuantifiedSize+Quantifier,Microsoft.Exchange.Data"),
			Type.GetType("Microsoft.Exchange.Data.PropertyBag+ValuePair, Microsoft.Exchange.Data"),
			Type.GetType("System.Collections.Generic.List`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]"),
			typeof(DialByNamePrimaryEnum),
			typeof(DialByNameSecondaryEnum),
			typeof(AudioCodecEnum),
			typeof(UMUriType),
			typeof(UMSubscriberType),
			typeof(UMGlobalCallRoutingScheme),
			typeof(UMVoIPSecurityType),
			typeof(SystemFlagsEnum),
			typeof(EumProxyAddress),
			typeof(EumProxyAddressPrefix)
		};
[snip]
2
Ratings
  • Attacker Value
    Very High
  • Exploitability
    Medium
Technical Analysis

Accellion’s legacy File Transfer Appliance (FTA) is an application to transfer large files securely. It is a 20-year-old product and will reach End of Life on April 30, 2021. Accellion recommends to migrate to kiteworks, its enterprise content firewall platform. According to this post, the SQL injection vulnerability is the starting point of a series of attacks against multiple organizations. This post reports that this vulnerability has been actively exploited since mid-December 2020 and is related to an ongoing ransomware campaign.

This SQL injection vulnerability enables an unauthenticated remote attacker to retrieve data from the database by sending specially crafted requests to the document_root file. Especifically, it has been exploited to retrieve a key that led to the installation of a web shell on the appliance. This web shell was then used to download sensitive data from the FTA internal database.

Due to the nature of this application, the data available is likely to be very sensitive and exploiting this vulnerability would lead to a critical information leak. As an emergency mitigation, external access to any vulnerable FTA should be shut down. However, this won’t block attacks coming from the internal network. It is highly recommended to patch to the latest version and to consider migrating to kiteworks.

4
Ratings
  • Attacker Value
    Very High
  • Exploitability
    Very High
Technical Analysis

Microsoft released details on an active state-sponsored threat campaign (attributed to HAFNIUM) that is exploiting on-prem Exchange Server installations. Microsoft’s observation was that these were limited, targeted attacks, but as of March 3, 2021, ongoing mass exploitation has been confirmed by multiple sources. More in the Rapid7 analysis tab.

Technical Analysis

On March 2, 2021, the Microsoft Threat Intelligence Center (MSTIC) released details on an active state-sponsored threat campaign leveraging four zero-day vulnerabilities to attack on-premises instances of Microsoft Exchange Server. In the attacks Microsoft observed, the threat actor used these vulnerabilities to access on-premises Exchange servers, which enabled access to email accounts and allowed installation of additional malware to facilitate long-term access to victim environments. Microsoft Threat Intelligence Center (MSTIC) attributes this campaign with high confidence to HAFNIUM, a group assessed to be state-sponsored and operating out of China, based on observed victimology, tactics and procedures.

The zero-day vulnerabilities disclosed on March 2, 2021 are:

  • CVE-2021-26855 is a server-side request forgery (SSRF) vulnerability in Exchange that allows an attacker to send arbitrary HTTP requests and authenticate as the Exchange server.
  • CVE-2021-26857 is an insecure deserialization vulnerability in the Unified Messaging service. Insecure deserialization is where untrusted user-controllable data is deserialized by a program. Exploiting this vulnerability gave HAFNIUM the ability to run code as SYSTEM on the Exchange server. This requires administrator permission or another vulnerability to exploit.
  • CVE-2021-26858 is a post-authentication arbitrary file write vulnerability in Exchange. If HAFNIUM could authenticate with the Exchange server then they could use this vulnerability to write a file to any path on the server. They could authenticate by exploiting the CVE-2021-26855 SSRF vulnerability or by compromising a legitimate admin’s credentials.
  • CVE-2021-27065 is a post-authentication arbitrary file write vulnerability in Exchange. If HAFNIUM could authenticate with the Exchange server then they could use this vulnerability to write a file to any path on the server. They could authenticate by exploiting the CVE-2021-26855 SSRF vulnerability or by compromising a legitimate admin’s credentials.

Microsoft has released out-of-band patches for all four vulnerabilities as of March 2, 2021.

Microsoft Exchange customers should apply the latest updates on an emergency basis and take steps to harden their Exchange instances. We strongly recommend that organizations monitor closely for suspicious activity and indicators of compromise (IOCs) stemming from this campaign. Rapid7 has a comprehensive list of IOCs available here.

For further information on the HAFNIUM-attributed threat campaign and related IOCs, see Microsoft’s blog here: https://www.microsoft.com/security/blog/2021/03/02/hafnium-targeting-exchange-servers/

Affected products

  • Exchange Server 2013
  • Exchange Server 2016
  • Exchange Server 2019

Security updates are available for the following specific versions of Exchange:

  • Exchange Server 2010 (for Service Pack 3 – this is a Defense in Depth update)
  • Exchange Server 2013 (CU 23)
  • Exchange Server 2016 (CU 19, CU 18)
  • Exchange Server 2019 (CU 8, CU 7)

Exchange Online is not affected.

Technical analysis and guidance

Microsoft said on March 2 that the attacks they observed were “limited and targeted.” Rapid7 detection and response teams, however, have been detecting indiscriminate exploitation of Exchange servers since February 27, 2021, including but not limited to the attacker behaviors below:

  • Attacker Tool – China Chopper Webshell Executing Commands
  • Attacker Technique – ProcDump Used Against LSASS

More information on attacker behaviors and a timeline of widespread Exchange exploitation is available here.

We strongly recommend that organizations both apply all updates for on-premises Exchange Server installations on an emergency basis and examine their environments for signs of compromise.