LFI2RCE via Eternal waiting
Last updated
Last updated
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Per impostazione predefinita, quando un file viene caricato su PHP (anche se non lo sta aspettando), genera 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 un'inclusione di file locale, se riesci a 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
):
Also, il numero di nomi di file potenziali è 62*62*62*62*62*62 = 56800235584
Altre tecniche si basano sull'attacco ai protocolli PHP (non sarai in grado se controlli solo l'ultima parte del percorso), divulgando il percorso del file, abusando di file attesi, o facendo soffrire PHP di un errore di segmentazione in modo che i file temporanei caricati non vengano eliminati. Questa tecnica è molto simile all'ultima ma senza dover trovare un zero day.
In questa tecnica abbiamo solo bisogno di controllare un percorso relativo. Se riusciamo a caricare file e far sì che LFI non finisca mai, avremo "abbastanza tempo" per forzare i file caricati e trovare uno di quelli caricati.
Vantaggi di questa tecnica:
Devi solo controllare un percorso relativo all'interno di un include
Non richiede nginx o un livello inaspettato di accesso ai file di log
Non richiede un 0 day per causare un errore di segmentazione
Non richiede una divulgazione del percorso
I principali problemi di questa tecnica sono:
Necessità che un file specifico sia presente (potrebbero essercene di più)
L'insana quantità di nomi di file potenziali: 56800235584
Se il server non utilizza cifre la quantità totale potenziale è: 19770609664
Per impostazione predefinita solo 20 file possono essere caricati in una singola richiesta.
Il numero massimo di lavoratori paralleli del server utilizzato.
Questo limite con i precedenti può far durare troppo a lungo questo attacco
Timeout per una richiesta PHP. Idealmente questo dovrebbe essere eterno o dovrebbe terminare il processo PHP senza eliminare i file temporanei caricati, altrimenti, questo sarà anche un problema
Quindi, come puoi far sì che un include PHP non finisca mai? Basta includere il file /sys/kernel/security/apparmor/revision
(non disponibile nei container Docker purtroppo...).
Provalo semplicemente chiamando:
Per impostazione predefinita, Apache supporta 150 connessioni concorrenti, 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.
Per impostazione predefinita, (come posso vedere nei miei test), un processo PHP può durare eternamente.
Facciamo un po' di conti:
Possiamo utilizzare 149 connessioni per generare 149 * 20 = 2980 file temporanei con il nostro webshell.
Poi, usa la ultima connessione per brute-forzare file potenziali.
A una velocità di 10 richieste/s 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 DoSando altri clienti!
Se il server Apache è migliorato e potessimo abusare di 4000 connessioni (metà del numero massimo). Potremmo creare 3999*20 = 79980
file e il numero sarebbe ridotto a circa 19.7h o 6.9h (10h, 3.5h 50% di probabilità).
Se invece di utilizzare il modulo php regolare per apache per eseguire 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 la quantità massima di secondi quando la richiesta a PHP deve terminare (infinito per impostazione predefinita, ma 30s se il parametro è decommentato). Quando una richiesta viene elaborata da PHP per il numero di secondi indicato, viene terminata. Questo significa che, se la richiesta stava caricando file temporanei, poiché il processo PHP è stato interrotto, 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 riduce la probabilità di un DoS per la piattaforma consumando tutte le connessioni.
Quindi, per evitare DoS, supponiamo che un attaccante utilizzerà solo 100 connessioni contemporaneamente e il tempo massimo di elaborazione PHP per 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 25min).
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 EC2 di dimensioni medie:
Nota che per attivare il timeout sarebbe sufficiente includere la pagina LFI vulnerabile, in modo che entri in un ciclo di inclusione eterno.
Sembra che per impostazione predefinita Nginx supporti 512 connessioni parallele contemporaneamente (e questo numero può essere migliorato).
Impara e pratica AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Impara e pratica GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)