Sécurité Informatique

Titre de l'article : Les cahiers de L'OWASP : L'encodage
Auteur : Adrien Guinault, consultant sécurité Xmco Partners
Courriel : adrien.guinault@xmcopartners.com
Date : Janvier 2007
Version complète en PDF : Actu Sécurité Janvier 2007


>Les cahiers de l'Owasp : L'encodage

L'objectif de ce document est de vous présenter deux formes d'attaques moins connues mais néanmoins très efficaces. La compréhension de ces techniques vous permettra d'implémenter des parades simples pour garantir la robustesse de vos applications lorsque celles-ci seront confrontées à des données encodées, internationalisées et uni-encodées (Unicode).

Définition du problème

Lors d'une communication entre deux systèmes, l'ensemble des caractères reçus par un système doit être analysé et compris unilatéralement. Aucun des navigateurs, des serveurs web, des pare-feux applicatifs ou autres agents HTTP ne traite les spécificités d'encodage de la même manière. La "canonicalization " consiste en la conversion des données d'un format vers un autre.
Peu de personnes prennent conscience de ce problème et cela engendrent des erreurs d'implémentation qui peuvent avoir une incidence sur la sécurité de l'application.
Beaucoup de plates-formes web sont vulnérables car les exploits unicode sont rarement testés.

"Canonical " signifie la forme la plus simple ou la plus standard pour une donnée. Le fait de convertir une entrée vers sa forme la plus standard constitue donc un enjeu majeur. Les applications web sont en permanence confrontées à des problèmes de conversion, par exemple en ce qui concerne les différentes façons d'encoder des URLs ou des adresses IP. Toutes les données reçues sous des formes inattendues doivent être traitées de la même manière.

NB: Se protéger à l'égard des attaques de type "canocalization " ou "Unicode " ne signifie pas que toutes les applications doivent être internationalisées, mais plutôt que toutes les applications doivent savoir gérer les données malformées ou sous forme Unicode.



Les pirates sont-ils plus nombreux? Est-ce que ceux-là intensifient la recherche et l'exploitation de vulnérabilités ? Les réponses sont difficiles à donner. En effet, bien que Windows reste la cible privilégiée des pirates, d'autres systèmes font également les frais de cette tendance plutôt inquiétante.

Le codage

Tout d'abord, voici un petit rappel des normes de codages (ASCII, Unicode, UTF-8) qui sont souvent mélangées :

Ascii

L'ASCII (American Standard Code for information interchange) est une norme qui longtemps a été la plus utilisée en informatique. Elle comprend 128 caractères et est codée en binaire sur un octet. Elle rassemble tous les caractères les plus courants.

Unicode

L'Unicode est une norme informatique qui fut développée par le Consortium Unicode (1991). Elle permet de spécifier un numéro unique pour chaque caractère, quelle que soit la plate-forme, quel que soit le logiciel et quelle que soit la langue. Dotée de 65535 codes elle établit donc une correspondance entre n'importe quel caractère d'écriture (toutes langues et tous signes) et un identifiant numérique.

Ce standard est aujourd'hui utilisé par la plupart des systèmes d'exploitation, dans tous les navigateurs récents et dans les applications client-serveur. Ainsi, cette implémentation permet d'échanger des données provenant de plusieurs langues et de plusieurs pays.


Généralement en Unicode, un caractère est codé sur 2 octets ce qui prend deux fois plus de place qu'en ASCII (1 octet). Etant donné que la majorité des caractères européens et américains utilisent seulement le code ASCII, un autre code a donc été implémenté afin de minimiser l'espace alloué à chaque caractère tout en étant capable d'en gérer une grande variété : l'UTF8.

UTF-8

UTF-8 possède la propriété de préserver l'intégralité du format ASCII américain (US-ASCII). Il est compatible avec les systèmes de fichiers, les parsers et autres logiciels basés sur des données US-ASCII. Par contre, il est transparent pour toutes les autres valeurs.

Un texte UTF-8 est donc composé de caractères ASCII et peut également comprendre de l'unicode pour définir uniquement les caractères spéciaux.
Un caractère Unicode est, quant à lui, composé d'une suite d'un ou de quatre octets. Les caractères de numéro 0 à 127 sont codés sur un octet dont le bit de poids fort est toujours nul.


Les caractères de numéro supérieur à 127 sont codés sur plusieurs octets. Dans ce cas, les bits de poids fort du premier octet forment une suite de 1 de longueur égale au nombre d'octets utilisés pour coder le caractère, les octets suivants ayant 10 comme bits de poids fort.

Un problème de sécurité évident

