7.4 KiB
LFI2RCE a través de la espera eterna
Aprende hacking en AWS de cero a héroe con htARTE (Experto en Red Team de AWS de HackTricks)!
Otras formas de apoyar a HackTricks:
- Si deseas ver tu empresa anunciada en HackTricks o descargar HackTricks en PDF Consulta los PLANES DE SUSCRIPCIÓN!
- Obtén el swag oficial de PEASS & HackTricks
- Descubre La Familia PEASS, nuestra colección exclusiva de NFTs
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @carlospolopm.
- Comparte tus trucos de hacking enviando PRs a los HackTricks y HackTricks Cloud repositorios de github.
Información Básica
Por defecto, cuando se carga un archivo en PHP (incluso si no se espera), generará un archivo temporal en /tmp
con un nombre como php[a-zA-Z0-9]{6}
, aunque he visto algunas imágenes de docker donde los archivos generados no contienen dígitos.
En una inclusión de archivo local, si logras incluir ese archivo cargado, obtendrás RCE.
Ten en cuenta que por defecto PHP solo permite cargar 20 archivos en una única solicitud (configurado en /etc/php/<versión>/apache2/php.ini
):
; Maximum number of files that can be uploaded via a single request
max_file_uploads = 20
Técnica de espera eterna
En esta técnica solo necesitamos controlar una ruta relativa. Si logramos cargar archivos y hacer que la LFI nunca termine, tendremos "suficiente tiempo" para realizar fuerza bruta en los archivos cargados y encontrar cualquiera de los que se hayan subido.
Ventajas de esta técnica:
- Solo necesitas controlar una ruta relativa dentro de una inclusión
- No requiere nginx ni un nivel inesperado de acceso a los archivos de registro
- No requiere un zero day para causar una falla de segmentación
- No requiere una divulgación de ruta
Los principales problemas de esta técnica son:
- Necesita que un archivo específico (o archivos) esté presente (puede haber más)
- La enorme cantidad de nombres de archivo potenciales: 56800235584
- Si el servidor no está utilizando dígitos, la cantidad total potencial es: 19770609664
- Por defecto, solo se pueden cargar 20 archivos en una sola solicitud.
- El número máximo de trabajadores en paralelo del servidor utilizado.
- Este límite junto con los anteriores puede hacer que este ataque dure demasiado
- Tiempo de espera para una solicitud de PHP. Idealmente, esto debería ser eterno o debería finalizar el proceso de PHP sin eliminar los archivos temporales cargados; de lo contrario, también será un dolor
Entonces, ¿cómo puedes hacer que una inclusión de PHP nunca termine? Simplemente incluyendo el archivo /sys/kernel/security/apparmor/revision
(desafortunadamente no disponible en contenedores Docker).
Inténtalo simplemente llamando:
php -a # open php cli
include("/sys/kernel/security/apparmor/revision");
Apache2
Por defecto, Apache admite 150 conexiones concurrentes, siguiendo https://ubiq.co/tech-blog/increase-max-connections-apache/ es posible aumentar este número hasta 8000. Sigue esto para usar PHP con ese módulo: https://www.digitalocean.com/community/tutorials/how-to-configure-apache-http-with-mpm-event-and-php-fpm-on-ubuntu-18-04.
Por defecto, (como puedo ver en mis pruebas), un proceso PHP puede durar eternamente.
Hagamos algunos cálculos:
- Podemos usar 149 conexiones para generar 149 * 20 = 2980 archivos temporales con nuestra webshell.
- Luego, usar la última conexión para bruteforce posibles archivos.
- A una velocidad de 10 solicitudes/s los tiempos son:
- 56800235584 / 2980 / 10 / 3600 ~= 530 horas (50% de probabilidad en 265h)
- (sin decimales) 19770609664 / 2980 / 10 / 3600 ~= 185h (50% de probabilidad en 93h)
{% hint style="warning" %} ¡Ten en cuenta que en el ejemplo anterior estamos haciendo un DoS completo a otros clientes! {% endhint %}
Si el servidor Apache se mejora y pudiéramos abusar de 4000 conexiones (a mitad de camino del número máximo). Podríamos crear 3999*20 = 79980
archivos y el número se reduciría a alrededor de 19.7h o 6.9h (10h, 3.5h 50% de probabilidad).
PHP-FMP
Si en lugar de usar el módulo php regular para apache para ejecutar scripts PHP la página web está utilizando PHP-FMP (esto mejora la eficiencia de la página web, por lo que es común encontrarlo), hay algo más que se puede hacer para mejorar la técnica.
PHP-FMP permite configurar el parámetro request_terminate_timeout
en /etc/php/<versión-php>/fpm/pool.d/www.conf
.
Este parámetro indica la cantidad máxima de segundos cuando la solicitud a PHP debe terminar (infinito de forma predeterminada, pero 30s si el parámetro está descomentado). Cuando una solicitud está siendo procesada por PHP durante el número indicado de segundos, es eliminada. Esto significa que si la solicitud estaba subiendo archivos temporales, debido a que el procesamiento de php se detuvo, esos archivos no se eliminarán. Por lo tanto, si puedes hacer que una solicitud dure ese tiempo, puedes generar miles de archivos temporales que no se eliminarán, lo que acelerará el proceso de encontrarlos y reducirá la probabilidad de un DoS a la plataforma al consumir todas las conexiones.
Entonces, para evitar un DoS supongamos que un atacante estará usando solo 100 conexiones al mismo tiempo y el tiempo máximo de procesamiento por php-fmp (request_terminate_timeout
) es 30s. Por lo tanto, la cantidad de archivos temporales que se pueden generar por segundo es 100*20/30 = 66.67
.
Luego, para generar 10000 archivos un atacante necesitaría: 10000/66.67 = 150s
(para generar 100000 archivos el tiempo sería 25min).
Luego, el atacante podría usar esas 100 conexiones para realizar un bruteforce de búsqueda. Suponiendo una velocidad de 300 req/s, el tiempo necesario para explotar esto es el siguiente:
- 56800235584 / 10000 / 300 / 3600 ~= 5.25 horas (50% de probabilidad en 2.63h)
- (con 100000 archivos) 56800235584 / 100000 / 300 / 3600 ~= 0.525 horas (50% de probabilidad en 0.263h)
Sí, es posible generar 100000 archivos temporales en una instancia de tamaño mediano de EC2:
{% hint style="warning" %} Ten en cuenta que para activar el tiempo de espera sería suficiente incluir la página LFI vulnerable, para que entre en un bucle de inclusión eterno. {% endhint %}
Nginx
Parece que por defecto Nginx admite 512 conexiones paralelas al mismo tiempo (y este número puede mejorarse).