# Some notes about Microsoft Exchange Deserialization RCE (CVE-2021-42321)
# **INTRO**
It's been several months since our last story about [**ProxyShell
Exploit**](/reproducing-the-proxyshell-pwn2own-exploit-49743a4ea9a1) and
recently Exchange was pwned again at Tianfu Cup 2021. We're very excited about
that Exploit and we're waiting for Tuesday Patch of MS Exchange this month to
analyse it.
There's already a [blog ](https://blog.khonggianmang.vn/phan-tich-ban-va-
thang-11-cua-microsoft-exchange/)analysis about CVE-2021-42321 and just
published yesterday. But we think that blog didn't cover enough technical
information and highlight notes so we decided to write this blog in English to
let everyone understand what happened inside this CVE!
From the advisory of Microsoft, it stated that this CVE is a post-auth RCE. We
just wonder that is a pre-auth RCE because it costs $200.000 when you have a
successful demonstration at Tianfu Cup 2021. But with the patch from MS we
only know that MS patch the post-auth RCE, maybe MS let the customer have time
to patch the post-auth RCE and later release another patch for an auth bypass
vulnerability?

<https://msrc.microsoft.com/update-guide/vulnerability/CVE-2021-42321>
If we look carefully at the advisory of Microsoft we can notice that only
Exchange 2016 CU 21,22 and Exchange 2019 CU 10,11 . This means the only recent
latest version of Exchange 2016,2019 are vulnerable to this CVE

<https://msrc.microsoft.com/update-guide/vulnerability/CVE-2021-42321>
Microsoft also release a patch for Exchange 2016,2019 before the Tianfu Cup
happened and Exchange was pwned after this patch, so we need to diff the patch
October and November 2021.

<https://www.catalog.update.microsoft.com/Search.aspx?q=Exchange%202016>
After decompiling with DnSpy and diffing with Win Merge we got about 275 files
were changed (╯°□°)╯︵ ┻━┻.

The patch almost patching for Deserialization Sink inside Exchange, so we can
be sure that this time it's a deserialization vulnerability.
# **THE SINK**
We noticed that some files were removed

There's a funny point inside **TypedBinaryFormatter** is when deserialize the
**binder** value was not passed correctly to
**ExchangeBinaryFormatterFactory**. As we already know that
**SerializationBinder** is used to control the actual types used during
serialization and deserialization. This can be a mechanism deserialization
vulnerability but in **TypedBinaryFormatter** we have a chance for it again :)
.

Microsoft.Exchange.Compliance.Serialization.Formatters.TypedBinaryFormatter
Without the **binder** variable pass to
**ExchangeBinaryFormatterFactory.CreateBinaryFormatter()** ,
**ExchangeBinaryFormatterFactory** will use **ChainedSerializationBinder** as
**SerializationBinder** to validate **** actual types used during
deserialization

Microsoft.Exchange.Diagnostics.ExchangeBinaryFormatterFactory.CreateBinaryFormatter()
When passing the argument from **TypedBinaryFormatter.Deserialize()** to
**ExchangeBinaryFormatterFactory.CreateBinaryFormatter()** Exchange initialize
**ChainedSerializationBinder** (implement SerializationBinder) **** with:
* strictMode = **false**
* allowList = System.DelegateSerializationHolder
* allowedGenerics = null
Let's have a look at **ChainedSerializationBinder.BindToType()** which will
validate the class for Deserialization.

ChainedSerializationBinder.BindToType()

ChainedSerializationBinder.ValidateTypeToDeserialize()
**For Block 1:**
* If the strictMode is **False** and our class is not in allow list, and in blacklist , Exchange will throw **InvalidOperationException**
* In this function, it only catches **BlockedDeserializationException** so **InvalidOperationException** will not be caught, so if Exchange through **InvalidOperationException** our deserialization chain will be broken
**For Block 2:**
* If there's a **BlockedDeserializationException** while ValidateTypeToDeserialize() was thrown, it will be caught in catch block but Exchange only throws that Exception when the value of the flag is True, but remembered that the strictMode value was passed to **ChainedSerializationBinder** is always False ! So there's Exchange will catch **BlockedDeserializationException** safely and Exchange didn't affect our Deserialization can continue without crashing.


Alright, now let's have a look at **ChainedSerializationBinder 's **blacklist.
It's a big mistake of Exchange developers …

When deserialize ClaimsPrincipal, it also triggers another
BinaryFormatter.Deserialize() without any filter. At this time, we can be sure
that we found the Sink of this CVE
System.Security.Claims.ClaimsPrincipal.OnDeserializedMethod()
System.Security.Claims.ClaimsPrincipal.DeserializeIdentities()
BinaryFormatter.Deserialize()
# **THE SOURCE**
With Dnspy we tracing back to find where we can trigger deserialization

After some handy steps we found that, we can go from
**OrgExtensionSerializer**. ** _TryDeserialize()_** to
**ClientExtensionCollectionFormatter**. ** _Deserialize()_**

It gets the Stream from userConfiguration to deserialize it and sound like
with a normal user we can set that setting also.

After a little bit of playing around with how can we set the
**userConfiguration** from HTTP request we found this document from Microsoft
[
## CreateUserConfiguration operation
### The CreateUserConfiguration operation creates a user configuration object
on a folder. The following example of a…
docs.microsoft.com
](https://docs.microsoft.com/en-us/exchange/client-developer/web-service-
reference/createuserconfiguration-operation)
But from the public document, we can't find how to set Stream into
userConfiguration so we dive into the logic of EWS to see how we can set it

