This article is in no way affiliated, sponsored, or endorsed with/by Siemens Healthineers or Microsoft Corporation. All graphics are being displayed under fair use for the purposes of this article.

Last year I spent some time looking for vulnerabilities in a commercial cardiovascular imaging web application called  Syngo Dynamics. This product is developed by Siemens Healthineers. Syngo Dynamics is a rather complex application that consists of a web application, .NET web services, native binaries, and a database.

For the purposes of this blog post, I’m going to focus on the web services portion of the application. I gave a BSIDES talk on this topic for those that may prefer that format. The remainder of this post will detail my approach when performing white-box vulnerability research on a .NET web application. The first thing I like to do is open up the IIS config and any “web.config” files for the application. The IIS config can be found at “C:\Windows\System32\inetsrv\config” and the “web.config” files are typically in the app pool directories with a format similar to “<Drive Letter>\inetpub\temp\appPools\<App Name>\web.config“.

When performing white-box vulnerability research, my primary goal is to locate the code so I can look for bugs. There’s two important things we can take away from these config files to further this effort. The “application” node defines the mapping of the application endpoint to the physical path on disk where code exists. The “endpoint” node inside the “service” node specifies the class contract that defines the endpoint behavior. If we navigate to the “physicalPath” for the “DataTransferServices” we find “svc” files that describe each service.

If we look at the contents of the “CommonService.svc” file, we can see that the assembly that implements this service is called DataTransferServices and the service name is DataTransferServices.CommonService. At this point we need to locate the assembly to begin the reverse engineering process. But what is an assembly?

So we’re looking for an assembly named “DataTransferServices.exe.” or “DataTransferServices.dll” likely in the same directory tree. If we do a quick search in explorer we find what we are looking for.

Now that the assembly has been located, it’s time to open up the binary in a decompiler. Since the binary is a managed C# assembly, we can decompile it back into readable C# code. There are two tools I like to use for this, dotPeek & dnSpy. They are mostly feature equivalent with the exception of dotPeek also providing debugging capabilities.

Opening up the “DataTransferServices.dll” binary we find the contract interface that defines the functions that are exposed through the service. From dnSpy we select “Go to Implementation” to view the code for one of the functions.

One of the first things that jumped out to me is the concatenation of user controlled parameters for the file path that is then read and returned in the response. Praetorian wrote up an interesting article about abusing the Path.Combine function in C# . Basically, if an absolute path is given as one of the parameters to concatenate, the others are ignored. To confirm this suspicion, I needed to craft a web request to hit this code. The first thing I did was navigate to the service endpoint in a browser to see what it displayed.

It looks like the metadata output is disabled. Unfortunately without metadata output enabled we won’t be able to easily generate sample web requests for the web service endpoints. Luckily the webpage tells us how to enable it inside the “web.config” file for the web service. After making the modification we are greeted with a slightly different landing page.

The service endpoint shows a link to a new “wsdl” endpoint that describes the functions published by the service. Unfortunately without something to ingest, the “wsdl” XML file it isn’t very useful. Fortunately, Burp has an extension called Wsdler that was made for this very purpose. If we navigate to the “wsdl” endpoint we can then right click on the request in the Burp Proxy -> HTTP history tab and send it to the Wsdler extension.

Using Wsdler you can select the operation in the list and it will generate a sample HTTP SOAP request. The request can then be sent to the Repeater tab to test out. I enter the full path in the “fileName” field to confirm the ability to read arbitrary files using the Path.Combine function.

Our suspicious was correct. We successfully read an arbitrary file rather than files in the intended directory. Often times it isn’t this simple and having the ability to debug the code is pivotal. In the next section, I’ll walk through how to do that using dnSpy.

Debugging .NET web services using dnSpy and dotPeek

To begin debugging a .NET web service, we need to locate the IIS w3wp.exe process. Click the Debug-> Attach to Process menu in dnSpy to bring up the process selection dialog. If no w3wp.exe process exists, send a web request to the web service to ensure a work process is created.

If the w3wp.exe process still isn’t listed, there could be an architecture mismatch either between the dnSpy application or the IIS server process. It is important to ensure the architecture of dnSpy, the .NET assembly, and the IIS server process are all the same. The architecture of the IIS process can be modified in the Advanced Settings of the IIS Application Pool.

Once you attach to the w3wp.exe process you should be able to drop breakpoints in the decompiled source that will get hit when the code executes. If you drop a breakpoint and the circle is an outline rather than filled-in, you have one more step to complete. This means that the debugger could not find symbols for the application you are debugging.

In this case we can use dotPeek to generate the symbols for the assembly. Simply open the assembly in dotPeek, right click on the assembly in the “Assembly Explorer” and click Generate Pdb.

After the PDB has been generated, copy the file into the same directory as the assembly that is being debugged and it should be automatically loaded. Restart the debugger and begin stepping through the code.

As shown in the screenshot above, I can confirm at the code level using the debugger that Path.Combine does indeed allow for the opening of arbitrary file paths if one of the arguments is absolute.

Finding Variants

With the discovery of one misuse of the Path.Combine function resulting in an arbitrary file read, it makes sense to search the rest of the code base for additional instances that may have more impact. Since this vulnerability isn’t particularly complex, I chose to use grepWin to search for other uses of Path.Combine who’s arguments may be controllable.

Reviewing the results led to the discovery of a similar usage of Path.Combine but this time as a file write. The impact of an arbitrary file write is often more critical as it typically allows for code execution.

Ultimately an additional 7 instances of exploitable insecure Path.Combine usage were identified. These findings fell into three vulnerability categories, arbitrary file read, arbitrary file write, & server side request forgery (SSRF). I will leave exploitation of these vulnerability types as an exercise for the reader as this is a pretty well documented topic. For some examples feel free to watch my BSIDES Charleston 2022 talk.

Vendor Disclosure & Patch

I reported these issues through the Siemens Healthineers vulnerability disclosure process and can say everything went smoothly and they worked with me to get the issues fixed and patched in a reasonable time frame. Given the severity of these findings, we strongly encourage anyone that has Syngo Dynamics deployed to update to the latest version immediately. More details about the vulnerabilities can be found on the Siemens Healthineers security advisory page.