web-dev-qa-db-fra.com

Comment les encodages de caractères sont-ils utilisés pour contourner les désinfectants XSS?

J'ai lu dans différents blogs que la fonction PHP htmlspecialchars() a certains problèmes lorsque l'on ne donne pas le jeu de caractères attendu comme paramètre facultatif.

Quelqu'un peut-il expliquer des choses de base sur les exploits XSS qui résultent d'une mauvaise utilisation des fonctions de nettoyage avec des exemples liés au codage de caractères?

Cela affecte-t-il également les navigateurs modernes?

9
XII

Le problème

L'utilisation abusive des encodages de caractères est une astuce populaire pour faire fonctionner XSS même lorsqu'il y a des filtres en place. Il existe un certain nombre de situations différentes lorsque cela fonctionne, mais elles partagent toutes des conditions préalables communes:

  • L'attaquant envoie une charge utile en encodage de caractères A.
  • Le serveur effectuant le filtrage ou l'assainissement fonctionne en encodage de caractères B.
  • Le navigateur des victimes interprète la page comme s'il s'agissait d'un codage de caractères A.

Regardons deux exemples de comment cela peut arriver.

Exemple # 1: aucun paramètre de codage dans htmlspecialchars

C'est une vue assez courante en PHP:

echo htmlspecialchars($_GET["query"], ENT_COMPAT | ENT_HTML401);

Le problème ici est le comportement par défaut PHP revient quand aucun encodage n'est spécifié. De le manuel :

S'il est omis, la valeur par défaut de l'encodage varie en fonction de la version PHP utilisée. Dans PHP 5.6 et versions ultérieures, l'option de configuration default_charset est utilisée comme valeur par défaut. PHP 5.4 et 5.5 utilisera UTF-8 par défaut. Les versions antérieures de PHP utilisent ISO-8859-1.

Donc ce que l'encodage PHP utilise dépend de votre version et de votre configuration. Génial. Alors maintenant tout ce qui se trouve entre vous et l'abîme est quelqu'un qui fait un changement innocent dans php.ini, Ou peut-être juste quelque chose d'aussi simple qu'une mise à niveau ou une réinstallation de serveur. Moi aussi j'aime vivre dangereusement ... mais pas si dangereusement.

Notez que cet exemple n'a rien à voir avec le navigateur. Moderne ou ancien, peu importe, car c'est le serveur et non le navigateur qui pose problème ici.

La solution est bien sûr de spécifier le codage correct et de vous assurer qu'il est spécifié dans l'en-tête HTTP Content-Type De la réponse:

echo htmlspecialchars($_GET["query"], ENT_COMPAT | ENT_HTML401, "UTF-8");

Exemple # 2: Heuristique du navigateur vous mordant

C'est un problème si votre serveur ne spécifie pas l'encodage qu'il utilise dans la réponse (ou s'il ne le fait que dans une balise META trop lointaine pour que le navigateur s'en soucie). Si vous ne dites pas au navigateur quel encodage utiliser, il devra deviner. Malheureusement, tous les navigateurs ne sont pas si bons que ça :

Si certaines chaînes d'entrée utilisateur - disons, +ADw-script+AD4-alert(1)+ADw-/script+AD4- - sont renvoyées assez tôt dans la page HTML, Internet Explorer peut deviner à tort que la page est codée en UTF-7. Du coup, l'entrée utilisateur par ailleurs inoffensive devient HTML actif et s'exécute.

La charge utile dans le devis est <script>alert(1)</script> encodée en UTF-7. Un désinfectant fonctionnant en UTF-8 ne verrait rien de dangereux dans cette charge utile et le laisserait passer, mais le navigateur qui est amené à fonctionner en UTF-7 le ferait toujours fonctionner.

Ma compréhension est que c'est principalement les anciennes versions d'IE où c'est un problème. Mais je ne suis pas sûr, donc je serais heureux de voir une autre réponse où elle est clarifiée.

EDIT: Voir réponse de Xavier59 pour une situation où cela fonctionne navigateurs modernes.

La solution

Ce que vous devez faire sur le serveur est simple en théorie. Vous devez vous assurer que ce qui suit est toujours vrai:

  • Le codage des caractères de la réponse est correctement défini dans les en-têtes HTTP.
  • Le filtre XSS fonctionne dans le même codage que celui spécifié ci-dessus.

En pratique, il est étonnamment facile de se tromper.

12
Anders

Cela vient en complément de la réponse d'Anders (ce qui est génial).

Je crois comprendre que ce sont principalement d'anciennes versions de IE où cela pose problème. Mais je n'ai pas de source pour cela, et je ne suis pas sûr, donc je serais heureux d'en voir une autre répondre là où il est clarifié.

Oui, cela affecte les navigateurs modernes.


Prenons la désinfection suivante:

<?php
    header('Content-Type: text/html;charset=utf-8');
    echo preg_replace('/<\w+/', '', $_GET['name']).", can you p0wn it ?"
?>

Cela peut ne pas sembler vulnérable car:

  • < suivi d'une ou plusieurs lettres est supprimé afin qu'un attaquant ne puisse pas ouvrir une nouvelle balise.
  • Content-Type l'en-tête est correctement défini sur utf-8

