In June of 2021, Microsoft released a patch to correct CVE-2021-26420 – a remote code execution bug in the supported versions of Microsoft SharePoint Server. This bug was reported to the ZDI program by an anonymous researcher and is also known as ZDI-21-755. This blog takes a deeper look at the root cause of this vulnerability.
Before this patch being made available, this vulnerability could be used by an authenticated user to execute arbitrary .NET code on the server in the context and permissions of service account of SharePoint web application. For successful attack, the attacker should have “Manage Lists” permissions on any SharePoint site. By default, any authenticated user can create their own site where they have the necessary permissions.
The Vulnerability
SharePoint workflows are pre-programmed mini-applications that streamline and automate a wide variety of business processes. An organization can use workflows to attach business logic to documents or items in a SharePoint list or library.
For security purposes, Workflow Foundation (WF) on SharePoint runs workflows only when all dependent types and assemblies are authorized in the authorizedTypes list in the web.config file. Along with allowed types, this list also contains items that block specific types from allowed namespaces that are considered dangerous. One example is System.Workflow.ComponentModel.Compiler. The Compile() method offered by WorkflowCompiler compiles a workflow based on parameters. Workflows in SharePoint are normally compiled in very restricted context (no code, types restricted by the authorizedTypes list, etc.), but invoking WorkflowCompiler would allow attackers to create a new context without these restrictions by specifying appropriate parameters. Because of this, WorkflowCompiler is blocked by the authorizedTypes list in web.config:
Therefore, we cannot mount an attack using WorkflowCompiler type. However, examining the .NET code of WorkflowCompiler.Compile reveals something interesting:
From here we see that the bulk of the implementation of WorkflowCompiler.Compile resides a different class named WorkflowCompilerInternal. This class is not blocked by authorizedTypes list. Furthermore, since it is found within the `System.Workflow` namespace, it is allowed by the line:
Therefore, we can compile an arbitrary workflow by invoking the WorkflowCompilerInternal type directly.
For arbitrary code execution, we can use the following XOML file (Attck.xoml
). When compiled, it will execute the desired .NET commands, provided that the noCode flag is not specified:
Here is the .rules file (WF02.rules
) of a SharePoint workflow that invokes the WorkflowCompilerInternal.Compile to compile an arbitrary XOML workflow definition, provided that the XOML file can be retrieved from an SMB share:
This rule will be triggered by the following lines from the XOML file of our SharePoint workflow (WF02.xoml
):
An attacker could store Attck.xoml
in a shared SMB folder controlled by the attacker and upload the WF02.xoml
and WF02.rules
files to a location on the SharePoint site, for example, to the Documents folder. Also, the attacker would need a workflow configuration file (WF02config.xml
) that points to the uploaded XOML and rules files, and associates them with the ListID of a list in the site where the attacker can add new items:
An attacker would need to associate this new workflow with the SharePoint list. This can be done by using the AssociateWorkflowMarkup method from the WebPartPages WebService (/_vti_bin/WebPartPages.asmx) and providing the relative path to the uploaded config file.
To trigger workflow execution, the attacker adds a new item to the target SharePoint list.
Proof of Concept
For this demonstration, we use Microsoft SharePoint Server 2019 installed with all default options on Windows Server 2019 Datacenter. The server’s computer name is sp2019.contoso.lab
and it is a member of the contoso.lab
domain. The domain controller is a separate virtual machine. It has been updated to the February 2021 patch level (version 16.0.10371.20043) and a couple of users have been added, including “user2” as a regular, unprivileged user.
On the attacker side, we need:
-- an SMB server hosting the Attck.xoml file containing the .NET code we want to execute. It must be hosted on a share that can be accessed by the SharePoint server
-- our PoC application for sending SOAP requests to the server (SP_soap_RCE_PoC.exe) that calls the AssociateWorkflowMarkup method of the WebPartPages web service
-- the workflow config file, WF02config.xml
-- The XOML and .rules
files referenced by the workflow config file
-- any supported web browser. For this demonstration, we are using Firefox.
Achieving Remote Code Execution
Let’s visit our SharePoint Server and authenticate as a regular user. In my case it is user2:
Before our attack, we need to prepare a few things. We could try to do this with current user sites, but if possible, we recommend creating a new site so that we will be the owner and have all permissions.
Click on “SharePoint” on the top panel:
Now click the “+ Create site” link:
Choose Team Site.
Pick a name for the new site. Here we use testsiteofuser2.
Click “Finish” and the new site will be created:
Now we need to create a new list. Click on the “Site contents” link:
Then click “+ New” -> “List”:
Enter any available name. For example, “NewList”:
We will need the ListID of this new list. Let’s go back to the “Site contents” page and click on “Settings” in the dropdown menu for our new list:
We will be redirected to the listedit.aspx page:
We need value of the List URL parameter. In my case it is:
28e11fd2-9f76-418c-83ea-dc0d8f3c184b
We need to place this value into the ListId attribute in the workflow config file, WF02config.txt:
Now we need to upload the Attck.xoml file, containing the .NET code we want to run, into an SMB folder that is accessible from the SharePoint server.
We should place the remote path to the Attck.xoml file into WF02.rules:
Save changes in both files (WF02config.txt and WF02.rules) and upload them together with the WF02.xoml file to the Documents folder of our SharePoint site:
We are almost ready for the final step. But first, let’s check the C:\windows\temp folder on our target SharePoint server:
There is no PoC_SPRCE02.txt file yet.
For our attack, we will use the SP_soap_RCE_PoC.exe application. We need to provide the following information:
— BaseUrl of the target SharePoint Site (in this case http://sp2019/sites/testsiteofuser2/)
— UserName (in this case user2)
— Password
— Domain
— Relative path to the workflow config file on our site - "Shared Documents/WF02config.txt"
We received a “Success” result. Now to trigger the RCE, we just need to add a new item to NewList:
Click on the “Save” button there.
Let’s check C:\windows\temp folder on our target SharePoint server:
The file PoC_SPRCE02.txt has been created, demonstrating the success of our code execution. In this manner, our attacker can execute arbitrary OS commands in the context of the SharePoint web application account.
To run additional OS commands, we would need to place them into Attck.xoml file in the shared folder and create another new item in NewList.
Conclusion
Microsoft patched this in June and assigned identifier CVE-2021-26420, with a CVSS score of 7.1. While the inclusion of an attacker-controlled SMB server adds a level of complexity, this bug can still be used for code execution in many scenarios. Since SharePoint is generally considered an attractive target for attackers, it would not be surprising if this bug were to be found in the wild. SharePoint continues to be an attractive target for security researchers, and many SharePoint-related disclosures are currently in our Upcoming queue. Stay tuned to this blog for details about those bugs once they are disclosed.
Until then, follow the team for the latest in exploit techniques and security patches.