How I gained Remote Code Execution and what we can learn as software developers
Article by Stiig Gade on 07 June 2023
The following post is a fictional security review based on an aggregate of experiences regarding cyber security in software development and from conducting security reviews.
What is Remote Code Execution (RCE)?
According to OWASP, RCE (or, in their terms, Code Injection) is the process of injecting code, which is then interpreted or executed by an application or system. Personally, I consider this the holy grail of vulnerabilities. If an attacker gains access to an RCE, they can quickly take over the system and gain a foothold/persistence, so they can easily access said system afterwards.
Why am I even hacking?
Even though we are a software consultancy company, we also conduct security reviews. We truly believe that security is a fundamental cornerstone of software development, and not a feature which is implemented as an afterthought in the last sprint, “because we have to”. By doing security reviews, we raise awareness of this fact both internally and externally, which improves the quality of our products, and helps clients reduce their attack surface.
Gaining access
I have been tasked with finding vulnerabilities within a system used to generate certificates. I start by powering up Burp Suite and browsing around the test-environment to get a feeling of the website. New users need to be approved by a moderator before gaining access to the system. Well, in production, at least, as it turns out that users get auto-approved in Test. This would be alright if the test environment were behind a VPN, but it’s open to the public. I have now gained access to the test environment; if I can, everyone else can too. This in itself may not seem too worrying, but let’s see where we can go from here.
Information exposure
I am a normal user in this system, and the only thing I can do is print certificates from predefined templates. The endpoint is GET /certificate/render/1?name=Stiig, which renders said certificate with my name. Now for every endpoint I find, I poke at it with various variations of requests, such as POST/PUT and with all kinds of input. POST’ing to said endpoint returns 401 (Unauthorized), but because this is Test, I also get a message and stack trace:

Stacktrace which exposes inner workings of the application
From this, it doesn’t take long to extrapolate that the User-entity has a relationship to a Moderator-entity. Still, as I am not a moderator, it seems like I don’t have that entity.
Privilege escalation
The system has an endpoint PUT /users, which is used to update my own information. It takes a User-entity that consists of Name, Email and Password. But what if I extend this information with ModeratorId? Often the first user created in a system is an admin-user; thus id 1 seems like a valid option:

Unsanitized input causes the application accept unintended modification
It looks like it was a success. Now I have escalated my privileges so that I can modify templates. One step at a time, we breach further and further.
This could have been avoided by specifying an input-model which only had the desired properties, like this:

Example of UserEditDto which would mitigate unintended data modification
Now deserialized input will not include ModeratorId, thus it isn’t possible to change.
Server-Side Template Injection exploit
As mentioned, the system allows for creating templates if you have the proper privileges – which I do now. So let’s attempt to do this, but with special characters, to see if the website is vulnerable. The process of testing for which payload should be used can easily be automated with various tools, but as I know, this system runs .net, I might start with manually testing this with @-sign, which denotes expressions in Razor:

Testing for Server Side Template Injection vulnerability
Now when rendering this template, we can see that the code is executed:

Verified SSTI vulnerability
The payload sent was @(2+2), which is interpreted by the Razor engine.
Accessing the host
Time to verify if we can access the host system, which can easily be done with a simple payload:

C:/temp/test.txt”) which, if executed, would store a file in the temp folder on C-drive” width=”1500″ height=”365″ /> Using SSTI to gain access to RCE on the host system
If this payload gets interpreted, we will create a file on the host system. Let’s attempt to GET this template:

Remote code execution using stored payload in SSTI
And the final check to see if the file was created:

Confirmation of access to the underlying host system
As we have proof that we can start processes on the remote system, we have gained Remote Code Execution and thus can infiltrate the host machine. And we did it in three small steps. Each may have seemed innocuous, but together enabled the holy grail of vulnerabilities.
Escaping the containment
When conducting security reviews, we must oblige to certain rules. These rules are both for the customers’ as well as our own protection so we don’t break something by mistake. In this case, I do not want to access other systems, but I want to verify if a potential attacker might be able to do this. For this purpose, I could list other IIS websites hosted by the machine. This gives the following output, which confirms that test and production are hosted on the same machine:

Confirmation of other systems present
Always separate different environments so that if an attacker should gain access, (s)he can’t access other systems.
What is the takeaway?
The point of this post is not how to mitigate individual vulnerabilities, but that systems are often compromised because of multiple vulnerabilities used in conjunction with one another. Unfortunately, reports of vulnerabilities are often brushed aside with the argument that exploitation requires other dependencies (like elevated rights). But as we have seen here, many small vulnerabilities can be stitched together into a large breach.
I want developers to change their fundamental chain of thought during development. When you implement functionality which handles user interaction, like an API, think Evil. Think about how this could be exploited, and then consider how this can be mitigated. Every single mitigated vulnerability reduces the attack surface of the system itself and other internal and external systems.
Sikkerhedsreview
Læs mere om Commentors arbejde med sikkerhedsreview her.