Ir al contenido
Nuevo informe: Consiga su copia del Informe sobre el estado de Internet 2024. | Descargar hoy
Blogs

CVE-2021-41773: Apache Path Traversal

Introducción

El 22 de julio de 2021, el popular servidor web Apache HTTPD se fusionó en un escriba a que sustituyó a la función ap_getparents() con una nueva función llamada ap_normalize_path(). Esta nueva función se promocionó como una forma más eficiente y estándar de tratar la normalización de las rutas sin procesar que se encuentran en el URI de una petición HTTP. El 15 de septiembre, estos cambios fueron incorporado al tronco y etiquetada como versión 2.4.49.

¿Cuál es el problema?

El 29 de septiembre, catorce días después del lanzamiento de la versión 2.4.49, un ingeniero de cPanel llamado Ash Daulton informó de que un atacante podría utilizar esta nueva lógica para ejecutar un exploit path-traversal, que podría conducir a la ejecución remota de código (RCE) cuando se cumplen ciertas condiciones de configuración.

Apache v2.4.49 a vista de pájaro

Censys encontró 75.085 hosts únicos que ejecutaban 136.469 servicios que se autoidentificaban como Apache HTTPD versión 2.4. 49 utilizando esta simple consulta de búsqueda. Y aunque la mayoría de las instalaciones de este servicio no son vulnerables desde el primer momento, Censys ha identificado algunos proveedores que ofrecen una configuración poco estricta, lo que permite el fallo. Uno de estos proveedores, Control Webpanel, con más de 22.000 hosts con CentOS, parece ser un objetivo probable para este ataque debido a la configuración que incluye.

Algunos investigadores han afirm ado que esta vulnerabilidad se está explorando activamente en la naturaleza, aunque no se ha hecho ninguna declaración definitiva sobre si las exploraciones están dando lugar a una explotación activa.

Por defecto, se establece la siguiente configuración de control de acceso:

DocumentRoot "/usr/local/apache2/htdocs"
<Directory />
    AllowOverride none
    Require all denied
</Directory>
<Directory "/usr/local/apache2/htdocs">
    AllowOverride None
    Require all granted
</Directory>

Si un usuario solicitara "/index.html"el servidor tomaría el DocumentRoot (/usr/local/apache2/htdocs) y añadir "/index.html" a la misma, dando como resultado el archivo "/usr/local/apache2/htdocs/index.html". Dado que una regla permite el acceso a todos los archivos dentro del "/usr/local/apache2/htdocs" la solicitud estaría permitida.

Por otra parte, si un usuario solicitara "/../../../../foobar"el servidor recortará el "/../../../.." vía ap_normalize_path() y añadir el resultado al archivo DocumentRootEl resultado es "/usr/local/apache2/htdocs/foobar"; un archivo que no debería existir en el sistema de archivos.

Pero como ap_normalize_path() no comprueba correctamente los puntos dobles cuando se codifica como ascii-hex, por ejemplo, "%2e%2e"esta función de normalización mostrará los puntos y no eliminará la ruta. Pero incluso después de la representación del doble punto, los controles de acceso por defecto deberían impedir la ejecución de la mayoría de las rutas. Sólo cuando un administrador establece explícitamente la configuración "Require all granted" en un directorio protegido que las cosas pueden salirse de control; es decir,

<Directory />
    AllowOverride none
    Require all granted
</Directory>

Esta configuración incorrecta, combinada con la configuración de Apache mod_cgi tiene el potencial de convertir esta simple vulnerabilidad de path-traversal en un ataque completo de ejecución remota de comandos (RCE).

Una mirada más profunda

Aunque este nuevo ap_normalize_path() desempeña un papel aquí, no es exactamente la función causa de la vulnerabilidad. Es más bien que esta función ayudó a que un problema ya existente saliera a la luz. Quiero decir, la prueba está en el nombre: es "normalizar", no "desinfectar". Pero este cambio tuvo efectos secundarios no deseados para otras funciones que dependían de esta iteración previa que saneaba los datos.

El verdadero problema está oculto en una función abstracta del sistema operativo llamada apr_filepath_merge()y, más concretamente, cómo se llama a esta función. Pero antes de hablar de cómo esta función es llamadodebemos entender qué esta función hace. La documentación indica que esta función:

"Fusionar ruta de archivo adicional en la ruta raíz procesada previamente"

El objetivo principal de esta función es tomar una ruta base (como un apache DocumentRoot), y una ruta pasada en el URI de una petición, y fusionarlos para asignar el URI de entrada a una ruta totalmente cualificada en el servidor.

He aquí una traducción muy básica de lo que hace esta función:

final_path = split("/usr/local/apache2/htdocs", "/") // result: ["usr", "local", "apache2", "htdocs"]
tokens     = split("usr/bin/..", "/")  // result: ["usr", "bin", ".."]
​
for each path_part in tokens:
    if path_part == "..":
        // remove the last segment in the final_path array
        final_path[length(final_path)] = null
    else:
        final_path.append(path_part)
​
return final_path.Join("/") // returns /usr/local/apache2/htdocs/usr 

