Skip to content
KitploitKITPLOIT
HerramientasExploitsBlog
Enviar
HerramientasExploitsBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
CVE-2026-18322 — Análisis de causa raíz, verificador de versión pasivo y PoC de laboratorio para CVE-2026-18322, una escalada de privilegios no autenticada en el plugin de WordPress Smart Popup by Supsystic. | Kitploit
Herramientas/GitHubGitHub/i3it/cve-2026-18322
Escalada de PrivilegiosEscáneres de VulnerabilidadesAnálisis de VulnerabilidadesExplotaciónExplotación de Aplicaciones WebVirtualización de SeguridadSeguridad WebPruebas de PenetraciónPapers e InvestigaciónLabs y Práctica
1hace 26 díasAún no revisado

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Compartir
GitHub
i3it/cve-2026-18322

CVE-2026-18322

Análisis de causa raíz, verificador de versión pasivo y PoC de laboratorio para CVE-2026-18322, una escalada de privilegios no autenticada en el plugin de WordPress Smart Popup by Supsystic.

Ver Repositorio

CVE-2026-18322 — Escalada de privilegios en Smart Popup by Supsystic

CVE Severity Affected Fixed in CWE Python Tests License

Investigación de seguridad sobre CVE-2026-18322, una vulnerabilidad de escalada de privilegios no autenticada en el plugin de WordPress Smart Popup by Supsystic (popup-by-supsystic). Este repositorio contiene un análisis de causa raíz derivado del diff del código fuente upstream, los parches extraídos, una herramienta de detección (fingerprinter pasivo de versiones) para identificar instalaciones afectadas y un laboratorio para el PoC de explotación.

La vulnerabilidad es pública y está parcheada. Este trabajo se publica para uso defensivo: ayudar a los operadores a encontrar y remediar hosts afectados.


Versiones afectadas

Rango de versionesEstado
< 1.13.0Vulnerable
>= 1.13.0Corregida

1.13.0 (publicada el 31.07.2026) es la primera versión parcheada; repara los tres eslabones de la cadena descrita a continuación. Véase §1 del análisis.


Resumen de la vulnerabilidad

Tres debilidades independientes se combinan para permitir la creación de un administrador sin autenticación:

1. Una colisión en el mapa de permisos — havePermissions() combinaba dos mapas de permisos con array_merge(). Ambos usan la clave de tipo string PPS_USERLEVELS ('userlevels'), y array_merge() sobrescribe las claves de tipo string, por lo que la lista corta por defecto del controlador base reemplazó por completo la lista del módulo popup — eliminando silenciosamente la restricción de administrador de save y otros nueve métodos:

root@kitploit:~
$permissions     = $mod->getController()->getPermissions();      // [... 'save' ...]
$permissionsBase = $mod->getController()->getBasePermissions();  // ['getListForTbl','removeGroup','clear']
$permissions     = array_merge($permissions, $permissionsBase);  // base wins — 'save' is gone

La comprobación falla abierta: una acción ausente del mapa nunca se deniega.

2. Un nonce reutilizable enviado por correo a desconocidos — save seguía requiriendo un pps_nonce, pero el correo de confirmación de suscripción incluía esa misma acción de nonce. Para usuarios no autenticados, un nonce de WordPress se basa en uid=0, por lo que el token generado para un suscriptor anónimo es válido para cualquier atacante anónimo.

3. Sin lista de roles permitidos del lado del servidor — createWpSubscriber() pasaba el rol configurado directamente a WP_User::set_role(). La lista de roles seguros del plugin (que excluye administrator) solo se aplicaba al renderizar el desplegable de administración — un control únicamente de UI.

Encadenado: suscribirse para obtener un nonce → reutilizarlo contra popup::save para establecer sub_wp_create_user_role=administrator → activar el flujo de suscripción → cuenta de administrador persistente.

Recorrido completo con referencias a archivo/línea: docs/ANALYSIS.md.


Detección — el comprobador pasivo

poc/cve_2026_18322_check.py determina si un sitio ejecuta una versión afectada. Nunca intenta la explotación: emite GETs HTTP simples y lee la versión que la instalación publica sobre sí misma.

Instalación

root@kitploit:~
git clone <this-repo> && cd CVE-2026-18322
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt

requests es recomendado pero opcional — la herramienta recurre a la biblioteca estándar.

Uso

root@kitploit:~
# Single target
python3 poc/cve_2026_18322_check.py https://example.com

# With supporting evidence
python3 poc/cve_2026_18322_check.py https://example.com -v

# Many targets, concurrently, exporting results
python3 poc/cve_2026_18322_check.py -f targets.txt -t 20 --json results.json
python3 poc/cve_2026_18322_check.py -f targets.txt --csv results.csv --only-vulnerable

# Through a proxy, ignoring TLS errors (lab use)
python3 poc/cve_2026_18322_check.py https://staging.internal -k --proxy http://127.0.0.1:8080

