Domanda:
L'hacker ha utilizzato il caricamento delle immagini per ottenere il codice PHP nel mio sito
Williamz902
2017-01-04 16:04:26 UTC
view on stackexchange narkive permalink

Sto lavorando a un sito web - in questo momento è nelle prime fasi del test, non ancora lanciato e contiene solo dati di test - grazie a Dio.

Prima di tutto, un hacker ha scoperto la password per accedere alle pagine di "amministrazione" dei siti Web * . Penso che abbiano utilizzato un key logger sul computer di un amico che ha effettuato l'accesso al sito per darmi un feedback.

In secondo luogo, hanno utilizzato una casella di caricamento delle immagini per caricare un file PHP. Ho eseguito un controllo rigoroso in modo che vengano accettati solo i file .jpg e .png - tutto il resto avrebbe dovuto essere rifiutato. Sicuramente non c'è modo di caricare un file .jpg e quindi modificare l'estensione una volta che il file è stato memorizzato?

Per fortuna, quando mi viene inviato un file, generi anche nuovi nomi di file, quindi non credo sono stati in grado di individuare il file per eseguire il codice.

Non riesco proprio a capire come il sito web lasci passare un file PHP. Cosa c'è di sbagliato nella mia sicurezza? Il codice della funzione di convalida è di seguito:

  function ValidateChange_Logo (theForm) {var regexp; if (theForm.FileUpload1.value == "") {alert ("Non hai scelto un nuovo file logo o il tuo file non è supportato."); theForm.FileUpload1.focus (); return false; } var extension = theForm.FileUpload1.value.substr (theForm.FileUpload1.value.lastIndexOf ('.')); if ((extension.toLowerCase ()! = ".jpg") && (extension.toLowerCase ()! = ".png")) {alert ("Non hai scelto un nuovo file logo o il tuo file non è supportato. "); theForm.FileUpload1.focus (); return false; } return true;}  

Una volta che il file arriva al server, utilizzo il codice seguente per mantenere l'estensione e generare un nuovo nome casuale. È un po 'disordinato, ma funziona bene.

  // Elabora e conserva l'estensione del file $ fileExt = $ _FILES [logo] [nome]; $ reversed = strrev ($ fileExt); $ extension0 = substr ($ reversed, 0, 1); $ extension1 = substr ($ reversed, 1, 1); $ extension2 = substr ($ reversed, 2, 1); $ fileExtension = ".". $ extension2. $ extension1 . $ extension0;
$ newName = rand (1000000, 9999999). $ fileExtension;  

Ho appena testato con un nome come logo.php; .jpg e sebbene l'immagine non possa essere aperta dal sito web, è corretta ha cambiato il nome in 123456.jpg . Per quanto riguarda logo.php / .jpg , Windows non consente tale nome di file.


* Pagine protette sul sito web che consentono semplici funzioni: come caricare un'immagine che poi diventa un nuovo logo per il sito web. I dettagli FTP sono completamente diversi dalla password utilizzata per accedere alle pagine protette del sito web. Così come le credenziali del database e del cPanel. Ho assicurato che le persone non possano nemmeno visualizzare la struttura delle cartelle e dei file del sito. Non riesco letteralmente a pensare di rinominare un'estensione .jpg o .png in .php su questo sito se non hai i dettagli FTP.

