
CalderaForms 1.5.9.1 XSS (plugin WordPress) - tutorial
CalderaForms 1.5.9.1 XSS (WordPress plugin) - tutorial
CalderaForm è un plugin per WordPress che permette di creare facilmente dei form tramite drag and drop. Durante una recente attività mi è capitato di testare alcuni portali, uno dei quali ospitava proprio un form di contatti creato tramite questo plugin. La configurazione personalizzata dell'istanza in oggetto mi ha permesso di trovarne una vulnerabilità: data la sua natura semplice, da manuale, credo che sia un buon pretesto per illustrare alcuni meccanismi a chi è alle prime armi.
Scopo esclusivamente didattico - non utilizzare queste informazioni per testare target senza esplicita autorizzazione né per scopi illeciti - non tuffarsi in acqua fredda durante la digestione - vestirsi a cipolla quando fa caldo
Per l'exploit completo:
https://www.exploit-db.com/exploits/44489/
Per la CVE:
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-7747
Nella configurazione oggetto di analisi, il form era impostato per rispondere con un messaggio di ringraziamento rivolto all'utente, chiamandolo con il nome appena inserito.
Per replicare l'ambiente di test, installare in locale un'istanza di WordPress e installare il plugin CalderaForms versione 1.5.9.1 (disponibile qui o qui).
Una volta installato, dalla console amministrativa di WordPress > colonna di sinistra > "Caldera Forms" > top buttons > "New Form" > selezioniare Contact Form, rinominare e "Create Form"

Una volta creato è possibile modificarne la configurazione: top buttons > "Form Settings" > modificare il Success Message in modo che includa uno dei dati inseriti dall'utente. Click sul box e appare una tendina di suggerimenti. Aggiungere
%first_name%
Top buttons > "Save Form"
Per inserire il form in una pagina: colonna di sinistra > "Pages" > "Sample Page" > "Edit" > "Caldera Form" > selezionare il form appena creato > "Insert Form" > colonna di destra > "Update"

Done.
Andiamo ad affrontare uno ad uno gli step necessari per costruire questo tipo di attacco, secondo il paradigma divide et impera.
In fase di test, quando si interagisce con un componente bisogna sempre prestare attenzione alle sue reazioni in base agli stimoli dati; in particolare, ci concentriamo sul "percorso" dei dati da noi inseriti e le eventuali trasformazioni che essi subiscono.
Un esempio specifico per il nostro caso è il seguente:
Visitiamo la pagina contenente il form: http://127.0.0.1/wordpress/sample-page/
Popoliamo il form con i seguenti dati
"First Name": myName
"Last Name": myLast
"Email Address": my@e.mail
"Comments": myComm
I dati vengono elaborati secondo la logica del plugin
Il messaggio di ringraziamento ricevuto contiene la stringa da noi inserita nel campo First Name
"Thank you myName, form has been successfully submitted."

La stringa da noi inserita nel campo "First Name" ci viene restituita nel messaggio di ringraziamento.
In particolare, la stringa è contenuta in un div tag HTML.
Il nostro input finisce nell'HTML della pagina.
Considerazione: "Ci piace. Abbiamo un punto di contatto."
Facciamo un passo in avanti. Come viene trattato il nostro input durante la fase che abbiamo chiamato di "elaborazione" (punto 2)? In particolare, quello che vogliamo sapere è: abbiamo delle limitazioni sui caratteri (e le loro combinazioni) che possiamo usare? Ovviamente l'obiettivo è quello di riuscire a iniettare "roba". Quando si cerca di effettuare una injection bisogna tenere a mente dove finisce il nostro input e usare "la lingua" adeguata.
Il nostro input viene elaborato da un interprete SQL? Dobbiamo parlare la sua lingua
Il nostro input viene elaborato da uno script PHP? Dobbiamo parlare la sua lingua
Il nostro input finisce su una pagina HTML? ...
Quindi quello che ci interessa è capire se possiamo usare i caratteri e i costrutti tipici dell'HTML e in particolare, data la capacità di questo linguaggio di contenere/interpretare codice JavaScript, capire se troviamo una strategia per inserire il nostro codice nella "zona di atterraggio", ovvero il tag div notato prima.
Per fare questo inseriamo nel campo "First Name" un semplice tag HTML e vediamo se viene "sanitizzato" (da sanitized), ovvero se viene modificato in modo tale da essere reso innocuo/non interpretabile, oppure se ci viene restituito tale e quale. Usiamo a questo scopo un tag <br>, utilizzato per inserire un line break nel testo.
Seguendo la numerazione precedente:
Popoliamo il form con i seguenti dati
"First Name": m<br>yName
"Last Name": myLast
"Email Address": my@e.mail
"Comments": myComm
Il messaggio di ringraziamento contiene il nostro tag HTML, che non è stato modificato, ed esso viene correttamente interpretato, inserendo un line break nel mezzo del messaggio
"Thank you m
yName, form has been successfully submitted."

