File upload vulnerabilities

PortSwigger Academy – File upload vulnerabilities

Continua il percorso di apprendimento suggerito da “PortSwigger Academy”.

In questa sezione, imparerai come le funzioni di caricamento dei file semplici possono essere utilizzate come potente vettore per un numero di attacchi ad alta severità. Ti mostreremo come bypassare i meccanismi di difesa comuni per caricare una shell Web, consentendo di prendere il pieno controllo di un server Web vulnerabile. Dato quanto sono comuni le funzioni di caricamento dei file, sapere come testarle correttamente è una conoscenza essenziale.

Cosa sono le vulnerabilità del caricamento dei file?

Le vulnerabilità di caricamento dei file sono quando un server Web consente agli utenti di caricare file sul proprio filesystem senza convalidare sufficientemente cose come il loro nome, tipo, contenuto o dimensione. Non riuscire a far rispettare correttamente le restrizioni su questi potrebbe significare che anche una funzione di caricamento di base di immagini può essere utilizzata per caricare file arbitrari e potenzialmente pericolosi. Ciò potrebbe anche includere file di script lato server che abilitano l’esecuzione del codice remoto.

In alcuni casi, l’atto di caricare il file è di per sé abbastanza da causare danni. Altri attacchi possono comportare una richiesta HTTP di follow-up per il file, in genere per attivare la sua esecuzione da parte del server.

Qual è l’impatto delle vulnerabilità del caricamento dei file?

L’impatto delle vulnerabilità del caricamento dei file dipende generalmente da due fattori chiave:

  • Quale aspetto del file il sito Web non convalida correttamente, che si tratti di dimensioni, tipo, contenuto e così via.
  • Quali restrizioni sono imposte sul file una volta che è stato caricato correttamente.

Nel peggiore dei casi, il tipo del file non è validato correttamente e la configurazione del server consente di eseguire alcuni tipi di file (come .php e .jsp) come codice. In questo caso, un utente malintenzionato potrebbe potenzialmente caricare un file di codice lato server che funziona come una shell Web, concedendo loro efficacemente il pieno controllo sul server.

Se il nome file non è validato correttamente, ciò potrebbe consentire a un utente malintenzionato di sovrascrivere i file critici semplicemente caricando un file con lo stesso nome. Se anche il server è vulnerabile al directory traversal, ciò potrebbe significare che gli aggressori sono persino in grado di caricare file in posizioni impreviste.

Non riuscire a assicurarsi che la dimensione del file rientri all’interno delle soglie previste potrebbe anche consentire una forma di attacco di denial-of-service (DOS), per cui l’attaccante riempie lo spazio del disco disponibile.

Come sorgono le vulnerabilità del caricamento dei file?

Dati i pericoli abbastanza ovvi, è raro che i siti Web in natura non abbiano alcuna restrizione su quali file gli utenti possono caricare. Più comunemente, gli sviluppatori implementano ciò che ritengono essere una convalida robusta che è intrinsecamente imperfetta o può essere facilmente bypassata.

Ad esempio, possono tentare una blacklist per tipi di file pericolosi, ma non riescono a tenere conto delle discrepanze di analisi durante il controllo delle estensioni del file. Come con qualsiasi lista nera, è anche facile omettere accidentalmente tipi di file più oscuri che possono essere ancora pericolosi.

In altri casi, il sito Web può tentare di verificare il tipo di file verificando le proprietà che possono essere facilmente manipolate da un utente malintenzionato utilizzando strumenti come il Proxy o il Repeater di Burp.

In definitiva, anche robuste misure di convalida possono essere applicate in modo incoerente attraverso la rete di host e directory che formano il sito Web, con conseguenti discrepanze che possono essere sfruttate.

In che modo i server Web gestiscono le richieste per i file statici?

Prima di esaminare come sfruttare i file caricare le vulnerabilità, è importante avere una comprensione di base di come i server gestiscono le richieste per i file statici.

