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 M
agic 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