Cuando se llama a esta función desde el gestor de Apache por defecto (ap_core_translate()), primero tomará la ruta de la petición de entrada y saltará la primera barra para indicar que se trata de una ruta relativa desde el archivo DocumentRoot. Por ejemplo, si el servidor está configurado con DocumentRoot "/usr/local/apache2/htdocs"y la solicitud entrante es:

GET /foo/bar HTTP/1.0

En apr_filename_merge() se llamará así:

char * path = "/foo/bar";
char * root = "/usr/local/apache2/htdocs";
char * output;
​
while (*path == '/') {
    ++path;
}
​
int rv = apr_filepath_merge(&output, root, path, APR_FILEPATH_SECUREROOT, r->pool));

Lo que daría lugar a la output tampón que contiene: /usr/local/apache2/htdocs/foo/bar. La razón por la que esta llamada a apr_filepath_merge es no vulnerable al ataque path-traversal se debe principalmente a la bandera APR_FILEPATH_SECUREROOT . Esto hará que la función falle si la ruta que intentamos añadir queda fuera del directorio "/usr/local/apache2/htdocs".

Pero no todas las peticiones son procesadas por el gestor por defecto, algunas son procesadas por módulos. Por ejemplo, el módulo mod_alias debe primero hacer coincidir la ruta de entrada con una ruta base, y esa ruta base debe expandirse a una ruta real. Tomemos la siguiente configuración:

<IfModule alias_module>
    ScriptAlias /cgi-bin/ "/usr/local/apache2/cgi-bin/"
</IfModule>

Esto establece que cualquier ruta URI entrante que empiece por "/cgi-bin/" debe ir en realidad al directorio "/usr/local/apache2/cgi-bin/". Así que la forma en que la URI se traduce a un archivo es diferente de lo que es en ap_core_translate(). Primero, mod_alias iterará sobre una lista de alias_entrycon el siguiente formato:

alias_entry entry = {
    .real = "/usr/local/apache2/cgi-bin/",
    .fake = "/cgi-bin/",
};

Mirará el prefijo de la ruta de la solicitud entrante y, si coincide con el prefijo fake es decir, "/cgi-bin/", el contenido de la URI entrante se añade al elemento real que a su vez se utiliza para llamar a apr_filepath_merge:

char * output_path = NULL;
​
prefix_len = alias_matches(request->uri, alias->fake);
​
if (prefix_len > 0) {
    // Append the contents of the request URI after the prefix to the end of the "real" element
    output_path = apr_pstrcat(request->pool, alias->real, request->uri + prefix_len, NULL);
    // if alias->real  IS "/usr/local/apache2/cgi-bin/"
    // AND
    // if request->uri IS "/cgi-bin/../../../etc"
    // THEN
    // output_path     IS "/usr/local/apache2/cgi-bin/../../../etc"
}
​
if (output_path != NULL) {
    char * newpath = NULL;
    int ret = apr_filepath_merge(&newpath, "/usr/local/apache2", output_path, 0, p);
    
    if (newpath != NULL && ret == APR_SUCCESS) {
        return newpath; // newpath IS /usr/etc
    }
}

Como ya se habrá dado cuenta, el APR_FILEPATH_SECUREROOT no se ha pasado a apr_filepath_merge como en ap_core_translate()lo que significa que aunque el URI contenga puntos dobles, la llamada no dará error - por lo tanto mod_alias es vulnerable al ataque path-traversal.

Pero para ser perfectamente claro, sólo porque esta función permitió que el traversal fuera renderizado, el servidor necesita ser configurado de tal manera que permita el acceso al directorio atravesado. Utilizando el ejemplo anterior de "/usr/etc", un administrador debe permitir explícitamente al servidor el acceso al directorio "/usr/etc" con una configuración de "Requerir todo concedido". De lo contrario, la solicitud fallará en una comprobación secundaria de la función ap_run_access_checker_ex().

¿Qué importancia tiene?

Si se dan las condiciones adecuadas, un atacante puede aprovechar esta vulnerabilidad path-traversal para ejecutar cualquier comando con los mismos permisos que el usuario que ejecuta el servicio. Un atacante puede entonces utilizar este acceso para obtener permisos administrativos elevados si existe alguna vulnerabilidad local no parcheada en el sistema.

Dado que esta vulnerabilidad es fácil de ejecutar y que las pruebas de concepto se encuentran fácilmente en las redes sociales y otros medios, debemos suponer que los malhechores ya han empezado a explotar los servidores vulnerables.

¿Qué hago al respecto?

Actualización 10-07-2021 (7:30 PM EST): Promotores de la CentOS Webpanel tienen proporcionó una actualización. Ejecutar manualmente /scripts/update_cwp degradará el servidor Apache HTTPD a la versión 2.4.48.

Sobre el autor

Mark Ellzey
Senior Security Researcher Todos los puestos de Mark Ellzey
Mark Ellzey es investigador principal de seguridad en Censys. Antes de ocupar su puesto actual, Mark ha trabajado como ingeniero de seguridad de redes y desarrollador de software para varios proveedores de servicios de Internet e instituciones financieras durante más de 22 años.
Soluciones de gestión de la superficie de ataque
Más información