Yet Another .NET deserialization

frycos
3 min readDec 29, 2019

--

This is my second post on white-box analysis but for another technology stack and vulnerability category: .NET deserialization leading to Remote Code Execution. The outcome for this self-paced training was CVE-2019-18211 with an outstanding product vendor (friendly/fast communication and quick bugfixes available since 30th October 2019).

According to the product authors’ website (https://c1.orckestra.com/), C1 CMS is one of the top rated open source CMS worldwide built on the Microsoft stack with more than 85.000 installations (on-premise and in cloud infrastructures).

After installation of the latest C1 CMS package (back then v6.6) via Microsoft Webmatrix installer, a running instance was dynamically investigated with live debugging (using: dnSpy). The standard administrator account of C1 CMS was used to add several lower privilege accounts e.g. holding Editor and Developer roles.

C1 CMS used webservice calls based on SOAP requests a lot. These request structures per service could easily be enumerated via WSDL files (observed via BurpSuite), provided during normal browsing of the web application.

A bottom-up approach was used to directly search for well-known .NET deserialization issues, namely looking for user-controlled input resulting in insecure deserialization. C1 CMS seemed to prefer JSON objects being sent through SOAP requests. No unsafe deserialization for these kind of objects were found (e.g. TypeNameHandling settings). Technically, deserialization candidates were searched simply by looking for Deserialize methods of loaded .NET assemblies. In the following, we focused on the TreeServiceFacade and EntityTokenSerializer class.

The TreeServiceFacade provided a method GetMultipleChildren(…) which was callable via a SOAP request. This request contained parameters referencing EntityTokens being de/serialized in the backend.

Interestingly, a legacy deserialization path was provided if the incoming object is not based on JSON. The serializedEntityToken parameter in EntityTokenSerializer was parsed with respect to a regular expression:

_keyValuePairRegEx = new Regex(\\s*(?<Key>[^=\\s]*)\\s*=\\s*(?<IsNull>null|(?<Value>[^\\\\\\r\\n]*(\\\\.[^\\\\\\r\\n]*)*))\\s*,*\\s*, RegexOptions.Compiled)

Starting from this regular expression, valid objects could be built manually. Now, the concrete vulnerability happened to be in the non-JSON part of the class EntityTokenSerializer. With the parameter entityTokenType one can fully control which .NET assembly will be used as a type for deserialization. Afterwards, the code searched for a Deserialize method for this assembly name and invoked this method via Reflection.

The only restriction for this was that the serialized object came in as a String. Looking for a proper Formatter lead us to the well-known BinaryFormatter which was used under the hood from Microsoft.Practices.EnterpriseLibrary.Logging.Formatters.BinaryLogFormatter. So using the Deserialize function of this assembly allowed a String parameter which was base64 decoded and afterwards fed into the BinaryFormatter deserialization call.

All what was left was creating a malicious payload with the tool ysoserial.NET (https://github.com/pwntester/ysoserial.net). As a proof-of-concept the following command was used:

ysoserial.exe -g TypeConfuseDelegate -f BinaryFormatter -c "cmd.exe /C echo pwned > C:\Users\Public\Downloads\PWNED.txt" -o base64

Delivering the following request as a user with Editor role (low privilege user) containing entityTokenType='Microsoft.Practices.EnterpriseLibrary.Logging.Formatters.BinaryLogFormatter' and entityToken=BINARYFORMATTERPAYLOAD lead to a Remote Code Execution on server-side.

Basically, the role didn’t matter. Any authenticated user was able to achieve Remote Code Execution within the CMS console this way.

--

--