L'UTF8 présente donc un avantage considérable : il permet de représenter des milliers de caractères tout en optimisant l'espace utilisé. En revanche, il engendre l'apparition d'un problème majeur. En effet, les programmes mal écrits peuvent accepter un certain nombre de représentations UTF-8 et les convertir comme un seul et unique caractère.

Prenons l'exemple de la chaîne suivante : "/../ ", qui est utilisée par les pirates afin de remonter une arborescence. La forme canonique du caractère ASCII “.” est un point encodé "2 E " en ASCII). Mais il est également possible de l'utiliser sous la forme UTF-8 sur 2 octets. Nous obtenons alors une représentation étendue telle que : C0 AE.
2 E (hexa)=0010 1111=46 en décimal
C0 AE (UTF-8)= 11000 000 1010 1111=46 en numérotation décimale

La chaîne "/../ ", composée de 4 caractères peut donc se coder "2F 2E 2E 2F " et "2F C0 AE 2E 2F ".

De même, il existe encore d'autres formes de représentations étendues pour le point : E0 80 AE, F0 80 80 AE, F8 80 80 80 AE et FC 80 80 80 80 AE.

Si l'analyseur syntaxique n'est pas soigneusement écrit pour rejeter ce type de chaînes, alors une faille de sécurité potentielle est ouverte. Un virus a exploité ce problème en 2001 en attaquant plusieurs serveurs web (nimda). Toutes les entrées utilisateur (URL, formulaires…) peuvent également être saisies sous forme Unicode pour camoufler un code malicieux et permettre ainsi une grande variété d'attaques. La RFC 2279 référence un grand nombre de façons d'encoder du texte comme nous allons vous le montrer dans la suite de cet article.


L'importance du format UTF-8 vient du fait que les serveurs web et les applications exécutent certaines étapes de leurs traitements dans ce format. L'ordre dans lequel ces traitements sont réalisés est important pour la sécurité de l'application.

Fondamentalement, les étapes "URL decoding ", "UTF-8 decoding " et les étapes intermédiaires sont des étapes au sein desquelles le contrôle sur les données reçues est critique pour la sécurité du reste de l'application. Par exemple, si le contrôle qui consiste à rechercher la chaîne “..” est réalisé avant l'étape “UTF-8 decoding”, il est possible d'injecter la chaîne “..” sous la forme étendue du format UTF-8 et donc de contourner le contrôle. Même si le contrôle détecte quelques variantes pour écrire un point sous une forme non-canonique, il est toujours possible que certaines formes d'encodage ne soient pas traitées.

Quelques exemples

De nombreuses variantes d'une même url peuvent donc être formées et piéger les serveurs web. Le but de notre attaque est de lister le répertoire /bin du serveur web à l'aide d'une attaque de “Directory Transversal” :

Version non codée :
http://www.example.com/cgi-bin/bad.cgi?foo=../../bin/ls %20-al

Version URL-encodée de l'exemple :
http://www.example.com/cgi-bin/bad.cgi?foo=..%2F../bin/ls%20-al

Version Uni-encodée de l'exemple : http://www.example.com/cgi-bin/bad.cgi?foo=..%c0%af../bin/ls%20-al
http://www.example.com/cgi-bin/bad.cgi?foo=..%c1%9c../bin/ls%20-al
http://www.example.com/cgi-bin/bad.cgi?foo=..%c1%pc../bin/ls%20-al
http://www.example.com/cgi-bin/bad.cgi?foo=..%c0%9v../bin/ls%20-al
http://www.example.com/cgi-bin/bad.cgi?foo=..%c0%qf../bin/ls%20-al
http://www.example.com/cgi-bin/bad.cgi?foo=..%c1%8s../bin/ls%20-al
http://www.example.com/cgi-bin/bad.cgi?foo=..%c1%1c../bin/ls%20-al
http://www.example.com/cgi-bin/bad.cgi?foo=..%c1%9c../bin/ls%20-al
http://www.example.com/cgi-bin/bad.cgi?foo=..%c1%af../bin/ls%20-al
http://www.example.com/cgi-bin/bad.cgi?foo=..%e0%80%af../bin/ls%20-al
http://www.example.com/cgi-bin/bad.cgi?foo=..%f0%80%80%af../bin/ls%20-al
http://www.example.com/cgi-bin/bad.cgi?foo=..%f8%80%80%80%af../bin/ls%20-al

les formats d'entrée

Les applications web travaillent généralement sur un seul format de données : ASCII, ISO8859-1 ou Unicode (les programmes Java utilisent UTF-16). Vos visiteurs peuvent utiliser d'autres formats (ou "locales ") et les attaquants peuvent choisir arbitrairement l'utilisation de n'importe quel code "local ".

