Une vulnérabilité sur Grafikart.fr

Posté le - A Propos Par Grafikart

Récemment, une vulnérabilité critique m'a été signalée sur Grafikart. Le problème permettait, dans certaines conditions, de prendre le contrôle d'un compte sur le site en faisant cliquer l'utilisateur sur un lien. Plutôt que de corriger silencieusement, je vous propose de revenir sur cette faille pour comprendre ce qui s'est passé et les protections mises en place.

Signalement de la faille

La vulnérabilité a été remontée par Romain, qui travaille dans la cybersécurité chez Devoteam (au sein de la branche Cybertrust). Dans le cadre du développement d'un nouvel outil interne, il a essayé de l'utiliser sur le code source de Grafikart.fr pour tester la découverte de failles de sécurité.

Cette analyse a été faite avec une base Claude Opus 5, en lui donnant accès au code source du site ainsi qu'à un environnement de test local afin qu'il puisse tester les vecteurs d'attaque. À partir de ces trouvailles, une qualification manuelle a été faite avec des vidéos pour présenter des proofs of concept des vulnérabilités trouvées.

C'est un point important : le code source étant ouvert, il est possible de l'analyser ou de lancer le projet en local pour faire des tests. En revanche, faire du pentesting directement sur le site n'est pas autorisé. Cela peut dégrader les performances, créer des comptes inutiles et rendre le nettoyage plus pénible.

Après analyse, plusieurs problèmes ont été remontés, avec surtout une vulnérabilité critique qui permettait de prendre le contrôle d'un compte à partir d'un simple clic.

La vulnérabilité critique

Cette vulnérabilité est intéressante, car elle est la combinaison de deux erreurs de conception qui, mises bout à bout, permettent la prise de contrôle d'un compte.

Une faille XSS sur le webhook Twitch

Le point de départ est une faille XSS, Cross-Site Scripting, qui permet d'injecter de l'HTML directement sur une page du site.

Dans mon cas, le problème venait de l'implémentation du webhook Twitch qui, pour bien fonctionner, demande de renvoyer en réponse un texte qu'il envoie. De mon côté, le code ressemblait à ça :