Maintenant, imaginez que nous envoyons %00%3C%00, l'analyseur d'expressions rationnelles échouera car < (%3C) n'est pas suivi d'une lettre (telle que définie par \w) mais %00 (l'octet nul). Dans UTF-8, l'entrée réfléchie n'exécutera rien, mais si nous pouvons trouver un moyen de la refléter dans UTF-16 ...

Voici ce que nous pouvons lire de W :

Si vous avez une marque d'ordre d'octets (BOM) UTF-8 au début de votre fichier, les versions de navigateur récentes autres qu'Internet Explorer 10 ou 11 l'utiliseront pour déterminer que le codage de votre page est UTF-8. Il a une priorité plus élevée que toute autre déclaration, y compris l'en-tête HTTP.

Vous pouvez ignorer la déclaration de méta-codage si vous avez une nomenclature, mais nous vous recommandons de la conserver, car elle aide les personnes qui consultent le code source à vérifier quel est le codage de la page.

Le caractère BOM dans UTF-16 est le caractère unicode U+FEFF (les différents encodages BOM sont mieux décrits sur Wikipedia ). Donc, parce que notre entrée est reflétée au début du dom, nous pouvons changer le jeu de caractères en UTF-16 et faire exécuter notre code.

Charge utile complète:

%FE%FF%00%3C%00s%00c%00r%00i%00p%00t%00%3E%00a%00l%00e%00r%00t%00(%00%22%00P%000%00w%00n%00e%00d%00%22%00)%00;%00%3C%00/%00s%00c%00r%00i%00p%00t%00%3E

Voici un POC que j'ai fait. La plupart des auditeurs xss ne tomberont pas pour lui, mais Firefox le fera puisque son auditeur est désactivé par défaut. (testé sur Firefox Nightly 60.0a1 - dernière version à ce jour)

Cependant, htmlspecialchars et htmlentities ne tomberont pas pour cela. Néanmoins, cela montre qu'il y a toujours des cas Edge difficiles au coin de la rue!


D'autres attaques sur l'encodage incluent mappage de caractères qui sont également toujours d'actualité.

6
Xavier59

De OWASP XSS page:

"Les attaques de script intersite sont un type de problème d'injection, dans lequel des scripts malveillants sont injectés dans les sites Web autrement bénins et de confiance. Les attaques de script intersite (XSS) se produisent lorsqu'un attaquant utilise une application Web pour envoyer du code malveillant, généralement sous la forme d'un script côté navigateur, pour un utilisateur final différent. Les failles qui permettent à ces attaques de réussir sont assez répandues et se produisent partout où une application Web utilise les entrées d'un utilisateur dans les sorties qu'elle génère sans les valider ni les encoder.

Un attaquant peut utiliser XSS pour envoyer un script illicite à un utilisateur sans méfiance. Le navigateur de l'utilisateur final n'a aucun moyen de savoir que le script ne doit pas être approuvé et exécutera le script. Parce qu'il pense que le script provient d'une source fiable, le script malveillant peut accéder à tous les cookies, jetons de session ou autres informations sensibles conservés par votre navigateur et utilisés avec ce site. Ces scripts peuvent même réécrire le contenu de la page HTML. "

Ceci est un exemple de mauvaises pratiques de codage où vous ne nettoyez pas l'entrée de l'utilisateur.

Imaginons que vous soyez développeur Web et que vous créez ce fichier dans votre site Web (name.php):

<form action="" method="GET">
  What is your name: <input type="text" name="username"><br>
  <input type="submit" value="Submit">
</form>

<?php
  print("Entered name is: ".$_GET["username"]);
?>

Lorsque vous ouvrez cette page sur votre navigateur, vous verrez quelque chose comme ceci:

welcome page, asking for username to print it later

Mettons un nom et voyons le comportement de ce fichier simple, comme nous utilisons la méthode GET, nous pourrons voir les données envoyées sur l'URL:

welcome page with sample name, not malicious input

Mais que se passe-t-il si quelqu'un essaie d'injecter du code HTML dans cette boîte input, quelque chose comme

<Marquee><h1>Andrew ng</h1></Marquee>

Voir les résultats dans l'image ci-dessous:

Code Injection in not sanitized field

L'entrée de l'utilisateur a été rendue comme si elle faisait partie du code source d'origine du fichier.

Maintenant, si nous essayons la même chose avec du code Javascript, voyons ce qui se passe, le code d'injection à tester sur les navigateurs sera 2 façons de XSS:

<h1>Andrew</h1><script>alert("XSS");</script>

<META HTTP-EQUIV="refresh" CONTENT="0;url=data:text/html;base64,PHNjcmlwdD5hbGVydCgndGVzdDMnKTwvc2NyaXB0Pg">

Dans les deux cas, Google Chrome a bloqué l'exécution de ce script: 1st XSS Attempt in Chrome

2nd XSS Attempt in Chrome

Mais, dans Mozilla Firefox, les deux scripts s'exécutent avec succès:

1st XSS Attempt in Firefox

2nd XSS Attempt in Firefox

J'espère que cela peut vous donner une meilleure compréhension de XSS et de la situation actuelle avec les navigateurs modernes, cela a été testé sur:

  • Google Chrome 64.0.3282.119 (version officielle) (64 bits)
  • Mozilla Firefox Quantum 58.0 (64 bits)

À propos de la fonction htmlspecialchars() vous pouvez trouver plus d'informations ici .

Un autre exemple de XSS qui pourrait vous intéresser est celui-ci dans mon blog.

J'espère que cela aide.

1
galoget