Storicamente, i siti Web consistevano quasi interamente da file statici che sarebbero stati serviti agli utenti quando richiesti. Di conseguenza, il percorso di ciascuna richiesta potrebbe essere mappato 1: 1 con la gerarchia di directory e file sul filesystem del server. Al giorno d’oggi, i siti Web sono sempre più dinamici e il percorso di una richiesta spesso non ha alcuna relazione diretta al filesystem. Tuttavia, i server Web affrontano ancora le richieste per alcuni file statici, inclusi fogli di stile, immagini e così via.

Il processo per la gestione di questi file statici è ancora in gran parte lo stesso. Ad un certo punto, il server analizza il percorso nella richiesta di identificare l’estensione del file. Quindi lo utilizza per determinare il tipo di file richiesto, in genere confrontandolo con un elenco di mappature preconfigurate tra estensioni e tipi di mime. Cosa succede dopo dipende dal tipo di file e dalla configurazione del server.

  • Se questo tipo di file non è eseguibile, come un’immagine o una pagina HTML statica, il server può semplicemente inviare il contenuto del file al client in una risposta HTTP.
  • Se il tipo di file è eseguibile, come un file PHP e il server è configurato per eseguire file di questo tipo, assegnerà variabili in base alle intestazioni e ai parametri nella richiesta HTTP prima di eseguire lo script. L’output risultante può quindi essere inviato al client in una risposta HTTP.
  • Se il tipo di file è eseguibile, ma il server non è configurato per eseguire file di questo tipo, generalmente risponderà con un errore. Tuttavia, in alcuni casi, il contenuto del file può ancora essere servito al client come testo semplice. Tali errate configurazioni possono occasionalmente essere sfruttate per carpire codice sorgente e altre informazioni sensibili. 

L’intestazione di risposta del Content-Type può fornire indizi sul tipo di file che il server pensa di aver servito. Se questa intestazione non è stata esplicitamente impostata dal codice dell’applicazione, normalmente contiene il risultato della mappatura del tipo di extension/MIME.

Exploiting dell’upload di file senza restrizioni per distribuire Web shell 

Dal punto di vista della sicurezza, lo scenario peggiore è quando un sito Web consente di caricare script sul lato server, come file PHP, Java o Python ed è anche configurato per eseguirli come codice. Questo rende banale creare la tua shell web sul server.

Nota

Una Web shell è uno script dannoso che consente a un utente malintenzionato di eseguire comandi arbitrari su un server Web remoto semplicemente inviando richieste HTTP all’endpoint giusto.

Se sei in grado di caricare correttamente una shell Web, hai effettivamente il pieno controllo sul server. Ciò significa che è possibile leggere e scrivere file arbitrari, exfiltraere dati sensibili, persino utilizzare il server per aprire gli attacchi contro l’infrastruttura interna e altri server al di fuori della rete. Ad esempio, il seguente PHP One-liner potrebbe essere utilizzato per leggere file arbitrari dal filesystem del server:

<?php echo file_get_contents(’/path/to/target/file’); ?>

Una volta caricato, l’invio di una richiesta per questo file dannoso restituirà il contenuto del file di destinazione nella risposta.

Lab-https://portswigger.net/web-security/file-upload/lab-file-upload-remote-code-execution-via-web-shell-upload

Una shell web più versatile può assomigliare a questa:

<?php echo system($_GET[’command’]); ?>

Questo script consente di passare un comando di sistema arbitrario tramite un parametro di query come segue:

GET /example/exploit.php?command=id HTTP/1.1

Exploiting della convalida imperfetta nell’upload di file

In natura, è improbabile che troverai un sito Web che non ha protezione contro gli attacchi sull’upload dei file come abbiamo visto nel laboratorio precedente. Ma solo perché le difese sono in atto, ciò non significa che siano robuste. A volte puoi ancora sfruttare i difetti in questi meccanismi per ottenere una shell Web per l’esecuzione del codice remoto.

Imperfetta convalida del tipo di file 

