CVSS2
Attack Vector
NETWORK
Attack Complexity
LOW
Authentication
NONE
Confidentiality Impact
PARTIAL
Integrity Impact
PARTIAL
Availability Impact
PARTIAL
AV:N/AC:L/Au:N/C:P/I:P/A:P
CVSS3
Attack Vector
NETWORK
Attack Complexity
LOW
Privileges Required
NONE
User Interaction
NONE
Scope
UNCHANGED
Confidentiality Impact
HIGH
Integrity Impact
HIGH
Availability Impact
NONE
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N
AI Score
Confidence
High
EPSS
Percentile
100.0%
Microsoft Exchange Server Remote Code Execution Vulnerability
Recent assessments:
wvu-r7 at March 03, 2021 6:59pm UTC reported:
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]
Assessed Attacker Value: 5
Assessed Attacker Value: 5Assessed Attacker Value: 3
CVSS2
Attack Vector
NETWORK
Attack Complexity
LOW
Authentication
NONE
Confidentiality Impact
PARTIAL
Integrity Impact
PARTIAL
Availability Impact
PARTIAL
AV:N/AC:L/Au:N/C:P/I:P/A:P
CVSS3
Attack Vector
NETWORK
Attack Complexity
LOW
Privileges Required
NONE
User Interaction
NONE
Scope
UNCHANGED
Confidentiality Impact
HIGH
Integrity Impact
HIGH
Availability Impact
NONE
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N
AI Score
Confidence
High
EPSS
Percentile
100.0%