Los objetivos pueden indicarse como argumentos o mediante -f (- lee de stdin). Un nombre de host sin esquema se asume como https://.

Ejemplo

root@kitploit:~
$ python3 poc/cve_2026_18322_check.py https://example.com -v
[VULNERABLE] https://example.com/  (Smart Popup by Supsystic 1.11.2)  < 1.13.0 affected by CVE-2026-18322; upgrade to 1.13.0+
      - asset-version: plugin asset enqueued with ?ver=1.11.2  (https://example.com/)
      - homepage-reference: references /plugins/popup-by-supsystic/  (https://example.com/)
      - readme: plugin readme.txt is publicly readable  (https://example.com/wp-content/plugins/popup-by-supsystic/readme.txt)
      - readme-stable-tag: Stable tag: 1.11.2  (.../readme.txt)

Estados

Códigos de salida: 0 = sin objetivos vulnerables, 1 = al menos uno vulnerable, 2 = error de uso.

Cómo detecta

Dos señales pasivas, ambas GETs HTTP simples de recursos públicos:

  1. Cache-busters de assets — el plugin encola su CSS/JS con ?ver=PPS_VERSION (classes/frame.php:410,473), por lo que la página de inicio a menudo filtra la versión exacta.
  2. readme.txt — Stable tag: es autoritativo y tiene precedencia sobre un asset en caché potencialmente obsoleto; el changelog sirve como respaldo.

El escaneo de la página de inicio también descubre directorios wp-content renombrados (p. ej. /app/ de Bedrock) para que la búsqueda del readme siga la estructura real del sitio.


Seguridad

La herramienta de detección no intenta la explotación. Aquí no se publica ningún exploit armamentizado para este CVE: el único código que ejecuta la cadena completa está estrictamente restringido al laboratorio local (véase más abajo).

El comprobador pasivo solo realiza fingerprinting de versiones. Su HttpClient no expone ningún verbo HTTP distinto de GET — impuesto por construcción y verificado en la suite de tests, por lo que no puede emitir una petición que cambie el estado ni siquiera por error. Nunca llama a popup::save, envía un parámetro sub_wp_create_user_role, envía un formulario de suscripción, dispara un correo de confirmación, ni crea o modifica ningún usuario, popup o ajuste. Lee dos recursos públicos — la página de inicio y readme.txt — y nada más.

Esa seguridad tiene un coste, dicho claramente: el veredicto es tan bueno como los metadatos de versión que publique el host. Compromisos y modos de fallo: §9.

El exploit del laboratorio se ejecuta solo en el laboratorio

lab/exploit_full_chain.py es la única pieza de código aquí que realiza el ataque real, y crea un administrador de WordPress. Se publica para que el análisis pueda reproducirse en lugar de darse por sentado — una afirmación sobre un fallo de autorización que no puede demostrarse es una aserción, no un hallazgo. Está escrito para el stack desechable en lab/ y para nada más:

  • Los objetivos no locales se rechazan. El nombre de host del objetivo se resuelve y todas las direcciones que devuelve deben ser loopback o privadas RFC1918. Un nombre DNS público se rechaza incluso si actualmente resuelve a una dirección privada — esa es una forma bien conocida de apuntar una herramienta de "laboratorio" a producción.
  • --lab-confirm es obligatorio. Ambas comprobaciones se ejecutan en assert_lab_target() antes de la primera petición HTTP, por lo que una invocación rechazada no toca el objetivo en absoluto.
  • Solo puede leer el buzón del laboratorio. La fase 1 recolecta el pps_nonce reutilizable del correo de confirmación de suscripción a través de la API de Mailpit del laboratorio (http://localhost:8025/api/v1/…). No hay ninguna ruta de código para recuperar ese correo desde ningún otro lugar, por lo que la cadena no tiene primer paso contra un host fuera del laboratorio.
  • El laboratorio al que apunta es inalcanzable desde fuera de la máquina. Cada servicio en lab/docker-compose.yml se enlaza a 127.0.0.1.

Tal como se distribuye, por lo tanto, no puede apuntarse a un sitio real — se detiene en REFUSING TO RUN antes de emitir una petición:

root@kitploit:~
$ python3 exploit_full_chain.py https://example.com --lab-confirm
REFUSING TO RUN: example.com resolves to non-local address(es) 104.20.23.154, ...
This script creates a WordPress administrator and is for the local lab only.
To test a real site, use the non-destructive checker in ../poc/.

Estas protecciones son estructurales y no decorativas: el script es la cadena real, y lo que lo mantiene como demostración es que no se ejecutará fuera de un stack local desechable. Por favor, no las elimines, y véase SECURITY.md — los cambios que las debiliten, o un exploit independiente destinado a ejecutarse fuera del laboratorio, quedan fuera del alcance de este repositorio. Para averiguar si un sitio real está afectado, usa poc/ — el comprobador responde a la misma pregunta sin tocar nada.

Para una respuesta autoritativa en un host que controlas:

root@kitploit:~
grep -n "array_merge(\$permissions, \$permissionsBase)" \
     wp-content/plugins/popup-by-supsystic/classes/frame.php

Cualquier salida significa que el host es vulnerable — esa línea es precisamente lo que 1.13.0 reemplazó.

Úsalo solo contra sistemas que poseas o para los que tengas autorización explícita de probar. Véase SECURITY.md.


Reproducirlo — el laboratorio de demostración

lab/ contiene un stack Docker desechable que ejecuta la vulnerabilidad de extremo a extremo, para que el análisis pueda verificarse en lugar de darse por sentado:

root@kitploit:~
cd lab
./setup.sh                                                    # WordPress + plugin 1.12.0
../.venv/bin/python exploit_full_chain.py http://localhost:8080 --lab-confirm
./verify.sh                                                   # confirm from the database
./teardown.sh

Su salida más útil es la comparación de versiones — peticiones idénticas contra tres versiones:

root@kitploit:~
./setup.sh --plugin-version 1.11.2    # exploit succeeds
./setup.sh --plugin-version 1.12.0    # exploit succeeds
./setup.sh --plugin-version 1.13.0    # exploit fails at phase 2

El laboratorio es intencionadamente vulnerable y su script de exploit crea un administrador de WordPress. Todo se enlaza a 127.0.0.1, y el script no se ejecutará contra nada que no sea un laboratorio local — véase el exploit del laboratorio se ejecuta solo en el laboratorio. No es un escáner; para comprobar un sitio real, usa poc/.


Remediación

  1. Actualiza a 1.13.0 o posterior.
  2. Si no puedes actualizar, deshabilita el plugin. Las mitigaciones parciales son débiles — la acción vulnerable es alcanzable por cualquier petición anónima.
  3. Si ejecutaste una versión afectada con popups de suscripción habilitados, audita en busca de compromiso: cuentas de administrador inesperadas, popups con sub_wp_create_user_role establecido a un rol privilegiado, y peticiones POST a admin-ajax.php que lleven pl=pps&mod=popup&action=save.

Consultas y greps de logs: §10.


Estructura del repositorio

root@kitploit:~
.
├── README.md
├── SECURITY.md                  Scope, authorized-use policy, disclosure
├── LICENSE                      MIT
├── requirements.txt
├── docs/
│   └── ANALYSIS.md              Full root-cause analysis and fix rationale
├── patches/
│   ├── 01-frame-permission-merge.diff              array_merge() → _mergePermissions()
│   ├── 02-subscribe-role-allowlist-and-nonce.diff  Role allowlist + dedicated nonce
│   └── 03-v1.12.0-confirmation-page-hardening.diff Unrelated hardening added in 1.12.0
├── poc/
│   └── cve_2026_18322_check.py  Passive version fingerprint (GET only)
├── lab/                         Disposable Docker lab — INTENTIONALLY VULNERABLE
│   ├── docker-compose.yml       MariaDB + WordPress 1.12.0 + Mailpit (loopback only)
│   ├── setup.sh / teardown.sh   Bring the lab up / destroy it
│   ├── exploit_full_chain.py    Full attack chain — CREATES AN ADMIN; lab-gated
│   └── verify.sh                Independent confirmation from the database
├── scripts/
│   └── fetch_versions.sh        Export plugin versions from SVN and regenerate diffs
└── tests/
    └── test_check.py            Offline unit tests for the passive checker

Desarrollo

root@kitploit:~
pip install -r requirements.txt pytest
python3 -m pytest tests/ -v

La suite es totalmente offline — usa fixtures grabadas, nunca objetivos en vivo. Cubre la clasificación de versiones en torno al límite 1.13.0, la normalización de objetivos y las reglas de evidencia que deciden cada estado.

Las propiedades de seguridad se verifican, no solo se documentan: que un escaneo completo no emite peticiones que cambien el estado, y que HttpClient no expone ningún verbo HTTP distinto de GET.

Dos regresiones encontradas durante el desarrollo quedan fijadas: un readme.txt con CRLF que derrotaba el parseo anclado a líneas, y una página de error HTTP contada erróneamente como evidencia del plugin.

Para reproducir el análisis desde las fuentes upstream:

root@kitploit:~
./scripts/fetch_versions.sh          # exports 1.11.2 / 1.12.0 / 1.13.0 and regenerates patches/

Licencia

MIT — véase LICENSE. El plugin analizado es GPLv2-or-later y no se redistribuye aquí; scripts/fetch_versions.sh lo obtiene del repositorio SVN oficial de WordPress.

Descargar herramienta
EstadoSignificado
VULNERABLEPlugin detectado en < 1.13.0
NOT_VULNERABLEPlugin detectado en >= 1.13.0
PLUGIN_DETECTED_VERSION_UNKNOWNPlugin presente, versión no determinable — verificar manualmente
PLUGIN_NOT_DETECTEDSin evidencia del plugin (no es prueba de ausencia)
ERRORObjetivo inalcanzable o malformado