viernes, noviembre 28, 2014

Ataques de PHP Object Injection en aplicaciones web

Cuando se hace una auditoría de seguridad a una aplicación web construida en tecnologías PHP, uno de los tipos de ataques que se pueden realizar son los de PHP Object Injection. Este fallo se da solo en aplicaciones PHP desarrolladas bajo paradigmas de Programación Orientada a Objetos (POO) y se tienen que dar algunas circunstancias muy concretas para que se pueda dar la vulnerabilidad, pero si esto es así, se pueden hacer muchas cosas, como vamos a ver.

Figura 1: Ataques de PHP Object Injection en aplicaciones web

Para que se pueda dar la vulnerabilidad, la aplicación web - como ya se ha dicho -, debe estar construida bajo el paradigma de POO. Eso hace que la aplicación tenga definida una estructura de clases para representar todos los objetos de la aplicación, algo que ayuda a la programación de frameworks complejos y a la sostenibilidad del código a largo plazo.

Normalmente, los frameworks de Internet cumplen esta característica. Casi todos los CMS que dan soporte a aplicaciones webs, o los e-commerce, o plataformas de administración de sistemas mediante tecnologías web tienen su propia jerarquía de clases para instanciar los objetos necesarios en cada parte de la aplicación.

Magic Methods en aplicaciones PHP con POO

Cuando se definen las propiedades y funciones de una clase, existen lo que se denominan Magic Methods, que no son más que una serie de funciones que se ejecutan no por invocación explícita, sino de forma implícita cuando se da una serie de condicionantes. Por ejemplo, cuando un objeto es creado, eliminado o utilizado con una conversión de tipo de datos implícito, se producen llamadas automáticas a los Magic Methods.

En PHP hay una buena cantidad de estos que son llamados de forma automática y que el programador puede implementar para gestionar las acciones oportunas. A continuación se muestra la lista de los “Magic Methods” y sus condiciones de ejecución.
  • __construct() Las clases que tengan este método se invocarán en cada nuevo objeto creado.
  • __destruct() Será llamado cuando se liberen todas las referencias o cuando el script finalice.
  • __call() Es lanzado al invocar un método inaccesible en un contexto de objeto.
  • __callStatic() Es lanzado al invocar un método inaccesible en un contexto estático.
  • __get() Se utiliza para consultar datos a partir de propiedades inaccesibles.
  • __set() Se ejecuta al escribir datos sobre propiedades inaccesibles.
  • __isset() Se lanza al llamar a isset() o a empty() sobre propiedades inaccesibles.
  • __unset() Se invoca cuando se usa unset() sobre propiedades inaccesibles.
  • __sleep() Se ejecuta antes de cualquier serialización. Es el método serialize() el que comprueba si en la clase existe este método.
  • __wakeup() Puede reconstruir cualquier recurso que el objeto pueda tener. Es el método unserialize() el que comprueba si en la clase existe este método.
  • __toString() Permite a una clase decidir cómo comportarse cuando se la trata como a un string
  • __invoke() Es llamado cuando un script intenta llamar a un objeto como si fuera una función.
  • __set_state() Se llama en respuesta a una instancia de su objeto que se pasa a la función var_export.
  • __clone() Si se clona un objeto y este ha finalizado de clonarse, se llamará al método __clone() del nuevo objeto (si el método __clone() estuviera definido)
  • __debugInfo() Este método es invocado por var_dump() al volcar un objeto para obtener las propiedades que deberían mostrarse.
Por supuesto, estos métodos por sí mismos no suponen una vulnerabilidad en sí mismos, pero un atacante podrá forzar su llamada generando las circunstancias adecuadas para lograr que, implicitamente, sea invocado. Es decir, un atacante podría lograr aprovecharse del código que hay allí escrito - y de las vulnerabilidades que allí haya - generando los condicionantes adecuados para se ejecute y así tomar el control.

Para ello debe existir en la implementación que haya hecho el programador de uno de esos Magic Methods alguna función susceptible de ser explotada, como un eval, include, shell_exec, mysqli_query, etcétera, que utilice algún parámetro manipulable por el atacante. Es decir, el atacante podrán el valor adecuado en el parámetro que utiliza la función insegura dentro del Magic Method, y luego generará los condicionantes adecuados para que se ejecute ese método. Veamos cómo.

Serialización de Objetos

Antes de continuar es importante entender en qué consiste el concepto de ”Serializar un objeto”. La serialización nace de la necesidad que tienen muchos sistemas de descargar un objeto que se encuentra en memoria para poder ser transmitido entre distintos sistemas. Es decir, para que pueda ser almacenado en disco y/o enviado por red usando una estructura entendible por el destinatario para que pueda re-armar el objeto en memoria, es decir, para que puede "Deserializar el objeto".

Uno de los formatos más utilizados para este fin - aunque no el único - son los ficheros JSON, y en PHP se utiliza la función unserialized para manipular los ficheros JSON con el fin de recrear en memoria dicho objeto. Por ello, cuando una aplicación web recibe un fichero JSON con datos relativos a un objeto definido en su estructura de clases, llama a la función unserialized, que en nuestro caso será la puerta de entrada para forzar la ejecución del Magic Method, inyectando, como veremos, un objeto en PHP malicioso.

