De LFI a RCE

· · | Ciberseguridad en De LFI a RCE

En este artículo, procederemos a explicar cómo un servidor que utiliza PHP, bajo un conjunto de condiciones determinadas, decanta en la posibilidad de ejecutar comandos arbitrarios en el servidor.

Al momento de construir una página web, un desarrollador podría identificar segmentos de código que vayan a repetirse en más de una página. Para esto, es típico usar la función include y su semejante require.

Citando la documentación oficial de php para la función include:

La sentencia include incluye y evalúa el archivo especificado[…] Cuando un archivo es incluido, el intérprete abandona el modo PHP e ingresa al modo HTML al comienzo del archivo objetivo y se reanuda de nuevo al final.
Por esta razón, cualquier código al interior del archivo objetivo que deba ser ejecutado como código PHP, tendrá que ser encerrado dentro de etiquetas válidas de comienzo y terminación de PHP.

En otras palabras, el contenido del fichero se interpretará como HTML hasta que encuentre una etiqueta PHP. Es decir, como si hubiéramos copiado y pegado el contenido del fichero a incluir dentro del archivo que se está renderizando en el navegador.

Esta propiedad, cuando no está correctamente sanitizada, puede servir para obtener información del servidor como: usuarios, ficheros de configuración, puertos abiertos, dispositivos conectados, etc. La explotación de esta vulnerabilidad se consigue introduciendo ficheros que no se supone que deberían ser incluidos.

A modo de demostración, se usará DVWA como ejemplo, ya que nos proporciona un laboratorio con la vulnerabilidad LFI en una máquina GNU/Linux,  y BurpSuite para controlar con más comodidad las cabeceras de las consultas y hacer una pequeña demostración:

Para aprovechar adecuadamente una vulnerabilidad LFI, es importante conocer la localización de los ficheros de configuración o contar con una lista de archivos apropiados para enumerarlos.

Sin embargo, como ya hemos visto en la documentación, esto no será suficiente para obtener los ficheros PHP, ya que debido a la misma naturaleza de la función include, estos se interpretarán antes de que se muestren en la pantalla. Para poder obtener el contenido, utilizamos los wrappers. En particular, filtraremos el contenido codificando este en Base64:

Una vez obtenida la información en Base64, es sencillo decodificarla ya sea usando herramientas web o utilidades instaladas en el sistema operativo de su preferencia. Combinando este método con una buena lista y un script de automatización, se puede obtener información importante del funcionamiento del servicio web:

[email protected]:~$ base64 -d
PD9waHANCg0KLy8gQ2hlY2sgaWYgdGhlIHJpZ2h0IFBIUCBmdW5jdGlvbnMgYXJlIGVuYWJsZWQNCiRXYXJuaW5nSHRtbCA9ICcnOw0KaWYoICFpbmlfZ2V0KCAnYWxsb3dfdXJsX2luY2x1ZGUnICkgKSB7DQoJJFdhcm5pbmdIdG1sIC49ICI8ZGl2IGNsYXNzPVwid2FybmluZ1wiPlRoZSBQSFAgZnVuY3Rpb24gPGVtPmFsbG93X3VybF9pbmNsdWRlPC9lbT4gaXMgbm90IGVuYWJsZWQuPC9kaXY+IjsNCn0NCmlmKCAhaW5pX2dldCggJ2FsbG93X3VybF9mb3BlbicgKSApIHsNCgkkV2FybmluZ0h0bWwgLj0gIjxkaXYgY2xhc3M9XCJ3YXJuaW5nXCI+VGhlIFBIUCBmdW5jdGlvbiA8ZW0+YWxsb3dfdXJsX2ZvcGVuPC9lbT4gaXMgbm90IGVuYWJsZWQuPC9kaXY+IjsNCn0NCg0KDQokcGFnZVsgJ2JvZHknIF0gLj0gIg0KPGRpdiBjbGFzcz1cImJvZHlfcGFkZGVkXCI+DQoJPGgxPlZ1bG5lcmFiaWxpdHk6IEZpbGUgSW5jbHVzaW9uPC9oMT4NCg0KCXskV2FybmluZ0h0bWx9DQoNCgk8ZGl2IGNsYXNzPVwidnVsbmVyYWJsZV9jb2RlX2FyZWFcIj4NCgkJWzxlbT48YSBocmVmPVwiP3BhZ2U9ZmlsZTEucGhwXCI+ZmlsZTEucGhwPC9hPjwvZW0+XSAtIFs8ZW0+PGEgaHJlZj1cIj9wYWdlPWZpbGUyLnBocFwiPmZpbGUyLnBocDwvYT48L2VtPl0gLSBbPGVtPjxhIGhyZWY9XCI/cGFnZT1maWxlMy5waHBcIj5maWxlMy5waHA8L2E+PC9lbT5dDQoJPC9kaXY+DQoNCgk8aDI+TW9yZSBJbmZvcm1hdGlvbjwvaDI+DQoJPHVsPg0KCQk8bGk+IiAuIGR2d2FFeHRlcm5hbExpbmtVcmxHZXQoICdodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9SZW1vdGVfRmlsZV9JbmNsdXNpb24nICkgLiAiPC9saT4NCgkJPGxpPiIgLiBkdndhRXh0ZXJuYWxMaW5rVXJsR2V0KCAnaHR0cHM6Ly93d3cub3dhc3Aub3JnL2luZGV4LnBocC9Ub3BfMTBfMjAwNy1BMycgKSAuICI8L2xpPg0KCTwvdWw+DQo8L2Rpdj5cbiI7DQoNCj8+DQo=
<?php

