
CalderaForms 1.5.9.1 XSS (plugin de WordPress) - tutorial
CalderaForms 1.5.9.1 XSS (plugin de WordPress) - tutorial
CalderaForm es un plugin para WordPress que permite crear formularios fácilmente mediante arrastrar y soltar. Durante una actividad reciente me tocó probar algunos portales, uno de los cuales alojaba precisamente un formulario de contacto creado con este plugin. La configuración personalizada de la instancia en cuestión me permitió encontrar una vulnerabilidad: dada su naturaleza sencilla, de manual, creo que es un buen pretexto para ilustrar algunos mecanismos a quienes están empezando.
Fin exclusivamente didáctico: no utilizar esta información para probar objetivos sin autorización explícita ni con fines ilícitos; no tirarse al agua fría durante la digestión; vestirse por capas cuando hace calor
Para el exploit completo:
https://www.exploit-db.com/exploits/44489/
Para la CVE:
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-7747
En la configuración objeto de análisis, el formulario estaba ajustado para responder con un mensaje de agradecimiento dirigido al usuario, llamándolo por el nombre recién introducido.
Para replicar el entorno de prueba, instalar localmente una instancia de WordPress e instalar el plugin CalderaForms versión 1.5.9.1 (disponible aquí o aquí).
Una vez instalado, desde la consola administrativa de WordPress > columna izquierda > "Caldera Forms" > botones superiores > "New Form" > seleccionar Contact Form, renombrar y "Create Form"

Una vez creado se puede modificar su configuración: botones superiores > "Form Settings" > modificar el Success Message para que incluya uno de los datos introducidos por el usuario. Hacer clic en el cuadro y aparece un desplegable de sugerencias.
Añadir %first_name%

Botones superiores > "Save Form"
Para insertar el formulario en una página: columna izquierda > "Pages" > "Sample Page" > "Edit" > "Caldera Form" > seleccionar el formulario recién creado > "Insert Form" > columna derecha > "Update"

Hecho.
Vamos a abordar uno a uno los pasos necesarios para construir este tipo de ataque, según el paradigma divide y vencerás.
Durante la fase de pruebas, al interactuar con un componente siempre hay que prestar atención a sus reacciones en función de los estímulos dados; en particular, nos centramos en el "recorrido" de los datos que introducimos y en las posibles transformaciones que sufren.
Un ejemplo específico para nuestro caso es el siguiente:
Visitamos la página que contiene el formulario: http://127.0.0.1/wordpress/sample-page/
Rellenamos el formulario con los siguientes datos
"First Name": myName
"Last Name": myLast
"Email Address": my@e.mail
"Comments": myComm
Los datos se procesan según la lógica del plugin
El mensaje de agradecimiento recibido contiene la cadena que introdujimos en el campo First Name
"Thank you myName, form has been successfully submitted."

La cadena que introdujimos en el campo "First Name" nos es devuelta en el mensaje de agradecimiento.
En particular, la cadena está contenida en una etiqueta div HTML.
Nuestra entrada termina en el HTML de la página.
Consideración: "Nos gusta. Tenemos un punto de contacto."
Demos un paso adelante. ¿Cómo se trata nuestra entrada durante la fase que hemos llamado de "procesamiento" (punto 2)? En particular, lo que queremos saber es: ¿tenemos limitaciones sobre los caracteres (y sus combinaciones) que podemos usar? Obviamente el objetivo es conseguir inyectar "cosas". Cuando se intenta hacer una inyección hay que tener en mente dónde termina nuestra entrada y usar "el idioma" adecuado.
¿Nuestra entrada es procesada por un intérprete SQL? Debemos hablar su idioma
¿Nuestra entrada es procesada por un script PHP? Debemos hablar su idioma
¿Nuestra entrada termina en una página HTML? ...
Por tanto, lo que nos interesa es entender si podemos usar los caracteres y construcciones típicos del HTML y, en particular, dada la capacidad de este lenguaje para contener/interpretar código JavaScript, entender si encontramos una estrategia para insertar nuestro código en la "zona de aterrizaje", es decir, la etiqueta div observada antes.
Para ello introducimos en el campo "First Name" una simple etiqueta HTML y vemos si es "sanitizada", es decir, si se modifica de manera que se vuelva inofensiva/no interpretable, o si se nos devuelve tal cual. Usamos para ello una etiqueta <br>, utilizada para insertar un salto de línea en el texto.
Siguiendo la numeración anterior:
Rellenamos el formulario con los siguientes datos
"First Name": m<br>yName
"Last Name": myLast
"Email Address": my@e.mail
"Comments": myComm
El mensaje de agradecimiento contiene nuestra etiqueta HTML, que no ha sido modificada, y es interpretada correctamente, insertando un salto de línea en medio del mensaje
"Thank you m
yName, form has been successfully submitted."