Quando si invia i moduli HTML, il browser invia in genere i dati forniti in una richiesta POST con il content type application/x-www-form-url-encoded. Va bene per inviare un testo semplice come il tuo nome o indirizzo. Tuttavia, non è adatto per l’invio di grandi quantità di dati binari, come un intero file di immagine o un documento PDF. In questo caso, è preferito il content type  multipart/form-data.

Prendi in considerazione un modulo contenente campi per caricare un’immagine, fornirne una descrizione e inserire il tuo nome utente. L’invio di tale modulo potrebbe comportare una richiesta che assomiglia a questa:

POST /images HTTP/1.1
    Host: normal-website.com
    Content-Length: 12345
    Content-Type: multipart/form-data; boundary=---------------------------012345678901234567890123456

    ---------------------------012345678901234567890123456
    Content-Disposition: form-data; name="image"; filename="example.jpg"
    Content-Type: image/jpeg

    [...binary content of example.jpg...]

    ---------------------------012345678901234567890123456
    Content-Disposition: form-data; name="description"

    This is an interesting description of my image.

    ---------------------------012345678901234567890123456
    Content-Disposition: form-data; name="username"

    wiener
    ---------------------------012345678901234567890123456--

Come puoi vedere, il corpo del messaggio viene diviso in parti separate per ciascuno degli input del modulo. Ogni parte contiene un’intestazione Content-Disposition, che fornisce alcune informazioni di base sul campo di input a cui si riferisce. Queste singole parti possono anche contenere la propria intestazione Content-Type, che indica al server il tipo MIME dei dati inviati utilizzando questo input.

Un modo in cui i siti Web possono tentare di convalidare i caricamenti di file è verificare che questa intestazione Content-Type specifica per input corrisponda a un tipo MIME previsto. Se il server si aspetta solo file di immagini, ad esempio, può consentire solo tipi come image/jpeg e image/png. I problemi possono sorgere quando il valore di questa intestazione è implicitamente affidabile dal server. Se non viene eseguita alcuna ulteriore convalida per verificare se il contenuto del file corrisponde effettivamente al presunto tipo di MIME, questa difesa può essere facilmente bypassata usando strumenti come Burp Repeater.

Lab-https://portswigger.net/web-security/file-upload/lab-file-upload-web-shell-upload-via-content-type-restriction-bypass

Mentre in primo luogo è chiaramente meglio impedire che i tipi di file pericolosi vengano caricati, la seconda linea di difesa è impedire al server di eseguire qualsiasi script che “scivolano” attraverso la rete.

Per precauzione, i server eseguono generalmente solo script il cui tipo di MIME è stato esplicitamente configurato per essere eseguito. Altrimenti, possono restituire una sorta di messaggio di errore o, in alcuni casi, servire invece il contenuto del file come testo semplice:

GET /static/exploit.php?command=id HTTP/1.1
    Host: normal-website.com


    HTTP/1.1 200 OK
    Content-Type: text/plain
    Content-Length: 39

    <?php echo system($_GET['command']); ?>

Questo comportamento è potenzialmente interessante a sé stante, in quanto può fornire un modo per carpire il codice sorgente, ma annulla qualsiasi tentativo di creare una shell Web.

Questo tipo di configurazione spesso differisce tra le directory. Una directory a cui vengono caricati i file forniti dall’utente avrà probabilmente controlli molto più severi rispetto ad altre posizioni sul filesystem che si presume siano fuori portata per gli utenti finali. Se riesci a trovare un modo per caricare uno script in una directory diversa che non dovrebbe contenere file forniti dall’utente, il server potrebbe eseguire lo script.

Nota

I server Web utilizzano spesso il campo filename nelle richieste multipart/form-data per determinare il nome e la posizione in cui il file deve essere salvato.

Lab-https://portswigger.net/web-security/file-upload/lab-file-upload-web-shell-upload-via-path-traversal

Dovresti inoltre notare che anche se puoi inviare tutte le tue richieste allo stesso nome di dominio, questo spesso indica un server proxy inverso di qualche tipo, come un bilanciamento del carico. Le tue richieste saranno spesso gestite da ulteriori server dietro le quinte, che possono anche essere configurati in modo diverso.