public function webhook(Request $request, TwitchAPI $api, LiveService $liveService): Response { $json = $request->json()->all(); if (isset($json['challenge'])) { return response($json['challenge']); } // ... }

L'idée était donc très simple :

Le problème, c'est que la fonction response() adapte les en-têtes de réponse en fonction du contexte. Si un navigateur appelle cette route, Laravel peut renvoyer une réponse interprétée comme du HTML par le navigateur.

En créant un formulaire, on peut déclencher un POST vers le webhook Twitch avec un challenge qui contient de l'HTML. Par exemple, un attaquant peut créer une simple page avec un formulaire :

<form id="f" method="POST" enctype="text/plain" action="https://grafikart.fr/api/twitch/webhook"> <input name='{"challenge":"&lt;script&gt;alert()&lt;/script&gt;","x":"' value='"}' /> </form> <script> document.querySelector("form").submit(); </script>

Si un utilisateur ouvre cette page, le navigateur le redirigera vers la page du webhook et générera une page HTML correspondant au challenge. En injectant du JavaScript, on peut ensuite accéder au localStorage ou aux cookies.

Heureusement, sur Grafikart, les cookies sont en HttpOnly, donc le JavaScript ne peut pas les lire directement, et aucune information sensible n'est stockée dans le localStorage. La faille n'était donc pas exploitable seule pour voler directement une session.

Le formulaire de changement de mot de passe

Le second problème se situe au niveau du formulaire de changement de mot de passe, qui ne demandait pas le mot de passe actuel de l'utilisateur.

Ainsi, en utilisant la faille XSS précédente, on peut générer une requête vers la page de changement de mot de passe pour simuler ce formulaire. La requête étant faite depuis le même domaine, les cookies sont bien transmis au serveur, l'utilisateur est considéré comme authentifié et son mot de passe est bien changé.

L'attaque mise bout à bout

Le scénario d'attaque était alors le suivant :

À partir de là, l'attaquant peut se connecter à ce compte en utilisant le mot de passe qu'il vient de définir.

Cette vulnérabilité n'a pas été exploitée sur mon compte administrateur, car mon mot de passe n'a pas été modifié à mon insu. Et l'utilisation de l'authentification à deux facteurs aurait protégé mon compte. Je n'ai pas non plus reçu de signalement laissant pensé que des comptes aient pu être compromis.

Les protections mises en place

La première correction a consisté à forcer explicitement le type de contenu de la réponse du webhook. Le challenge est maintenant renvoyé en text/plain, afin que le navigateur ne l'interprète plus comme du HTML.

$json = $request->json()->all(); if (isset($json['challenge'])) { return response($json['challenge'], Response::HTTP_OK, ['Content-Type' => 'text/plain']); }

Concrètement, l'objectif est d'éviter qu'une valeur contrôlée par l'utilisateur puisse devenir une page HTML interprétable. Même si la donnée semble venir d'un service de confiance, il faut garder en tête qu'un point d'entrée HTTP peut être appelé si la route est découverte.

La seconde correction a été d'ajouter une vérification du mot de passe actuel lors du changement de mot de passe. C'est une bonne pratique pour toutes les actions sensibles :

Cela permet de limiter les dégâts si une session est ouverte sur un ordinateur partagé, ou si une action est déclenchée depuis une page malveillante.

À propos du CSRF

Le rapport mentionnait aussi l'absence de protection CSRF sur le site. Dans ce cas précis, je ne pense pas qu'un token CSRF aurait empêché l'exploitation.

Une fois qu'un attaquant peut injecter du HTML et du JavaScript sur le domaine, il peut aussi faire une requête pour récupérer un formulaire contenant le token CSRF, puis le réinjecter dans sa propre requête. Le problème principal restait donc la XSS et l'absence de confirmation du mot de passe pour une action sensible.

Le rôle des outils et de l'analyse humaine

Ce signalement montre aussi l'intérêt des outils d'IA pour analyser un code source. Ils peuvent repérer facilement des patterns classiques dans une grande quantité de code :

En revanche, il faut toujours une validation humaine derrière, car ils ont tendance à signaler de faux positifs ou des vulnérabilités qui ne sont pas vraiment exploitables.

Dans mon cas, un signalement concernait le système de révisions du site, qui permet aux utilisateurs de proposer des modifications sur des articles. L'outil signalait un risque d'injection HTML, mais dans les faits les révisions sont validées manuellement et il n'est pas possible que j'accepte une modification contenant de l'HTML arbitraire.

Sécurité & Open source

Aussi, l'accès au code source a facilité la découverte et on peut se dire que rendre le code ouvert n'est plus forcément une bonne idée. Pour le coup, il est vrai que cela révèle plus facilement la surface d'attaque, mais d'un autre côté, ça permet aussi à plus de personnes de tester la sûreté du code et de soumettre des rapports pour améliorer l'application.

Dans mon cas, si le code n'avait pas été public, on n'aurait peut-être pas découvert le problème si rapidement et il aurait pu être exploité sans que je m'en rende compte.

Ce qu'il faut retenir

Ce que je trouve intéressant dans cette vulnérabilité, c'est le fait d'avoir réussi à combiner plusieurs éléments pour créer un scénario d'attaque viable. Mais c'est aussi la preuve que, quel que soit son niveau d'expertise, on peut vite laisser une petite erreur se glisser dans le code. C'est aussi pour cela que les revues de code, les audits et le travail en équipe sont utiles : quelqu'un d'autre aurait peut-être repéré, au moment du commit, que cette réponse devait explicitement être renvoyée en text/plain.

Merci encore à Romain pour le signalement et le travail de formalisation. Les corrections étaient simples, mais l'impact potentiel justifiait clairement de prendre le sujet au sérieux.