Secureaks

Corriger les XSS sur du texte formaté avec iframe sandbox

Article illustration

Qu'est ce qu'une XSS

Le cross-site scripting (également connu sous le nom de XSS) est une faille de sécurité web qui permet à un pirate de faire s’exécuter du code malveillant dans le navigateur de sa victime. Cela est habituellement dû à la manière dont l’application Web réutilise les données manipulées par un utilisateur de manière non sécurisée.

Une XSS peut être reflétée (la cible doit cliquer sur un lien contenant le code malveillant qui s’exécutera sur son navigateur) ou stockée (le pirate place le code malveillant sur l’application web, par exemple dans un champ commentaire), et le code s’exécutera à chaque affichage de la page.

Voici un exemple de code PHP vulnérable :

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Reflected XSS Example</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/water.css@2/out/light.css">
    <style>
        .disclaimer {
            color: red;
        }
    </style>
</head>
<body>

    <h1>Reflected XSS Example</h1>

    <p class="disclaimer">
        THIS IS AN EXAMPLE OF A PAGE VULNERABLE TO REFLECTED XSS.
        THE TEXT IS NOT SANITIZED AND CAN BE USED TO INJECT MALICIOUS CODE.
        DO NOT USE THIS CODE IN REAL PROJECTS.
    </p>

    <p>Use the GET parameter text (example: http://localhost/reflected_xss.php?text=test) to insert text in the page. The text will be displayed below.</p>

    <div>
        <?= $_GET['text']?? "" ?>
    </div>
</body>
</html>

Le lien suivant peut par exemple afficher une popup grâce à du code JavaScript, prouvant que la vulnérabilité est présente :

  • http://localhost/reflected_xss.php?text=%3Cimg%20src=1%20onerror=alert(1)%3E

/uploads/8209ace7c24ebdd53df6e76187f76efc18afcf74.png

Bien entendu, dans des conditions réelles, un attaquant tentera plutôt par exemple de voler des cookies ou des tokens de session ou d’afficher un faux formulaire de connexion pour voler des identifiants.

Afin de corriger cette vulnérabilité, la solution la plus courante et la plus efficace est d’encoder de manière sécurisée les données avant qu’elles ne soient envoyées au navigateur de la victime. Par exemple sous PHP, le code vulnérable ci-dessus peut être corrigé de la manière suivante :

<?= htmlentities($_GET['text']?? "", ENT_QUOTES) ?>

Le problème du texte formaté

Le problème se complique lorsqu’on souhaite que les utilisateurs soient en mesure de formater du texte, en ajoutant des balises HTML, par exemple avec un éditeur visuel comme CKEDITOR.

La première idée est souvent de créer des whitelists ou des blacklists de balises autorisées ou interdites, mais cela nécessite de se baser sur des REGEX ou des filtres qui ne vont pas toujours inclure tous les cas de figure, les pirates étant souvent très imaginatifs pour ce qui est de contourner ce genre de filtres.

Voici un exemple de code contenant un champ de texte éditable qui est repris dans la page sans être correctement sécurisé. Cet exemple reprend directement les données dans la page (c’est donc une XSS reflétée) mais le principe sera le même si les données sont stockées et réutilisées plus tard.

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Insecure formatted textarea</title>
    <script src="https://cdn.ckeditor.com/4.20.2/standard/ckeditor.js"></script>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/water.css@2/out/light.css">
    <style>
        .disclaimer {
            color: red;
        }
    </style>
</head>
<body>

    <h1>Exemple of insecure formatted text</h1>

    <p class="disclaimer">
        THIS IS AN EXAMPLE OF A TEXTAREA WITH INSECURE FORMATTING TEXT LEADING TO XSS.
        THE TEXT IS NOT SANITIZED AND CAN BE USED TO INJECT MALICIOUS CODE.
        DO NOT USE THIS CODE IN REAL PROJECTS.
    </p>

    <p>Here is a text area with formatting text. You can write some text and submit it. The text will be displayed below the form.</p>

    <form action="textarea_insecure.php" method="post">
        <label for="text">Insecure formatted text</label>
        <textarea id="text" name="text" rows="10" cols="50"></textarea>
        <p>
            <input type="submit" value="Submit">
        </p>
    </form>
    <script>
        CKEDITOR.replace( 'text' );
    </script>

    <div>
        <?= $_POST['text']?? "" ?>
    </div>
</body>
</html>

/uploads/4d11ef2c1b88d626bf691b9c7c7b8d2922396e95.png

Sur cette application, il est possible d’intercepter le trafic pour manipuler les données et ajouter du code malveillant. Avec CKEDITOR, il est également possible de simplement cliquer sur le bouton « source » pour éditer directement le HTML. En saisissant simplement du code JavaScript, celui-ci sera interprété lorsqu’il sera réutilisé.

/uploads/633db57c395489e5d1953b61228ecd2ea3760691.png

L’iframe sandbox

Pour corriger ce problème, il n’est pas possible de simplement encoder les données, car cela supprimerait toute possibilité de formatage de texte. C’est là que le paramètre « sandbox » des balises HTML « iframe » va nous faciliter la vie.

En effet, ce paramètre est conçu pour nettoyer automatiquement ce qui se trouve dans l’iframe de tout ce qui pourrait être dangereux, tout en gardant les balises HTML inoffensives.

Voici comment il est possible d’utiliser cette iframe en reprenant l’exemple ci-dessus :

<iframe src="data:text/html;base64,<?= base64_encode($_POST['text']?? "") ?>" sandbox=""></iframe>

Si les données sont réutilisées de manière brute comme ci-dessus, il est important d’utiliser un encodage BASE64 pour s’assurer qu’il ne sera pas possible d’injecter du code dans le paramètre src de la balise. En effet, sans cet encodage, il aurait pu être possible d’ajouter une double quote « " » puis de fermer la balise avec « > » pour ensuite injecter directement ici.

Il est cependant tout à fait possible de passer une simple URL sans le paramètre « src ».

Les données saisies par l’utilisateur sont donc affichées de manière sécurisée tout en gardant la mise en forme.

/uploads/d003f829a926ded9eff630acba37b1256a880e2f.png

/uploads/d9261aaca8a317adc9d5102c0668085eb47aefd3.png

Par défaut, le paramètre sandbox est très strict, mais il peut prendre certains paramètres afin de modifier son comportement (source) :

  • allow-downloads : permet aux téléchargements d'avoir lieu après une action de la part de l'utilisateur.
  • allow-forms : le contexte de navigation intégré peut envoyer des formulaires. Si ce mot-clé n'est pas utilisé, cette opération n'est pas autorisée.
  • allow-modals : le contexte de navigation peut ouvrir des fenêtres modales.
  • allow-orientation-lock : le contexte de navigation peut désactiver le verrouillage de l'orientation de l'écran.
  • allow-pointer-lock : le contexte de navigation peut utliser l'API Pointer Lock.
  • allow-popups : le contexte de navigation peut déclencher des fenêtres contextuelles (par exemple avec window.open, target="_blank", showModalDialog). Si ce mot-clé n'est pas utilisé, la fonctionnalité échouera sans message d'erreur.
  • allow-popups-to-escape-sandbox : ce mot-clé permet à un document isolé dans un bac à sable (sandboxed) d'ouvrir de nouvelles fenêtres sans avoir à forcer la mise en bac à sable pour ces fenêtres. Cela permettra par exemple à une publicité tierce d'être correctement mise dans un bac à sable sans appliquer les mêmes restrictions sur la page initiale.
  • allow-presentation : ce mot-clé permet à un iframe de démarrer une session de présentation (en-US).
  • allow-same-origin : ce mot-clé permet au document isolé de supporter les tests de same-origin policy en désactivant le remplacement de l'origine de l'iframe par une origine unique.
  • allow-scripts : le contexte de navigation peut exécuter des scripts (mais ne peut pas créer de fenêtres contextuelles). Si ce mot-clé n'est pas utilisé, cette opération n'est pas autorisée.
  • allow-storage-access-by-user-activation Expérimental : ce mot-clé permet au contexte de navigation embarqué de demander l'accès aux fonctionnalités de stockage du document parent (ex. ses cookies, le stockage web) grâce à l'API Storage Access (en-US).
  • allow-top-navigation : le contexte de navigation peut charger du contenu depuis le contexte de navigation de plus haut niveau. Si ce mot-clé est absent, cette opération n'est pas autorisée.
  • allow-top-navigation-by-user-activation : le contexte de navigation peut charger du contenu depuis le contexte de plus haut niveau uniquement si l'action provient de l'utilisateur. Si ce mot-clé est absent, cette opération n'est pas autorisée.

Le code source des différentes pages utilisées dans cet article peut être retrouvé ici : https://github.com/Secureaks/xss-fix-iframe-sandbox

Par Romain Garcia le 07/03/2023 dans la catégorie Ethical Hacking