LFI2RCE via Eternal waiting

Impara l'hacking su AWS da zero a eroe con htARTE (Esperto Red Team AWS di HackTricks)!

Altri modi per supportare HackTricks:

Informazioni di base

Per impostazione predefinita, quando un file viene caricato su PHP (anche se non lo sta aspettando), verrà generato un file temporaneo in /tmp con un nome come php[a-zA-Z0-9]{6}, anche se ho visto alcune immagini docker in cui i file generati non contengono cifre.

In una inclusione di file locale, se riesci ad includere quel file caricato, otterrai RCE.

Nota che per impostazione predefinita PHP consente di caricare solo 20 file in una singola richiesta (impostato in /etc/php/<version>/apache2/php.ini):

; Maximum number of files that can be uploaded via a single request
max_file_uploads = 20

Altre tecniche

Altre tecniche si basano sull'attacco ai protocolli PHP (non sarà possibile se si controlla solo l'ultima parte del percorso), sulla divulgazione del percorso del file, sull'abuso dei file attesi, o facendo sì che PHP subisca un errore di segmentazione in modo che i file temporanei caricati non vengano eliminati. Questa tecnica è molto simile alla precedente ma senza la necessità di trovare una vulnerabilità zero day.

Tecnica dell'attesa eterna

In questa tecnica abbiamo solo bisogno di controllare un percorso relativo. Se riusciamo a caricare file e a far sì che l'LFI non finisca mai, avremo "abbastanza tempo" per forzare la ricerca dei file caricati e trovare uno qualsiasi di quelli caricati.

Vantaggi di questa tecnica:

  • È sufficiente controllare un percorso relativo all'interno di un include

  • Non richiede nginx o un livello di accesso inaspettato ai file di log

  • Non richiede una vulnerabilità zero day per causare un errore di segmentazione

  • Non richiede la divulgazione del percorso

I principali problemi di questa tecnica sono:

  • È necessario che un file specifico (potrebbero essercene di più) sia presente

  • La quantità folle di nomi di file potenziali: 56800235584

  • Se il server non utilizza cifre, il totale potenziale è: 19770609664

  • Per impostazione predefinita, solo 20 file possono essere caricati in una singola richiesta.

  • Il numero massimo di worker paralleli del server utilizzato.

  • Questo limite insieme ai precedenti può far durare troppo a lungo questo attacco

  • Timeout per una richiesta PHP. Idealmente questo dovrebbe essere eterno o dovrebbe interrompere il processo PHP senza eliminare i file temporanei caricati, altrimenti sarà un problema anche questo

Quindi, come si può far sì che un include PHP non finisca mai? Semplicemente includendo il file /sys/kernel/security/apparmor/revision (purtroppo non disponibile nei container Docker).

Prova a chiamarlo semplicemente:

php -a # open php cli
include("/sys/kernel/security/apparmor/revision");

Apache2

Di default, Apache supporta 150 connessioni simultanee, seguendo https://ubiq.co/tech-blog/increase-max-connections-apache/ è possibile aumentare questo numero fino a 8000. Segui questo per utilizzare PHP con quel modulo: https://www.digitalocean.com/community/tutorials/how-to-configure-apache-http-with-mpm-event-and-php-fpm-on-ubuntu-18-04.

Di default, (come posso vedere nei miei test), un processo PHP può durare eternamente.

Facciamo un po' di calcoli:

  • Possiamo utilizzare 149 connessioni per generare 149 * 20 = 2980 file temporanei con la nostra webshell.

  • Quindi, utilizzare l'ultima connessione per forzare potenziali file.

  • A una velocità di 10 richieste al secondo i tempi sono:

  • 56800235584 / 2980 / 10 / 3600 ~= 530 ore (50% di probabilità in 265h)

  • (senza cifre) 19770609664 / 2980 / 10 / 3600 ~= 185h (50% di probabilità in 93h)

Nota che nell'esempio precedente stiamo completamente facendo un DoS ad altri client!

Se il server Apache viene migliorato e potremmo abusare di 4000 connessioni (metà del numero massimo). Potremmo creare 3999*20 = 79980 file e il numero sarebbe ridotto a circa 19.7 ore o 6.9 ore (10 ore, 3.5 ore 50% di probabilità).

PHP-FMP

Se invece di utilizzare il modulo php regolare per apache per eseguire gli script PHP la pagina web sta utilizzando PHP-FMP (questo migliora l'efficienza della pagina web, quindi è comune trovarlo), c'è qualcos'altro che può essere fatto per migliorare la tecnica.

PHP-FMP consente di configurare il parametro request_terminate_timeout in /etc/php/<php-version>/fpm/pool.d/www.conf. Questo parametro indica il massimo numero di secondi quando la richiesta a PHP deve terminare (infinito per impostazione predefinita, ma 30s se il parametro è scommentato). Quando una richiesta viene elaborata da PHP per il numero di secondi indicato, viene terminata. Ciò significa che se la richiesta stava caricando file temporanei, poiché l'elaborazione php è stata interrotta, quei file non verranno eliminati. Pertanto, se riesci a far durare una richiesta quel tempo, puoi generare migliaia di file temporanei che non verranno eliminati, il che accelererà il processo di trovarli e ridurrà la probabilità di un DoS alla piattaforma consumando tutte le connessioni.

Quindi, per evitare un DoS supponiamo che un attaccante utilizzerà solo 100 connessioni contemporaneamente e il tempo massimo di elaborazione php tramite php-fmp (request_terminate_timeout) è 30s. Pertanto, il numero di file temporanei che possono essere generati al secondo è 100*20/30 = 66.67.

Quindi, per generare 10000 file un attaccante avrebbe bisogno di: 10000/66.67 = 150s (per generare 100000 file il tempo sarebbe 25 minuti).

Quindi, l'attaccante potrebbe utilizzare quelle 100 connessioni per eseguire una ricerca brute-force. **** Supponendo una velocità di 300 req/s il tempo necessario per sfruttare questo è il seguente:

  • 56800235584 / 10000 / 300 / 3600 ~= 5.25 ore (50% di probabilità in 2.63h)

  • (con 100000 file) 56800235584 / 100000 / 300 / 3600 ~= 0.525 ore (50% di probabilità in 0.263h)

Sì, è possibile generare 100000 file temporanei in un'istanza di dimensioni medie EC2:

Nota che per attivare il timeout sarebbe sufficiente includere la pagina LFI vulnerabile, in modo che entri in un loop di inclusione eterno.

Nginx

Sembra che di default Nginx supporti 512 connessioni parallele contemporaneamente (e questo numero può essere migliorato).

Last updated