We clearly see that there's a XML node name "BinaryData", which sound like the
serialization data we want to set
**CreateUserConfiguration**. _Execute()
_**UserConfigurationCommandBase**. _SetProperties()
_**UserConfigurationCommandBase**. _SetStream()_

**UserConfigurationCommandBase**. _SetProperties()_

**UserConfigurationCommandBase**. _SetStream()_
Basically, our request to EWS looks like this

sample request for setting BinaryData
Now we can set **userConfiguration** but how to trigger the actual
deserialization sink?
Tracing back from **OrgExtensionSerializer**. ** _TryDeserialize()_** _we can
reach to_ **GetClientAccessToken**

With this API from MS documents, now we can trigger the deserialization sink
[
## GetClientAccessToken operation
### Find information about the GetClientAccessToken EWS operation. The
GetClientAccessToken operation gets a client access…
docs.microsoft.com
](https://docs.microsoft.com/en-us/exchange/client-developer/web-service-
reference/getclientaccesstoken-operation)

sample request for **GetClientAccessToken**
# **FULL EXPLOIT**
We finally achieve post-auth RCE:
1. Create UserConfiguration with BinaryData as our Gadget Chain
2. Request to EWS for GetClientAccessToken to trigger the Deserialization
**About the gadget chain:**
* We can embed any ysoserial.net gadget chain into **System.Security.Claims.ClaimsPrincipal** to trigger 2nd Deserialization
* Or we can use directly use **TypeConfusedDelegate** Chain (This chain is not in the blacklist of **ChainedSerializationBinder** and I don't know why :> )
# **IMPROVEMENT**
We already can pop calc on our Lab environment but how about the actual
environment with up to date Windows Defender?

This is what we got :)
Since ProxyLogon, ProxyShell, and till now some EDRs,AV,sysmon and Microsoft
Windows Defender try to catch and prevent process spawn from w3wp.exe process.
This also annoys us but we need some improvements to overcome it!

As we know that we can use **ClaimsPrincipal** or **TypeConfusedDelegate**
gadget chain to achieve post-auth RCE, but directly executing cmd.exe is not a
good idea
With some improvements from @zcgonvh to ysoserial.net , gadget
**ActivitySurrogateSelector** can use to load a DLL via **Assembly.Load()**
function. The whole idea is from @zcgonvh blog also.
[
## 草泥马之家-CVE-2020-17144漏洞分析与武器化
### 0x00 前言
和CVE-2018-8302、CVE-2020-0688类似,CVE-2020-17144同属需登录后利用的反序列化漏洞,但仅影响Exchange2010服务器。
...
www.zcgonvh.com
](http://www.zcgonvh.com/post/analysis_of_CVE-2020-17144_and_to_weaponizing.html)
This helps us achieve mem-shell when exploiting .net deserialization. Instead
of calling Process.Start() now we move on eval JScript. With JScript, we can
do many things with scripting instead of spawning cmd.exe / powershell.exe.

A snippet of code to eval JScript
Next, we write a new plugin for ysoserial.net with **ClaimsPrincipal** (this
is almost the same with ClaimsIdentity) then put **ActivitySurrogateSelector**
object into **ClaimsPrincipal** gadget chain.
But **ActivitySurrogateSelector** has some limitations with .NET 4.8 and later


Shout out to [@monoxgas](https://twitter.com/monoxgas) for
**ActivitySurrogateDisableTypeCheck** gadget chain to disable the protection
for **ActivitySurrogateSelector**
Everything we need now is to put **ActivitySurrogateDisableTypeCheck** into
**ClaimsPrincipal**

_We 've been laughing for hours while making this meme_
The main idea of **ActivitySurrogateDisableTypeCheck** gadget chain is when
deserializing it will call _GetObjectFromSerializationInfo()_ and finnally
reach to XamlReader.Parse() and run our Xaml payload to set value for
**DisableActivitySurrogateSelectorTypeCheck**


Our Xaml payload was executed but …

There's an **InvalidCastException** was thrown before we can set
**DisableActivitySurrogateSelectorTypeCheck** to True, and this Exception was
not caught by Exchange so this chain won't work and we cannot change
**DisableActivitySurrogateSelectorTypeCheck** because of crashing w3wp.exe
process!
So we can't use **ActivitySurrogateDisableTypeCheck** gadget chain. Now we
need to find another gadget chain or another way to call
**XamlReader.Parse()**
Remember that we can use **TypeConfusedDelegate**?
With **TypeConfusedDelegate** we can invoke the method arbitrarily ( ͡° ͜ʖ ͡°)

This is how we call Process.Start()
How about **XamlReader.Parse()** ? **XamlReader.Parse** is a public/static
also, so we can easily call it with **TypeConfusedDelegate**

from TypeConfusedDelegate -> XamlReader.Parse()
Finally, everything works as our expected, we can change
**DisableActivitySurrogateSelectorTypeCheck** to True to overcome the
limitation of .NET and later inject DLL to achieve mem-shell with Jscript to
bypass the detection
PoC video:
PoC: We don't think we will …
# **Final Thought**
This exploit only work for
* Microsoft Exchange 2019 CU10, 11
* Microsoft Exchange 2016 CU21, 22
And didn't affect another version, who knows what happened behind the scenes
with a typo mistake on **ChainedSerializationBinder** ¯\\_(ツ)_/¯
暂无评论