Lista nera di file pericolosi insufficiente 

Uno dei modi più ovvi per impedire agli utenti di caricare script dannosi è quello di blacklist estensioni di file potenzialmente pericolose come .php. La pratica della lista nera è intrinsecamente imperfetta in quanto è difficile bloccare esplicitamente ogni possibile estensione di file che potrebbe essere utilizzata per eseguire il codice. Tali liste nere a volte possono essere bypassate utilizzando estensioni di file alternative meno note che possono essere ancora eseguibili, come .php5, .shtml e così via.

Overriding della configurazione del server

Come abbiamo discusso nella sezione precedente, i server in genere non eseguiranno file a meno che non siano stati configurati per farlo. Ad esempio, prima che un server Apache eseguirà i file PHP richiesti da un client, gli sviluppatori potrebbero dover aggiungere le seguenti direttive al loro file /etc/apache2/apache2.conf:

LoadModule php_module /usr/lib/apache2/modules/libphp.so
    AddType application/x-httpd-php .php

Molti server consentono inoltre agli sviluppatori di creare file di configurazione speciali all’interno delle singole directory per sovrascrivere o aggiungere a una o più impostazioni globali. I server Apache, ad esempio, caricheranno una configurazione specifica della directory da un file chiamato .htaccess se uno è presente.

Allo stesso modo, gli sviluppatori possono effettuare una configurazione specifica per la directory sui server IIS utilizzando un file web.config. Ciò potrebbe includere direttive come le seguenti, che in questo caso consente di servire i file JSON agli utenti:

<staticContent>
    <mimeMap fileExtension=".json" mimeType="application/json" />
    </staticContent>

I server Web utilizzano questi tipi di file di configurazione quando presenti, ma normalmente non è consentito accedervi utilizzando le richieste HTTP. Tuttavia, occasionalmente potresti trovare server che non ti impediscono di caricare il tuo file di configurazione dannoso. In questo caso, anche se l’estensione del file di cui hai bisogno è nella lista nera, potresti essere in grado di indurre il server a mappare un’estensione arbitraria e personalizzata su un tipo MIME eseguibile.

Lab-https://portswigger.net/web-security/file-upload/lab-file-upload-web-shell-upload-via-extension-blacklist-bypass

Estensioni di file offuscanti

Anche le liste nere più esaustive possono potenzialmente essere bypassate usando le classiche tecniche di offuscamento. Supponiamo che il codice di convalida sia sensibile al caso e non riconosce che exploit.pHp è in realtà un file .php. Se il codice che successivamente mappa l’estensione del file su un tipo MIME non è case sensitive, questa discrepanza consente a file PHP dannosi di sgattaiolare la convalida e alla fine essere eseguito dal server.

Puoi anche ottenere risultati simili utilizzando le seguenti tecniche:

  • fornire più estensioni: a seconda dell’algoritmo utilizzato per analizzare il nome file, il seguente file può essere interpretato come un file PHP o un’immagine JPG: exploit.php.jpg;
  • aggiungere caratteri finali: alcuni componenti spoglieranno o ignoreranno spazi bianchi, punti e simili: exploit.php;
  • provare a utilizzare la codifica URL (o la codifica a doppio URL) per punti, slash e back slash. Se il valore non viene decodificato durante la convalida dell’estensione del file, ma viene successivamente decodificato sul lato server, questo può anche consentire di caricare file dannosi che sarebbero altrimenti bloccati: exploit%2Ephp;
  • aggiungere punti e virgole o caratteri byte null codificati con URL prima dell’estensione del file. Se la convalida è scritta in un linguaggio di alto livello come PHP o Java, ma il server elabora il file utilizzando funzioni di livello inferiore in C/C ++, ad esempio, ciò può causare discrepanze in quella che viene trattata come la fine del file: exploit.asp, .jpg o exploit.asp%00.jpg;
  • provare a utilizzare i caratteri unicode multibyte, che possono essere convertiti in byte e punti nulli dopo la conversione o la normalizzazione Unicode. Sequenze come xC0 x2E, xC4 xAE or xC0 xAE possono essere tradotte in x2E se il filename è analizzato come stringa UTF-8, ma poi convertito in caratteri ASCII prima di essere utilizzato in un percorso.