Consideración: "Nos gusta. Podemos usar los símbolos menor-que y mayor-que; podemos insertar etiquetas HTML que no se sanitizan y son interpretadas."
Un paso más. Sustituimos la etiqueta de formato por algo más útil, como una etiqueta <script>, que nos permite insertar y ejecutar código JavaScript dentro de la página.
Rellenamos el formulario con los siguientes datos
"First Name": m<script>alert(1);</script>yName
"Last Name": myLast
"Email Address": my@e.mail
"Comments": myComm
El mensaje de agradecimiento contiene nuestra etiqueta HTML, que no ha sido modificada, y es interpretada correctamente, mostrándonos un cuadro de alert

Consideración 1: "Nos gusta. Podemos ejecutar código JavaScript arbitrario en el contexto del navegador del usuario."
Consideración 2: "No nos gusta. El usuario que ejecuta el JavaScript somos nosotros mismos"
La situación es esta: podemos ejecutar JavaScript a través de un sitio que no controlamos en el contexto del navegador de un usuario, pero ese usuario, por ahora, es el mismo que introduce los valores en el formulario. Todo esto es bastante inútil.
La idea es esta: ¿existe alguna forma de volver a invocar el mensaje de agradecimiento que contiene nuestro código para ejecutarlo?
Volvamos a nuestro primer envío, el de "exploración" (o reejecutemos los primeros pasos).
Analizando el tráfico de red o el código fuente de la página, entendemos que el formulario realiza una petición POST hacia la dirección
http://127.0.0.1/wordpress/cf-api/CF5ad9b3176c0f4
(la última parte puede variar; modifícala adecuadamente en todos los ejemplos siguientes)
es decir, hacia la dirección
http://<target>/cf-api/<form-id>
y que la respuesta a esta petición es un JSON que contiene algunos datos, entre ellos el mensaje de agradecimiento, y que tiene la siguiente estructura:
{
"data":
{"cf_id":"48"},
"html":"<div class=\" alert alert-success\">Thank you myName, form has been successfully submitted.<\/div>",
"type":"complete",
"form_id":"CF5ad9b3176c0f4",
"form_name":"MyContactForm",
"status":"complete"
}
guardamos esta información; volveremos a ella en breve; sobre todo, observamos los campos "form_id" y "cf_id".
¿Qué hay en la dirección hacia la que se realiza la POST? Sin muchas suposiciones, veamos qué ocurre si hacemos una GET, es decir, visitamos la página en la dirección http://127.0.0.1/wordpress/cf-api/CF5ad9b3176c0f4 y encontramos, ni más ni menos, el HTML del formulario en cuestión.
y comprobamos el tráfico de red que se deriva del envío

Esta vez recibimos un código HTTP 302 (redirect) hacia la ubicación /wordpress/cf-api/CF5ad9b3176c0f4/?cf_su=1&cf_id=49
que nos devuelve, ni más ni menos, el HTML que contiene la etiqueta div del mensaje de agradecimiento.
Observemos el formato de esta dirección:
http://<target>/cf-api/<form-id>/?cf_su=1&cf_id=<cf-id>
donde los valores de <form-id> y <cf-id> son precisamente los contenidos en el JSON analizado anteriormente, respectivamente "form_id" y "cf_id".
¿Hemos respondido a la pregunta inicial? Sí. Hemos encontrado una forma de recuperar el contenido del mensaje de agradecimiento.
Consideración : "Nos gusta. Podemos recuperar, según sea necesario, el mensaje que contiene nuestros datos."
Recapitulemos, uniendo toda la información recopilada hasta ahora y preparemos un ataque.
1) guardamos nuestro código malicioso en el objetivo
2) recopilamos los datos necesarios para recuperar ese código
3) construimos la URL adecuada para "disparar" nuestro ataque
Rellenamos el formulario (desde cualquiera de las dos páginas, es indiferente) con los siguientes datos
"First Name": m<script>document.body.innerHTML=String.fromCodePoint(128046);</script>yName
"Last Name": myLast
"Email Address": my@e.mail
"Comments": myComm
Mediante el análisis del tráfico generado, ya sea leyendo el JSON recibido en el caso de la página sample-page o leyendo el redirect en el caso de la página que contiene solo el formulario, recuperamos los identificadores form_id y cf_id
{
"data":
{"cf_id":"69"},
"html":"...",
"type":"...",
"form_id":"CF5ad9b3176c0f4",
"form_name":"...",
"status":"..."
}
http://127.0.0.1/wordpress/cf-api/CF5ad9b3176c0f4/?cf_su=1&cf_id=69

Algunas consideraciones:
Al lector se le propone, si está interesado:
' (apóstrofo, comilla simple), el carácter " (comillas dobles) o el carácter ` (acento grave, backtick o backquote), ausente en la disposición de los teclados italianos