I commenti non sono per discussioni estese;questa conversazione è stata [spostata nella chat] (http://chat.stackexchange.com/rooms/51538/discussion-on-question-by-williamz902-hacker-used-picture-upload-to-get-php-code).
Risposta breve: la convalida lato client può essere facilmente aggirata impostando un proxy.Possono semplicemente inviare prima a.jpg e superare tutti i controlli.La richiesta verrà quindi intercettata nel proxy e modificata in a.php.È necessario eseguire la convalida sul lato server.
Undici risposte:
#1
+313
Anders
2017-01-04 16:12:38 UTC
view on stackexchange narkive permalink

Convalida lato client

Il codice di convalida che hai fornito è in JavaScript. Ciò suggerisce che è il codice che utilizzi per eseguire la convalida sul client.

La regola numero uno per proteggere le webapp è di non fidarsi mai del client . Il client è sotto il pieno controllo dell'utente o, in questo caso, dell'aggressore. Non puoi essere sicuro che qualsiasi codice che invii al client venga utilizzato per qualsiasi cosa, e nessun blocco che metti in atto sul client ha alcun valore di sicurezza come mai. La convalida sul client è solo per fornire un'esperienza utente fluida, non per applicare effettivamente vincoli rilevanti per la sicurezza.

Un utente malintenzionato potrebbe semplicemente cambiare quel pezzo di JavaScript nel proprio browser, o disattivare completamente gli script, o semplicemente non invia il file da un browser ma crea invece la propria richiesta POST con uno strumento come curl.

Devi riconvalidare tutto sul server. Ciò significa che il tuo PHP deve controllare che i file siano del tipo giusto, cosa che il tuo codice attuale no.

Il modo in cui farlo è una questione ampia che penso esuli dall'ambito della tua domanda, ma questa domanda sono ottimi posti per iniziare a leggere. Potresti voler dare un'occhiata anche ai tuoi file .htaccess .

Ottenere l'estensione

Non è un problema di sicurezza forse, ma questo è un modo migliore per farlo:

  $ ext = pathinfo ($ filename, PATHINFO_EXTENSION);  

Funzioni PHP magiche

Quando memorizzo i dati dalle caselle di modifica, utilizzo tutte e tre le funzioni di PHP per pulirli:

  $ cleanName = strip_tags ($ _ POST [nome]); // Rimuovi i tag HTML $ cleanName = htmlspecialchars ($ cleanName); // Consenti caratteri speciali, ma conservali in modo sicuro. $ cleanName = mysqli_real_escape_string ($ connectionName, $ cleanName);  

Questa non è una buona strategia. Il fatto che tu lo menzioni nel contesto della convalida delle estensioni di file mi preoccupa che tu stia solo lanciando un paio di funzioni relative alla sicurezza al tuo input sperando che risolva il problema.

utilizzando istruzioni preparate e non sfuggendo per proteggersi da SQLi. L'escape di tag HTML e caratteri speciali per impedire XSS deve essere eseguita dopo che i dati sono stati recuperati dal database e il modo in cui farlo dipende da dove si inseriscono i dati. E così via, e così via.

Conclusione

Non sto dicendo che questo sia cattivo, ma sembra che tu stia facendo molti errori qui. Non sarei sorpreso se ci fossero altre vulnerabilità. Se la tua app gestisce informazioni sensibili, ti consiglio vivamente di consentire a qualcuno con esperienza in materia di sicurezza di esaminarla prima di metterla in produzione.

Per fare un semplice esempio: apri il tuo sito web in Chrome.Premi F12 per aprire gli strumenti per sviluppatori.Seleziona la scheda Fonti.Vedrai tutti i tuoi JavaScript, allineati e ben modificabili.Trova la tua funzione di convalida.Eliminalo e sostituiscilo con return true;
/ \ Non solo in Chrome
I commenti non sono per discussioni estese;questa conversazione è stata [spostata in chat] (http://chat.stackexchange.com/rooms/51233/discussion-on-answer-by-anders-hacker-used-picture-upload-to-get-php-code-in-m).
Una cosa importante da sottolineare è "** ri ** convalida tutto" - solo perché stai convalidando sul server non significa che non dovresti farlo nel browser.Basta non usarlo per * sicurezza *.In questo modo i tuoi utenti possono sapere cose come "oh, ho messo una A nel mio numero di telefono, whoops" prima di inviare il modulo.
Bene, la convalida da parte dell'utente dovrebbe essere utilizzata solo per diminuire la quantità di richieste e quindi aumentare la velocità.
E per rendere le cose ancora più interessanti, ci sono sempre i poliglotti ... http://blog.portswigger.net/2016/12/bypassing-csp-using-polyglot-jpegs.html
Usare solo la convalida javascript significa nessuna convalida.permette di postare tramite curl
@Cryptopat qualcosa qualcosa Node.js
`Penso che abbiano usato un key logger sul computer di un amico` beh no, questo accade molto raramente perché molti siti sono pieni di vulnerabilità più facili da sfruttare.
Non controllerei un tipo di file guardando l'estensione del file.Ci sono strumenti là fuori per controllare il contenuto di un file e farti sapere che tipo di file è (`file` in Linux è un esempio).
@YoMismo Poiché l'estensione determina cosa viene eseguito e non dal server, lo controllerei sicuramente.Ma il tuo suggerimento potrebbe essere una buona aggiunta a questo.
@Anders, potrebbero esserci delle restrizioni per caricare alcuni tipi di file (eseguibili) ma non altri (immagini), se controlli solo le estensioni dei file invece dei contenuti qualcuno potrebbe essere in grado di caricare un eseguibile.Ciò potrebbe portare all'esecuzione di quel file che parla dell'avvento di altre vulnerabilità.E su un sistema operativo diverso da Windows un eseguibile non ha nulla a che fare con le estensioni dei file.
@YoMismo Con la configurazione standard l'estensione `.php` determina se un file viene eseguito o meno, sia su Linux che no.Non sto dicendo che il controllo di ciò che è effettivamente nel file non è corretto, sto solo dicendo che se non controlli anche l'estensione hai un problema.
#2
+36
thel3l
2017-01-04 16:12:29 UTC
view on stackexchange narkive permalink

Prima di tutto, se qualcuno ha sfruttato una funzione di caricamento di file, assicurati di verificare il file sia sul client e server . Ignorare la protezione JavaScript lato client è banale.

In secondo luogo, assicurati di seguire e implementare queste idee da OWASP.

Questo dovrebbe coprire la maggior parte delle tue i problemi. Assicurati inoltre di non avere altri difetti privi di patch sul tuo server (ad esempio, plugin obsoleti, server sfruttabili, ecc.)

Puoi approfondire le idee di "OWASP", mandare le persone lontano da * qui * non è necessariamente una buona cosa.Anche portare qui alcuni contenuti o idee migliorerebbe notevolmente questa risposta.
Sono d'accordo con @Mooz, OWASP è anche un wiki, cioè molte di queste idee possono essere modificate in seguito e alcune di esse possono aiutare l'OP in questa particolare situazione.Se ti concentri sull'aggiunta di idee / implementazioni che possono aiutare l'OP, potrei votare a favore ... Attualmente la tua risposta è solo una copia dei suggerimenti di risposte / commenti precedenti (anche il semplice suggerimento di [disabilitare PHP nella directory di caricamento] (http: //security.stackexchange.com/a/147396/121288) è più utile di un collegamento a un lungo elenco di idee in un altro sito web).
@CPHPython - Colpa mia.Lo aggiusterò.Grazie per il testa a testa.
la convalida dell'input sul lato client non è necessaria, è solo se si desidera offrire agli utenti la comodità dei primi messaggi di errore, ma dal punto di vista della sicurezza, la convalida lato client non ha alcun valore.
#3
+26
gowenfawr
2017-01-04 23:44:46 UTC
view on stackexchange narkive permalink

È estremamente importante notare qui che PHP è stato progettato per essere incorporato in altri contenuti . In particolare, è stato progettato per essere incorporato all'interno di pagine HTML, integrando una pagina in gran parte statica con piccoli frammenti di dati aggiornati dinamicamente.

E, molto semplicemente, non importa cosa sia incorporato in . L'interprete PHP scansionerà qualsiasi tipo di file arbitrario ed eseguirà qualsiasi contenuto PHP adeguatamente contrassegnato con tag script.

Il problema storico che ciò ha causato è che, sì, i caricamenti di immagini possono contenere PHP. E se il server web è disposto a filtrare quei file attraverso l'interprete PHP, allora quel codice verrà eseguito. Guarda il fiasco del plugin "Tim Thumb" di Wordpress.

La soluzione corretta è assicurarsi che il tuo server web non tratti mai e poi mai i contenuti non attendibili come qualcosa che dovrebbe funzionare tramite PHP. Tentare di filtrare l'input è un modo per farlo, ma come hai scoperto, limitato e soggetto a errori.

Molto meglio verificare che sia la posizione del file che l'estensione del file siano utilizzate per definire cosa PHP farà processo e per separare i caricamenti e altri contenuti non attendibili in posizioni ed estensioni che non verranno elaborate. (Il modo in cui lo fai varia da server a server e soffre del problema "far funzionare le cose di solito significa allentare la sicurezza in modi che non sei a conoscenza".)

Curiosamente, questa natura è stata utilizzata (senza dire che sia una cosa buona o cattiva) dalla libreria di ridimensionamento delle immagini PHP timthumb.Essenzialmente, la stringa `
@LukeA.Leber Presumo che questa sia la soluzione codificata per il vecchio problema di Tim Thumb che era che gli script incorporati nelle immagini pwnavano i server Wordpress a destra ea sinistra ... Non sono sicuro se considerarla una soluzione elegante o un hack spericolato :)
Non giudicherò l'autore, ma quando è uscito lo zero-day di Joomla 3.4.8 ho avuto un enorme spavento quando il mio scanner euristico interno stava rilevando un tag php di apertura in oltre 10.000 immagini su 3 server.Uno di quei momenti in cui puoi sentire il sangue defluire dal tuo viso.
Il fatto che tu possa caricare script che devono essere eseguiti dall'interprete non è correlato al fatto che PHP possa essere incorporato, però.Proprio come con PHP, puoi caricare script Python o Ruby.Il problema, tuttavia, è che PHP e Apache (per impostazione predefinita) espongono pubblicamente la struttura delle directory del server e ti consentono di accedere a qualsiasi file tu voglia sul server tramite HTTP.
#4
+23
medskill
2017-01-06 00:20:41 UTC
view on stackexchange narkive permalink

Puoi disabilitare PHP nella tua directory di caricamento tramite .htaccess in modo che il tuo server non esegua alcun codice PHP nella directory e nelle sue sottodirectory.

Crea un file .htaccess nella tua directory di caricamento con questo regola:

  php_flag engine off  

Tieni presente che la regola .htaccess funzionerà solo se il tuo server PHP è in esecuzione in mod_php modalità.

Modifica: ovviamente, ho dimenticato di dire che .htaccess funziona solo in apache.

Nota che ".htaccess" si applica solo ad apache.
#5
+11
iainpb
2017-01-04 19:19:48 UTC
view on stackexchange narkive permalink

Stai solo controllando l'estensione, questo non è necessariamente correlato al tipo di file effettivo. Lato server potresti usare qualcosa come exif_imagetype per verificare che il file sia un'immagine. Utilizzo del tipo di immagine exif: http://php.net/manual/en/function.exif-imagetype.php

Questa potrebbe essere una buona idea, ma ricorda che (a) potrebbe essere possibile creare un file valido sia come immagine che come PHP, e (b) un file PHP con un'estensione `.png` non fa molto malesul tuo server, poiché non verrà eseguito.
@Anders ma è ancora codice dannoso, e tutto ciò che serve per rompere le cose è qualcuno / qualcosa che gli dice di eseguire `foo.png` come file php.Sebbene sia improbabile, preferirei stare lontano dal tenere le bombe tascabili in * mia * tasca, anche se sono l'unico con un detonatore.
@Anders A meno che il tuo server non sia configurato per interpretare i file in base al loro contenuto e non all'estensione, ovviamente.La sicurezza è difficile ...
@Delioth D'accordo, ero un po 'categorico.Avrei dovuto dire è che non è così pericoloso, non che è privo di rischi.
#6
+6
LostBoy
2017-01-05 03:14:44 UTC
view on stackexchange narkive permalink

Una possibile parte di una soluzione qui è di mantenere l'immagine, ma anche di fare una copia ridimensionata con PHP in modo da assicurarti di ottenere un'immagine e ridurre le dimensioni del immagine originale. Ciò ha diversi effetti positivi:

  • Non consente mai di utilizzare l'immagine originale per la visualizzazione, quindi è più sicura
  • Consente di risparmiare spazio sul server (il che potrebbe o meno essere un problema )
  • Tempo di caricamento migliore per i visitatori del sito poiché non aspetterebbero il caricamento di un'immagine da 10 MB. Invece hanno un'immagine da 300 KB con un caricamento di dimensioni ragionevoli

Il punto principale è che non puoi fidarti dei dati degli utenti, mai. Convalida tutto, caricamenti di file, input, cookie per assicurarti che i dati non possano causare problemi.

#7
+6
akman
2017-01-06 18:24:02 UTC
view on stackexchange narkive permalink

Sono totalmente d'accordo con la risposta accettata, ma suggerirei di fare un po 'di più che giocare con il nome del file. Dovresti ricomprimere il file originale / caricato con PHP utilizzando GD o Imagick e utilizzare la nuova immagine . In questo modo, distruggi qualsiasi codice inserito (ad essere onesti, il 90% delle volte, ci sono modi per far sopravvivere il codice alla compressione, ma è molto lavoro).

Inoltre, potresti usa file .htaccess per impedire che la directory di caricamento esegua codice PHP (non so di IIS e probabilmente esiste un equivalente .webconfig).

  <FilesMatch \ .php $ > SetHandler application / x-httpd-php< / FilesMatch>  

In questo modo, il file caricato verrà eseguito solo se "l'estensione finale" è PHP, quindi image.php.jpg non lo farà essere eseguito.

Alcune risorse:

* "In questo modo, il file caricato verrà eseguito solo se" l'estensione finale "è PHP, quindi image.php.jpg non verrà eseguito." * - presumibilmente per il tipo di sito descritto dall'OP, però, dovrestidisabilita semplicemente PHP all'ingrosso nella directory dei caricamenti e non preoccuparti dell'estensione di file che ha.Se per qualsiasi motivo qualcuno fosse riuscito a caricare un file che termina con `.php`, non vorresti che fosse eseguito
@anotherdave true!La parte principale è ricomprimere l'immagine ... la seconda è solo un extra che può essere configurato in base alle proprie esigenze
#8
+2
Daniel
2017-01-06 16:00:10 UTC
view on stackexchange narkive permalink

Ho solo una piccola cosa da aggiungere a questo che non è stato toccato: usare la strategia della lista bianca.

Come altri hanno già sottolineato, fare affidamento esclusivamente sulla convalida lato client non è certamente una buona cosa. Hai anche impiegato una strategia di lista nera, il che significa che stai controllando le cose che sai / pensi siano cattive e consenti tutto il resto. Una strategia molto più efficace consiste nel verificare le cose che sai essere ok e rifiutare tutto il resto.

A volte questo è un compromesso con il feedback degli utenti, come far sapere agli utenti inesperti cosa devono fare per caricare correttamente un immagine.

Nella mia esperienza, le due strategie non si escludono necessariamente a vicenda fintanto che sono utilizzate per scopi diversi: convalida della lista nera lato client per il feedback degli utenti e convalida della lista bianca lato server per sicurezza.

#9
+2
a coder
2017-01-08 19:04:23 UTC
view on stackexchange narkive permalink

Consento il caricamento di immagini ma presumo che tutte siano caricate con codice trojan. Posiziono temporaneamente l'immagine caricata sopra la radice Web al momento del caricamento, quindi utilizzo ImageMagick per convertirla in BMP, quindi in JPG, quindi spostarla dove l'app Web può utilizzarla. Questo rimuove efficacemente qualsiasi codice PHP incorporato.

Come sottolinea @Sneftel nel commento qui sotto, l'esecuzione di questa conversione su un JPG provocherà un certo degrado dell'immagine. Per il mio scopo, questo è un compromesso accettabile.

La conversione in un BMP e poi in un JPG, in particolare se l'input era un JPG, comporterà una notevole perdita di qualità.Reimballare l'immagine non è una cattiva idea, ma dovresti farlo in un modo che non degradi l'immagine.
Per i nostri scopi un leggero degrado dell'immagine non è stato un problema.
Sto usando il pacchetto incluso con RHEL 7, che attualmente è ImageMagick 6.7
#10
+1
Tomek Leszczynski
2017-01-08 01:46:23 UTC
view on stackexchange narkive permalink

Verifica i tag EXIF ​​di un'immagine o utilizza una funzione strpos per trovare un codice PHP nel contenuto dell'immagine. Esempio:

  $ imageFile = file_get_contents ($ your_image_temp_path); // ATTENZIONE! Il percorso temporaneo per le immagini è davvero necessario per motivi di sicurezza! $ DangerousSyntax = ['<?', '<? Php', '? >']; $ error = ''; foreach ($ dangerousSyntax as $ value) {$ find = strpos ($ value, $ imageFile); if ($ find! == true) {unlink ($ your_image_temp_path); $ error = 'Abbiamo trovato un codice pericoloso nella tua immagine'; }} if (! empty ($ error)) {die ($ error);}  
Cosa ti fa pensare che un determinato file immagine non avrebbe i caratteri "
#11
  0
Grey
2017-01-06 14:37:36 UTC
view on stackexchange narkive permalink

Oltre alla risposta accettata, suggerirei di utilizzare la funzione getImageSize incorporata in PHP per ottenere il tipo MIME e assicurarsi che un file PHP non si nasconda sotto un'estensione di file immagine.

Puoi dettagliare meglio nella tua risposta come assicurarti che non ci siano tag PHP consultando il tipo MIME?Stai facendo qualcosa di simile a [esempio 1] (http://php.net/manual/en/function.getimagesize.php#example-3471) e reimpostando l'intestazione `Content-type` con uno qualsiasi dei [tipi disponibili](http://php.net/manual/en/function.image-type-to-mime-type.php)?
@CPHPython Quel commento è stato intentato per essere citato in giudizio oltre alla risposta accettata per assicurarsi che nessun tipo di file PHP (o altri non corretti) venga caricato semplicemente cambiando l'estensione (ad esempio, un file PHP, ma denominato image.jpeg).Non fornisce alcuna sicurezza contro la presenza di codice nel nome del file.


Questa domanda e risposta è stata tradotta automaticamente dalla lingua inglese. Il contenuto originale è disponibile su stackexchange, che ringraziamo per la licenza cc by-sa 3.0 con cui è distribuito.
Loading...