Considerazione: "Ci piace. Possiamo usare i simboli minore-di e maggiore-di, possiamo inserire tag HTML che non vengono sanitizzati e vengono interpretati."
Passo avanti. Sostituiamo il tag di formattazione con qualcosa di più utile, come un tag <script>, che ci permette di inserire ed eseguire codice JavaScript all'interno della pagina.
Popoliamo il form con i seguenti dati
"First Name": m<script>alert(1);</script>yName
"Last Name": myLast
"Email Address": my@e.mail
"Comments": myComm
Il messaggio di ringraziamento contiene il nostro tag HTML, che non è stato modificato, e che viene correttamente interpretato, mostrandoci un box di alert

Considerazione 1: "Ci piace. Possiamo eseguire codice JavaScript arbitrario nel contesto del browser dell'utente."
Considerazione 2: "Non ci piace. L'utente che esegue il JavaScript siamo noi stessi"
La situazione è questa: possiamo eseguire JavaScript tramite un sito non controllato da noi nel contesto del browser di un utente, ma questo utente, al momento, è lo stesso che inserisce i valori nel form. Tutto ciò è abbastanza inutile.
L'idea è questa: esiste un modo di richiamare il messaggio di ringraziamento contenente il nostro codice da eseguire?
Torniamo al nostro primo submit, quello di "perlustrazione" (o ri-eseguiamo i primi step).
Analizzando il traffico di rete o il sorgente della pagina, capiamo che il form esegue una richiesta POST verso l'indirizzo
http://127.0.0.1/wordpress/cf-api/CF5ad9b3176c0f4
(l'ultima parte può variare, modificarla opportunamente in tutti gli esempi che seguono)
ovvero verso l'indirizzo
http://<target>/cf-api/<form-id>
e che la risposta di questa richiesta è un JSON contenente alcuni dati, tra cui il messaggio di ringraziamento, e avente la seguente struttura:
{
"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"
}
teniamo da parte questa informazione, ci torneremo a breve; soprattutto notiamo i campi "form_id" e "cf_id".
Cosa c'è all'indirizzo verso il quale viene eseguita la POST? Senza troppe supposizioni, vediamo cosa accade se eseguiamo una GET, ovvero visitiamo la pagina all'indirizzo http://127.0.0.1/wordpress/cf-api/CF5ad9b3176c0f4 e troviamo, né più né meno, l'HTML del form in questione.
e controlliamo il traffico di rete che deriva dal submit

Questa volta riceviamo un codice HTTP 302 (redirect) verso la location /wordpress/cf-api/CF5ad9b3176c0f4/?cf_su=1&cf_id=49
che ci restituisce, né più né meno, l'HTML contenente il tag div del messaggio di ringraziamento.
Notiamo il formato di questo indirizzo:
http://<target>/cf-api/<form-id>/?cf_su=1&cf_id=<cf-id>
dove i valori di <form-id> e <cf-id> sono proprio quelli contenuti nel JSON precedentemente analizzato, rispettivamente "form_id" e "cf_id".
Abbiamo risposto alla domanda di partenza? Si. Abbiamo trovato un modo per richiamare il contenuto del messaggio di ringraziamento.
Considerazione : "Ci piace. Possiamo richiamare, secondo necessità, il messaggio contenente i nostri dati."
Tiriamo le fila, congiungendo tutte le informazioni che abbiamo raccolto fino ad ora e confezioniamo un attacco.
1) salviamo il nostro codice malevolo sul target
2) raccogliamo i dati necessari a recuperare tale codice
3) costruiamo la URL adatta a "innescare" (da "to trigger") il nostro attacco
Popoliamo il form (da una delle due pagine, è indifferente) con i seguenti dati
"First Name": m<script>document.body.innerHTML=String.fromCodePoint(128046);</script>yName
"Last Name": myLast
"Email Address": my@e.mail
"Comments": myComm
Tramite l'analisi del traffico generato, che sia leggere il JSON ricevuto nel caso della pagina sample-page o che sia leggere il redirect nel caso della pagina contenente solo il form, recuperiamo gli identificativi form_id e 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

Alcune considerazioni:
Al lettore si propone, se interessato, di:
' (apice, single quote), il carattere " (virgolette, double quotes) o il carattere ` (accento grave, backtick o backquote o grave accent), assente dal layout delle tastiere italiane