Altre difese prevedono lo stripping o la sostituzione di estensioni pericolose per impedire l’esecuzione del file. Se questa trasformazione non viene applicata in modo ricorsivo, è possibile posizionare la stringa proibita in modo tale da rimuoverla lasciando ancora una valida estensione del file. Ad esempio, considera cosa succede se si spoglia .php dal seguente nome file:

exploit.p.phphp

Questa è solo una piccola selezione dei molti modi in cui è possibile offuscare le estensioni dei file.

Lab-https://portswigger.net/web-security/file-upload/lab-file-upload-web-shell-upload-via-obfuscated-file-extension

Convalida imperfetta del contenuto del file

Invece di fidarsi implicitamente del Content-Type specificato in una richiesta, i server più sicuri cercano di verificare che il contenuto del file corrisponda effettivamente a ciò che è previsto.

Nel caso di una funzione di caricamento di una immagine, il server potrebbe provare a verificare alcune proprietà intrinseche di un’immagine, come le sue dimensioni. Se provi a caricare uno script PHP, ad esempio, non avrà alcuna dimensione. Pertanto, il server può dedurre che non può essere un’immagine e rifiutare il caricamento di conseguenza.

Allo stesso modo, alcuni tipi di file possono sempre contenere una sequenza specifica di byte nell’header o nel footer di pagina. Questi possono essere usati come un’impronta digitale o una firma per determinare se i contenuti corrispondono al tipo previsto. Ad esempio, i file JPEG iniziano sempre con i byte FF D8 FF.

Questo è un modo molto più robusto di convalidare il tipo di file, ma anche questo non è infallibile. Utilizzando strumenti speciali, come Exiftool, può essere banale creare un file JPEG polyglot contenente codice dannoso all’interno dei suoi metadati.

Lab-https://portswigger.net/web-security/file-upload/lab-file-upload-remote-code-execution-via-polyglot-web-shell-upload

Sfruttamento delle condizioni di caricamento dei file

I framework moderni sono più resistenti contro questo tipo di attacchi. In genere non caricano file direttamente nella destinazione prevista sul filesystem. Invece, prendono precauzioni come il caricamento in una directory temporanea e sandboxing e randomizzando il nome per evitare di sovrascrivere i file esistenti. Quindi eseguono la convalida su questo file temporaneo e lo trasferiscono a destinazione solo una volta considerato sicuro.

Detto questo, gli sviluppatori a volte implementano il proprio processo di caricamento di file indipendentemente da qualsiasi framework. Non solo è abbastanza complesso da implementare bene, ma può anche introdurre condizioni pericolose che consentono a un aggressore di aggirare completamente anche la convalida più robusta.

Ad esempio, alcuni siti Web caricano il file direttamente sul filesystem principale e quindi lo rimuovono se non supera la convalida. Questo tipo di comportamento è tipico nei siti Web che si basano sul software antivirus e di verifica del malware. Ciò può richiedere solo pochi millisecondi, ma per il breve periodo esiste sul server, l’attaccante può potenzialmente ancora eseguirlo.

Queste vulnerabilità sono spesso estremamente subdole, il che le rende difficili da rilevare durante i test blackbox, a meno che non si riesca a trovare un modo per far trapelare il codice sorgente pertinente.

Lab-https://portswigger.net/web-security/file-upload/lab-file-upload-web-shell-upload-via-race-condition

Race condition nei carichi di file basati su URL

Condizioni di corsa simili possono verificarsi in funzioni che consentono di caricare un file fornendo un URL. In questo caso, il server deve recuperare il file su Internet e creare una copia locale prima di poter eseguire qualsiasi convalida.

Poiché il file viene caricato utilizzando HTTP, gli sviluppatori non sono in grado di utilizzare i meccanismi integrati del loro framework per la convalida in modo sicuro. Invece, possono creare manualmente i propri processi per archiviare e convalidare temporaneamente il file, che potrebbe non essere così sicuro.