Un programador que recibe un JSON con información relativa a un objeto, debe sanitizar correctamente los datos antes de construir el objeto, ya que si no, estaría permitiendo la ejecución automática de los Magic Methods sin haber tomado ninguna precaución. Esto no es siempre así, y por lo general, los desarrolladores no tienden a validar las propiedades de las clases en las que no se establece un valor a través de una entrada de datos de un usuario. Es decir, valores que supuestamente ningún usuario debería haber podido manipular con el interfaz. Grave error.

Un ataque de PHP Object Injection

Como ya hemos dicho, visto todo lo anterior, un atacante podría enviar un objeto malicioso serializado en formato JSON a una aplicación web en PHP escrita bajo el paradigma de POO y en la que la clase del objeto que se envía tiene un Magic Method implementado con una función PHP insegura para la construcción o destrucción del objeto. Si la aplicación web hace uso de la  función unserialized sin sanitizar previamente los valores del objeto que viene por JSON antes de construir  o destruir el objeto, el atacante habrá podido inyectar código en el sistema para, por ejemplo, hacer un ataque de Remote Command Injection, RFI, etcétera..

En el siguiente ejemplo se puede ver una clase que tiene implementado el Magic Method para la destrucción del objeto. En la implementación se hace uso de la función de shell_exec() tomando como parámetros una de las propiedades del objeto.

Figura 2: Clase de ejemplo en PHP vulnerable a PHP Object Injection

La clase se llama “Example1”, implementa el Magic Method __destruct() y dentro del mismo se encuentra la función “shell_exec”, a la que se le pasa como parámetro la propiedad data del objeto invocado. Este código de ejemplo permite, a través de la inyección de un objeto PHP, ejecutar un comando en el sistema operativo. Esto es posible no por la inyección del objeto en sí, si no por no controlar el parámetro que se le está pasando a la función shell_exec.

Preparando el payload

Para explotar esta vulnerabilidad de forma sencilla lo primero que hay que hacer es preparar el payload. Para ello se ha de crear un objeto con la propiedad data maliciosa, es decir, con el comando que queremos inyectar dentro de shell_exec.

Figura 3: Objeto PHP malicioso para construir el Payload

A continuación se debe realizar un codificado de tipo URL del objeto PHP malicioso que se desea inyectar, por lo que tras este procesado ya se habrá generado el payload que se necesita para tomar control de esta aplicación web y será posible enviarlo a través de un parámetro una petición HTTP. En nuestro ejemplo, éste es el payload obtenido
O%3A8%3A%22Example1%22%3A1%3A%7Bs%3A4%3A%22data%22%3Bs%3A20%3A%22ipconfig+%7C+find+%22v4%22%22%3B%7D
Una vez inyectado el objeto PHP serializado en la aplicación vulnerable, el resultado devuelto es la salida del comando ejecutado en el sistema, en este caso las direcciones IP privadas del sistema.

Figura 4: Ejecución del comando a través del método __destruct()

Además, en nuestro ejemplo no solo se llamó al Magic Method __destruct, sino que también se invocaron los métodos __wakeup y __toString de forma automática, ya que el motor va utilizando de forma implícita a los métodos cuando los va necesitando.

Conclusión final de este ejemplo

Esta vulnerabilidad, a pesar de necesitar de una buena serie de condicionantes, ya se ha dado en muchos CMS y frameworks de Internet. A continuación tenéis una pequeña lista de vulnerabilidades de PHP Object Injection publicadas debido al mal uso de la función unserialized.
- Vanilla Forums 2.0 - 2.0.18.5 - PHP Object Injection Vulnerability
- Joomla! <= 3.0.3 - PHP Object Injection Vulnerability
- Joomla! <= 3.0.2 PHP Object Injection Vulnerability
- CubeCart 5.2.0 PHP Object Injection Vulnerability
- Invision Power Board <= 3.3.4 PHP Code Execution
- Tiki Wiki CMS Groupware <= 8.3 PHP Code Execution
- SugarCRM CE <= 6.3.1  PHP Code Execution
Para mitigar estos ataques es mejor no utilizar la función unserialized que construye directamente el objeto, y utilizar en su lugar la función PHP json_decode, con la que se consigue validar cada dato que se recibe para evitar la inyección de propiedades maliciosas que puedan acabar inyectadas en funciones inseguras.

Autor: Ricardo Martín (@ricardo090489)
Security QA Engineer en Eleven Paths

1 comentario:

Anónimo dijo...

>> "Esto no es siempre así, y por lo general, los desarrolladores no tienden a validar las propiedades de las clases en las que no se establece un valor a través de una entrada de datos de un usuario. Es decir, valores que supuestamente ningún usuario debería haber podido manipular con el interfaz. Grave error."

No sé si entendí mal esa parte pero ¿También es malo no validar cuándo los datos no son introducidos por un usuario? ¿de qué manera podría ser peligroso?. Yo soy de los que he acostumbrado ahorrarme validaciones en esos casos porque no lo he visto necesario (y para ahorrar código claro).

Entrada destacada

10 maneras de sacarle el jugo a tu cuenta de @MyPublicInbox si eres un Perfil Público

Cuando doy una charla a algún amigo, conocido, o a un grupo de personas que quieren conocer MyPublicInbox , siempre se acaban sorprendiendo ...

Entradas populares