BACK_TO_ARCHIVES
2026-04-21 6 MIN READ

RCE (Remote Command Execution) 4 Dummies

VULNERABILITIES PYTHON RED TEAM RCE OWASP
RCE (Remote Command Execution) 4 Dummies

RCE in a single way

Think of your application like a waiter in a restaurant. A user places an order (input), and the application sends that order to the kitchen (the system). Now imagine the waiter doesn’t check the order properly. Instead of just asking for “pizza”, someone says:

pizza… and also go into the kitchen and turn off the lights and get the secret recipe for the sauce.

If the waiter blindly follows instructions, they’re no longer just serving food, they’re executing unexpected actions. That’s essentially what happens in an RCE vulnerability. The application trusts user input too much and ends up executing more than it should.

In simple terms, it's the result of a vulnerability that allows an attacker to write and execute commands on a target system from a remote location, without requiring prior authentication or direct access to the machine. This primarily occurs when user inputs are not properly sanitized or validated, enabling the injection of malicious commands. RCE is a type of injection vulnerability and is categorized within the OWASP Top 10 under A05:2025 Injection.

RCE represents one of the most critical cybersecurity vulnerabilities, as it enables malicious actors to run unauthorized code on target systems including servers, computers, and network devices from remote locations. This security flaw is particularly dangerous because it allows attackers to gain complete control over compromised systems without needing physical access or, in many scenarios, any form of user authentication.

The impact of a successful RCE attack can be devastating, potentially allowing attackers to steal sensitive data, install malware, create backdoors for persistent access, or even use the compromised system as a launchpad for attacks against other systems within the network.

Types of RCE Attacks

  • Command Injection: Injecting OS-level commands, you can inject ls; for example in an input (e.g., via system() calls).
  • Code Injection: Injecting language-specific code (e.g., using eval() in PHP or Python).
  • Insecure Deserialization: Exploiting how data is converted into objects to trigger execution.
  • SSTI (Server-Side Template Injection): Injecting code into web templates like Jinja2 or Twig.
  • Out-of-bounds write: Using buffer overflows to overwrite memory with malicious instructions.

Let's learn with practice

The best way to understand this is applying the concepts and methodology on a adecuated environment.

The first is download the repository and go to the machine-rce folder.

If we review the code in app.py, we can observe that the application exposes an endpoint called ping, which internally executes the command ping -c 4 IP. The critical issue lies in how user-supplied input is handled: it is accepted without any form of validation or sanitization. This oversight allows an attacker to bypass intended restrictions and inject malicious commands, potentially leading to unauthorized access to system information or further exploitation.

@app.route('/ping')
def ping():
    ip = request.args.get('ip')
    if not ip:
        return "Falta el parámetro ip", 400

    try:
        result = subprocess.getoutput(f"ping -c 4 {ip}") #just executing without checking the ip value?
        return f"<pre>{result}</pre>"
    except Exception as e:
        return f"Error: {str(e)}", 500

Exploiting

Start the machine using the cmd.

docker compose up --build

We already know the endpoint, but if that weren’t the case, we could always inspect API requests and interactions through the Network tab in the browser’s developer tools. By analyzing these requests, we can understand how the application behaves and identify patterns. In this scenario, the interface is quite intuitive, allowing us to perform the assessment without needing to interact with the API directly using tools like curl or Postman.

Now we can start testing different payloads to bypass the lack of input validation. A useful resource is PayloadAllTheThings, which provides a wide range of payloads for different scenarios.

In this case, simply appending ; cmd; to the IP input may allow us to execute both the ping command and an additional injected command. In some scenarios, encoding techniques such as URL encoding may be required to bypass basic string parsing or filtering mechanisms. If the underlying operating system is unknown, we can run basic commands for both Linux and Windows to help identify it. For example, using whoami can reveal the user under which the application is running, giving us more context about the target environment.

In this case, we’ve identified that the target is a Linux machine. Assuming the application is running with elevated privileges, this significantly increases the potential impact. We can now leverage standard enumeration commands to gather more information about the system and its configuration. For example:

We can observe that the application responds with an HTTP status 200, which indicates that the request was successfully processed. This suggests that the attack is not being detected or blocked, highlighting the absence of proper input validation, sanitization, or security controls to mitigate such malicious input.

At this point, we can go deeper into enumeration, potentially downloading files from an attacker controlled server or establishing a reverse shell to create a connection back to the attacker’s machine. All of this can be achieved remotely, without direct interaction through a local CLI or terminal on the target system. Interesting... right?

Mitigation

Based on this case, we can conclude that it is essential to implement proper input validation and sanitization. All user-supplied data must be strictly validated to ensure it conforms to expected formats and does not contain malicious content. This helps prevent vulnerabilities like command injection, which can place systems at significant risk if left unaddressed.

import ipaddress

@app.route('/ping')
def ping():
    ip = request.args.get('ip')

    # Strict validation
    if not ip or not isinstance(ip, str):
        return "IP inválida", 400

    try:
        # Validate is a valid IP address
        ipaddress.ip_address(ip)
    except ValueError:
        return "IP inválida", 400

    # Use a list and NEVER concatenate strings
    try:
        result = subprocess.check_output(
            ["ping", "-c", "4", ip],
            stderr=subprocess.STDOUT,
            timeout=10,          # Timeout
            text=True
        )
        return f"<pre>{result}</pre>"
    except subprocess.CalledProcessError as e:
        return f"Error: {e.output}", 500
    except subprocess.TimeoutExpired:
        return "Timeout", 500

RCE vs Command Injection

At this point, it’s important to clarify something that often causes confusion:

RCE is not a single vulnerability type, it’s the result.

  • Command Injection: A vulnerability where an attacker can add and execute system commands (like in Bash or PowerShell) through an application.

  • Code Injection: A vulnerability where an attacker can insert and run code (like Python, PHP, or JavaScript) inside the application itself.

  • Remote Code Execution (RCE): The result of exploiting a vulnerability that allows an attacker to run code on a system remotely. It’s not the vulnerability itself, but the impact, and it can come from different issues (not just code injection).

That’s all for now. I hope you found this explanation clear and useful, and that it helped you understand these concepts in a simple way so you can apply them in your offensive security practices. For further reading, consider reviewing the Cloudflare and OWASP documentation:

Hoping this content results useful to you, never stop learning.

END_OF_REPORT | AUTHOR: JESUS SALCEDO