CVE-2019-0604: Details of a Microsoft SharePoint RCE VulnerabilityMarch 13, 2019 | Guest Blogger
Last month, Microsoft released patches to address two remote code execution (RCE) vulnerabilities in SharePoint. In both Critical-rated cases, an attacker could send a specially crafted request to execute their code in the context of the SharePoint application pool and the SharePoint server farm account. Both of these bugs were reported to the ZDI program by Markus Wulftange. He has graciously provided the following write-up on the details of CVE-2019-0604.
When searching for new vulnerabilities, one approach is the bottom-up approach. It describes the approach of looking for an interesting sink and tracing the control and data flow backwards to find out if the sink can be reached.
One of these promising sinks is the deserialization using the XmlSerializer. In general, it is considered a secure serializer as it must be instrumented with the expected type and it is not possible to specify an arbitrary type within the stream that cannot appear in the object graph of the expected type. But it is exploitable if the expected type can be controlled as well, as it has been shown in Friday the 13th – JSON Attacks by Alvaro Muñoz & Oleksandr Mirosh [PDF].
For analyzing the SharePoint 2016 assemblies, dnSpy is an excellent tool as it can be used for both decompiling and debugging of .NET applications. So, after dnSpy is attached to the IIS worker process w3wp.exe that is running SharePoint 2016, and the assemblies have been loaded, the usage of the
XmlSerializer(Type) constructor can be analyzed. Now the tedious part begins where every one of the
XmlSerializer(Type) constructor calls has to be looked at and to check whether the expected type is variable at all (e.g. it is not hard-coded as in
new XmlSerializer(typeof(DummyType))) and whether it is possible to control the type.
One of the methods where the
XmlSerializer(Type) constructor gets called is the
Microsoft.SharePoint.BusinessData.Infrastructure.EntityInstanceIdEncoder.DecodeEntityInstanceId(string) method in Microsoft.SharePoint.dll. The same type with the same functionality is also in the
Microsoft.Office.Server.ApplicationRegistry.Infrastructure namespace in the Microsoft.SharePoint.Portal.dll. We will come back to this later and stick to the one in Microsoft.SharePoint.dll.
Here both the
typeName, used to specify the expected type, and the data that gets deserialized originate from
text, which originates from the method's argument
This looks perfect as long as the method gets actually called and the passed parameter can be controlled.
Tracing back the Flow to the Source
The next step is to go through the calls and see if the one of them originates from a point that can be initiated from outside and whether the argument value can also be supplied.
If you’re familiar with the ASP.NET, some of the methods might look familiar like
Page_Load(object, EventArgs) or
OnLoad(EventArgs). They are called during the ASP.NET life cycle, and the types they are defined in extend System.Web.UI.Page, the base type that represents
.aspx files. And, in fact, all three types have a corresponding
Although in all three cases the parameter value originates from the HTTP request, it is from the URL's query string. That might become a problem as the hex encoding will multiply the length by 4 and thereby can get pretty long and exceed the limit of the HTTP request line.
After further analysis, the last one of all, the
ItemPicker.ValidateEntity(PickerEntity) method, turned out to be a better pick.
Key property of the passed
PickerEntity is used in the
EntityInstanceIdEncoder.DecodeEntityInstanceId(string) call. It gets called by
EntityEditor.Validate(), which iterates each entry stored in the
EntityEditor.Entities property to validate it.
That method gets called by
EntityEditor.LoadPostData(string, NameValueCollection), which implements the
System.Web.UI.IPostBackDataHandler.LoadPostData(string, NameValueCollection) method.
So that method gets automatically called on post back requests to
ItemPicker web controls. The call graph looks as follows:
Also note the type hierarchy:
Verifying the Data Flow
Now that there is a way to reach the
EntityInstanceIdEncoder.DecodeEntityInstanceId(string) from an
ItemPicker web control post back, it is still unclear whether the
Key property of a
PickerEntity can be controlled as well.
EntityEditor.Entities property is backed by the private field
m_listOrder, which gets only assigned at two points: during instantiation and within the
EntityEditor.Validate() method. In the latter case, it gets the value of the private field
m_listOrderTemp assigned (see line 597 in Fig. 4 above). That field, again, also only gets assigned at two points: during instantiation and within the
EntityEditor.ParseSpanData(string) method. This method is also called by
EntityEditor.LoadPostData(string, NameValueCollection) with the value of an
HtmlInputHidden and the name "hiddenSpanData" (see line 707 in Fig. 5 above). That field's value can be controlled by the user.
What is left is to see what
EntityEditor.ParseSpanData(string) does with the passed data and whether it ends up as a
Key. We'll skip that because
EntityEditor.ParseSpanData(string) is pretty long to show and unless it contains special constructs of nested
<DIV> tags, which get parsed out, everything else ends up in the
Key and then in
So, now we've found and traversed a vector that allows us to reach
EntityInstanceIdEncoder.DecodeEntityInstanceId(string) from an
ItemPicker's post back handling while also having control over the input. What is still left is to find an instance of that web control.
Finding the Entry Point
ItemPicker web control is actually never used directly in an
.aspx page. But when looking at the usages of its base type,
EntityEditorWithPicker, it turned out that there is a
/_layouts/15/Picker.aspx that uses it – what a coincidence!
That page expects the type of the picker dialog to use to be provided via the "PickerDialogType" URL parameter in the form of its assembly-qualified name. Here, any of the two
ItemPickerDialog types can be used:
Microsoft.SharePoint.WebControls.ItemPickerDialog in Microsoft.SharePoint.dll
Microsoft.SharePoint.Portal.WebControls.ItemPickerDialog in Microsoft.SharePoint.Portal.dll
Using the first
ItemPickerDialog type shows the following page:
Here, the bottom text field is associated to the
ItemPicker. And there is also the correspondent of the
HtmlInputHidden with the name
ctl00$PlaceHolderDialogBodySection$ctl05$hiddenSpanData that we were looking for. This is the source of our
Proof of Concept
When the form gets submitted with a
ctl00$PlaceHolderDialogBodySection$ctl05$hiddenSpanData value beginning with
"__dummy"), a break point at
EntityInstanceIdEncoder.DecodeEntityInstanceId(string) will reveal the following situation.
At that point the call stack looks like this:
And when the other
ItemPickerDialog type is used, just the two topmost entries are different and then look like this:
This is the final proof that the data of
ctl00$PlaceHolderDialogBodySection$ctl05$hiddenSpanData ends up in
EntityInstanceIdEncoder.DecodeEntityInstanceId(string). The rest is only coping with entity instance id encoding and finding an appropriate
After the patch was made available in February, Markus noticed something unusual. The original patch only addressed the
Microsoft.SharePoint.BusinessData.Infrastructure.EntityInstanceIdEncoder in Microsoft.SharePoint.dll but not the
Microsoft.Office.Server.ApplicationRegistry.Infrastructure.EntityInstanceIdEncoder in Microsoft.SharePoint.Portal.dll.
By using the
EntityInstanceIdEncoder type from the Microsoft.SharePoint.Portal.dll with the
Picker.aspx as described here, the exploit still worked even though the patch was installed. Microsoft addressed this with the re-release of CVE-2019-0604 yesterday.