Ad esempio, se il file viene caricato in una directory temporanea con un nome randomizzato, in teoria, dovrebbe essere impossibile per un attaccante sfruttare eventuali condizioni di gara. Se non conoscono il nome della directory, non saranno in grado di richiedere il file per attivare la sua esecuzione. D’altra parte, se il nome della directory randomizzato viene generato utilizzando funzioni pseudo-casuali come uniqid() di PHP, può potenzialmente essere rafforzato.

Per semplificare gli attacchi come questo, puoi provare a prolungare il tempo impiegato per elaborare il file, allungando così la finestra per il brute-forcing del nome della directory. Un modo per farlo è caricare un file più grande. Se viene elaborato in blocchi, puoi potenzialmente trarne vantaggio creando un file dannoso con il carico utile all’inizio, seguito da un gran numero di byte arbitrari di imbottitura.

Sfruttare le vulnerabilità del caricamento dei file senza esecuzione del codice remoto

Negli esempi che abbiamo esaminato finora, siamo stati in grado di caricare script sul lato server per l’esecuzione del codice remoto. Questa è la conseguenza più grave di una funzione di upload di file insicuri, ma queste vulnerabilità possono ancora essere sfruttate in altri modi.

Caricamento di script dannoso lato client 

Sebbene potresti non essere in grado di eseguire script sul server, potresti comunque essere in grado di caricare script per gli attacchi sul lato client. Ad esempio, se è possibile caricare file HTML o immagini SVG, è possibile utilizzare potenzialmente i tag <script> per creare payload XSS archiviati.

Se il file caricato viene quindi visualizzato in una pagina visitata da altri utenti, il loro browser eseguirà lo script quando tenta di visualizzare la pagina. Si noti che a causa delle restrizioni delle politiche della stessa origine, questi tipi di attacchi funzionano solo se il file caricato viene servito dalla stessa origine a cui lo si carica.

Sfruttando le vulnerabilità nell’analisi di file caricati

Se il file caricato sembra essere archiviato e servito in modo sicuro, l’ultima risorsa è provare a sfruttare le vulnerabilità specifiche per l’analisi o l’elaborazione di diversi formati di file. Ad esempio, sai che il server analizza i file basati su XML, come i file Microsoft Office .doc o .xls, questo potrebbe essere un potenziale vettore per gli attacchi di iniezione XXE.

Caricamento di file utilizzando PUT

Vale la pena notare che alcuni server Web potrebbero essere configurati per supportare le richieste PUT. Se non sono in atto difese appropriate, ciò può fornire un mezzo alternativo per caricare file dannosi, anche quando una funzione di caricamento non è disponibile tramite l’interfaccia Web.

PUT /images/exploit.php HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-httpd-php
Content-Length: 49

<?php echo file_get_contents('/path/to/file'); ?>

Nota

È possibile provare a inviare richieste OPTIONS a endpoint diversi per testare qualsiasi supporto per pubblicizzare per il metodo PUT.

Come prevenire le vulnerabilità del caricamento dei file

Consentire agli utenti di caricare file è all’ordine del giorno e non deve essere pericoloso fintanto che prendi le precauzioni giuste. In generale, il modo più efficace per proteggere i propri siti Web da queste vulnerabilità è implementare tutte le seguenti pratiche:

  • controlla l’estensione del file con una whitelist di estensioni consentite piuttosto che una lista nera di quelle proibite. È molto più facile indovinare quali estensioni potresti voler consentire che indovinare quali un attaccante potrebbe provare a caricare;
  • assicurarsi che il filename non contenga alcun substrings che può essere interpretato come una directory o una sequenza di attraversamento (../);
  • rinomina i file caricati per evitare collisioni che possono causare la sovrascrittura di file esistenti;
  • non caricare file sul filesystem permanente del server fino a quando non sono stati completamente validati;
  • per quanto possibile, utilizzare un framework consolidato per la preelaborazione degli upload di file anziché tentare di scrivere i propri meccanismi di convalida.