Sobre OFFCEPT
Construida por operadores que dedicaron años a entrar en redes antes de crear una empresa alrededor de ello.
Saber Más
← Volver
Investigación Técnica18 de julio de 20265 min de lectura

wp2shell (CVE-2026-63030 & CVE-2026-60137): Inyección SQL No Autenticada en WordPress Core

Escrito por 0xM4L · Investigación de Seguridad OFFCEPT

TL;DR

wp2shell encadena un desync de validación del batch endpoint de la REST API con un parámetro de WP_Query sin sanitizar, en una inyección SQL anónima y sin autenticación contra WordPress Core. Filtra el nombre de usuario admin y el hash de su contraseña. Llegar a una shell completa desde ahí requiere algunos pasos extra, algo que ninguno de los dos CVE concede directamente. Actualiza a 6.9.5 o 7.0.2 ahora.

wp2shell fue encontrado por Adam Kues en Assetnote, parte de Searchlight Cyber, y reportado a través del programa HackerOne de WordPress. Lleva dos CVEs. CVE-2026-63030 es un desync de validación en el batch endpoint de la REST API de WordPress. CVE-2026-60137 es una inyección SQL en el parámetro author__not_in de WP_Query. Encadenados, un atacante anónimo puede correr blind SQL injection contra una instalación WordPress por defecto y extraer datos directamente de la tabla users, sin login, sin plugin y sin configuración especial. El investigador de seguridad Andy Gill publicó un trazado completo a nivel de código del patch diff en zsec.uk, que es la base del desglose técnico de abajo.

Qué hacen realmente los dos CVEs

  • CVE-2026-63030, desync de validación del batch. El batch endpoint de la REST API (/wp-json/batch/v1) construye arrays paralelos para trackear cada subrequest, su ruta emparejada y su resultado de validación. Un path de subrequest malformado falla silenciosamente y solo se añade a uno de esos arrays, no al otro. Cada subrequest posterior queda emparejado con el match y el estado de validación de la ruta equivocada.
  • CVE-2026-60137, author__not_in sin sanitizar. Usando ese desync, una petición dirigida al endpoint público de posts puede despacharse por una ruta cuyo schema de parámetros nunca se ejecutó, así que author_exclude llega a WP_Query como string en bruto en vez de como array de enteros. La rama del array pasa todo por absint(). La rama escalar se la salta por completo y se concatena directamente en la cláusula SQL NOT IN (...).

El resultado es blind SQL injection, no un volcado de datos visible. No hay output de errores ni resultado reflejado de la query, solo un side channel de timing: envuelve la condición inyectada en SLEEP(), y una respuesta lenta significa que la condición era verdadera. Eso basta para extraer datos carácter a carácter, incluido username:password_hash de la tabla users, pero son unos cientos de peticiones por hallazgo, no un solo clic.

El registro del CVE llama a esto remote code execution, y una ruta hacia una shell sí existe: crackear la contraseña recuperada offline, iniciar sesión con ella de forma normal, y luego usar la propia función de subida de plugins de esa cuenta para dejar caer y solicitar directamente un fichero PHP. Pero esa segunda mitad depende de sus propias precondiciones (una contraseña crackeable, sin MFA, permisos de install_plugins, file mods y system() ambos activados) que no tienen nada que ver con ninguno de los dos CVE. Merece la pena conocerla, pero los CVEs en sí son por lo que parcheas, así que en eso se centra este artículo.

Versiones afectadas y corregidas

EstadoVersionesNotas
No afectadas6.8.5 y anterioresConfirmado vía patch diff a nivel de código
Vulnerables6.9.0 – 6.9.4Actualizar a 6.9.5
Vulnerables7.0.0 – 7.0.1Actualizar a 7.0.2
Corregidas6.9.5, 7.0.2, 7.1 Beta 2Alinea los arrays del batch y normaliza author__not_in en el límite SQL

$ Qué hacer ahora

Ya existen PoCs públicos, y la mayoría recorren la cadena completa de punta a punta: blind SQLi, recuperación de credenciales, login, subida de plugin. No esperes a reportes confirmados in-the-wild. Si está sin parchear y expuesto a internet, asume que ya está siendo escaneado.

  • Parchea todas las instancias de WordPress que gestionas. 6.9.x → 6.9.5, 7.0.x → 7.0.2. Los hosts gestionados suelen hacerlo automáticamente, pero verifícalo, y no olvides los micrositios y entornos de staging de los que te habías olvidado.
  • ¿No puedes parchear de inmediato? Bloquea el acceso anónimo a /wp-json/batch/v1 y ?rest_route=/batch/v1 en el WAF como puente de 24 a 48 horas. Puede romper integraciones legítimas (editor de bloques, apps móviles), así que elimínalo en cuanto la versión parcheada esté desplegada.
  • Caza los IOCs específicos. Ráfagas de peticiones POST /wp-json/batch/v1 casi idénticas con paths malformados http://: y un valor de author_exclude que contenga SLEEP, CHAR_LENGTH o SUBSTRING. Un directorio de plugin que coincida con wp-content/plugins/wp2shell-*. Cuerpos de respuesta que contengan WP2SHELL_OUT_START. Cualquiera de estas señales significa que la cadena llegó más lejos que la filtración de credenciales.
  • Si tu contraseña de admin fue alguna vez débil o reutilizada, rótala ahora independientemente de si encuentras IOCs, y activa MFA si aún no lo has hecho. Ese único control rompe la cadena incluso después de que el SQLi ya haya filtrado el hash.
  • Considera DISALLOW_FILE_MODS en sitios que no necesitan instalar plugins desde la UI de administración. Elimina una de las precondiciones de las que depende esta cadena, para este bug y para el próximo que llegue a una sesión de admin por otro camino.

Si quieres que comprobemos si tu parque de WordPress fue alcanzado antes de que parchearas, habla con nosotros.