Le double encodage

La plupart des applications web n'effectuent qu'une seule fois l'opération de décodage des données reçues. Malheureusement, un attaquant peut encoder deux fois les données envoyées (encodage de l'encodage). Cette technique permet d'induire en erreur le contrôle et d'insérer des caractères qui seront utilisés pour l'attaque.

Tester votre application

Identifier l'encodage supporté par votre application

Une technique simple et rapide permet de tester rapidement l'encodage utilisé par votre application. En vous envoyant une requête HEAD sur le port 80 de votre site web avec l'outil telnet, vous obtiendrez en réponse le type d'encodage utilisé dans les champs "content-type ".

Double encodage : votre application est-elle vulnérable

Comme nous l'avons présenté, le double encodage est une technique utilisée pour contourner le mécanisme de validation de votre serveur web.
Il est important de vérifier si votre application web gère ce genre d'astuces. Vous pouvez utiliser les outils de conversion mis à votre disposition à la fin de l'article "XSS Cheat Sheet " du site suivant : http://ha.ckers.org/xss.html

Si l'injection réalisée sur votre application web fournit un résultat, votre application est alors vulnérable.

Comment se protéger

Utiliser les standards

Différentes solutions peuvent être implémentées afin de valider correctement les caractères envoyés par un utilisateur de votre application web.
Tout d'abord, une forme canonique doit être choisie et toutes les entrées de l'utilisateur doivent être converties vers cette forme avant d'êtres prises en compte par l'application. Les contrôles de sécurité (sanitization) doivent être réalisés après avoir correctement effectué le décodage UTF-8. Vous devez analyser les contrôles effectués par votre application afin d'être certain qu'un format précis de données a été défini par les développeurs.

Si aucun format n'est défini, les formats (par défaut) suivants sont conseillés :
-Pour les requêtes "HTTP POST " : Le format ISO 8859-1 est recommandé. Ce format ne gère pas forcément les caractères encodés sur 2 octets (double byte character). Vous devez tester votre application avec les navigateurs clients officiellement supportés afin de vous assurer que les caractères encodés sur 2 octets soient correctement supportés.

-Pour les requêtes "HTTP GET " : Le format par défaut dépend du navigateur client et du format utilisé par la précédente requête GET. L'encodage de l'URL ne gère pas forcément correctement les caractères encodés sur 2 octets. Microsoft Internet Explorer peut être contraint d'utiliser exclusivement le format UTF-8, qui est un format canonique. -.NET: Unicode (little endian*) doit être utilisé.

-Implémentations JSP, à l'instar de Tomcat : ce genre d'applications utilise le format UTF8 (Reportez-vous à la section “javaEncoding” du fichier de configuration web.xml pour tous les conteneurs de servlets).

-Java: Unicode (UTF-16, généralement en "big endian "* mais cela peut varier en fonction de l'OS natif exécutant la machine virtuelle Java).

-PHP: Par défaut dans php.ini, le format est ISO 8859-1.

NB: Plusieurs fonctions PHP réalisent des assomptions (parfois erronées) sur le format utilisé ce ce qui entraîne des problèmes lors du changement de format.

Définition explicite des formats et des locales utilisés

Le serveur web doit toujours définir et annoncer la locale et le code pays, comme par exemple : “en_US”, “fr_FR”, “zh_CN”. De la même manière, le ou les formats attendus par l'application doivent être clairement définis. L'utilisation des standards HTML précisés plus haut sont recommandés. Ainsi les entrées et les sorties seront correctement traitées par tous les navigateurs, serveurs ou autres applications.

Une fois que chacun de ces points est correctement mis en oeuvre, il est fortement conseillé de tester l'application avec divers encodages.* Little endian : ou petit-boutiste. L'octet de poids faible est stocké avant l'octet de poids fort. Utilisé chez Intel. À la place de "octet ", on peut avoir un Word, un DWord ou le couple Segment-Offset pour une adresse (dans ce cas l'offset précède le segment).

* Big Endian : ou gros-boutiste, Description d'une façon (parmi d'autres) dont on stocke les nombres dans plusieurs octets : l'octet de poids fort est stocké avant l'octet de poids faible. Utilisé sur la famille des 680x0 de Motorola, c'est la façon habituelle de voir un nombre.

Bibliographie

IDS Evasion using Unicode :
http://online.securityfocus.com/print/infocus/1232

W3C Internationalization Home Page :
http://www.w3.org/International/

XSS Cheat Sheet :
http://ha.ckers.org/xss.html

RFC2279 :
http://www.ietf.org/rfc/rfc2279.txt