// Check if the right PHP functions are enabled
$WarningHtml = '';
if( !ini_get( 'allow_url_include' ) ) {
        $WarningHtml .= "<div class=\"warning\">The PHP function <em>allow_url_include</em> is not enabled.</div>";
}
if( !ini_get( 'allow_url_fopen' ) ) {
        $WarningHtml .= "<div class=\"warning\">The PHP function <em>allow_url_fopen</em> is not enabled.</div>";
}


$page[ 'body' ] .= "
<div class=\"body_padded\">
        <h1>Vulnerability: File Inclusion</h1>

        {$WarningHtml}

        <div class=\"vulnerable_code_area\">
                [<em><a href=\"?page=file1.php\">file1.php</a></em>] - [<em><a href=\"?page=file2.php\">file2.php</a></em>] - [<em><a href=\"?page=file3.php\">file3.php</a></em>]
        </div>

        <h2>More Information</h2>
        <ul>
                <li>" . dvwaExternalLinkUrlGet( 'https://en.wikipedia.org/wiki/Remote_File_Inclusion' ) . "</li>
                <li>" . dvwaExternalLinkUrlGet( 'https://www.owasp.org/index.php/Top_10_2007-A3' ) . "</li>
        </ul>
</div>\n";

?>

Para poder ejecutar código en el servidor remoto (Remote Code Execution), nuestro LFI debe ir acompañado de determinadas condiciones, una de ellas es una desafortunada gestión de privilegios de los ficheros de registro, también llamados logs.

Podría ser que el fichero que almacena los registros de apache2 tenga privilegios de lectura para el usuario que se encarga de manejar el servicio HTTP, permitiéndonos leer su contenido a través del LFI. Como este fichero se modifica cada vez que algún cliente consulta al servidor, podemos realizar una consulta que inserte una etiqueta PHP con código controlado por nosotros. Para hacerlo bien, debemos observar los logs del servicio que se pretende explotar:

En el caso de ejemplo, ponemos nuestra carga útil (payload) en el User-Agent. Esto se puede hacer a través de un proxy web o mediante una consulta curl. Para este ejemplo, se utilizó la herramienta BurpSuite:

Luego, se realiza una consulta incluyendo el fichero de registro (log) seleccionado para ver que el código se ha ejecutado en el servidor remoto:

Esta técnica que se denomina envenenamiento de registro o log poisoning, se puede aplicar a todo fichero de registro que tenga los permisos apropiados. Algunos servicios que también deben tenerse en cuenta son ssh y ftp. Sin embargo, esta vulnerabilidad no se limita a los registros (logs) solamente, ya que si un atacante encuentra una manera de subir información a un servidor, cosa común y necesaria en muchos servicios, podrá transformar el LFI en un RCE en tanto conozca la ruta del fichero con código malicioso que ya ha cargado en el servidor.

Cuando la opción allow_url_include de PHP se encuentra puesta en On, se puede usar el wrapper php://input y enviar el código a ejecutarse mediante una consulta POST, donde nuestra carga útil se encontrará en el cuerpo de la petición HTTP:

Conclusión

Como conclusión, se ha demostrado dos situaciones en las que se puede extender una vulnerabilidad LFI a una vulnerabilidad RCE, conviene reflexionar un poco sobre el impacto potencial y la importancia de prevenir este tipo de vulnerabilidades críticas. Lo usual, ante una vulnerabilidad RCE, es que sea utilizada para subir una web shell o ejecutar código que permita obtener una shell reversa y así obtener acceso al servidor para posteriormente escalar privilegios, control total sobre este y eventualmente comprometer la red corporativa de una organización.