Blog Haypo

Aller au contenu | Aller au menu | Aller à la recherche

vendredi 16 janvier 2009

Broadcast IPv4, socket RAW et multicast DNS

Pour tester l'interface de configuration de parefeu sur laquelle je travaille, j'ai mis en place des règles de filtrage strictes sur ma machine. Stricte car par défaut rien ne passe : il faut accepter chaque service explicitement avec une nouvelle règle. Cet article présente les cas particuliers que sont les sockets RAW et le multicast IPv4.

DHCP et table raw

Sur ma machine, l'attribution d'une adresse IPv4 est dynamique et utilise le protocole DHCP (niveau 4). Le premier paquet (DHCP Discover) est envoyé à destination de 255.255.255.255 (adresse de broadcast : envoi à tout le monde) avec comme source 0.0.0.0, et l'adresse MAC destination est aussi du broadcast (ff:ff:ff:ff:ff:ff). Bizzarement, même en bloquant tout en sortie avec iptables (iptables -F; iptables -P OUTPUT DROP), DHCP continue à fonctionner.

Ceci vient du fait que le programme dhclient utilise une socket de type RAW. les paquets sont forgés et n'entrent pas dans le suivi de connexion (conntrack) de Netfilter. On peut néanmoins loguer ou filtrer ces paquets en utilisant la table « raw ». Le schéma suivant montre que raw est le premier traitement pour les paquets entrant (chaîne PREROUTING) et sortant (OUTPUT) : schéma synthétique d'iptables.

Multicast DNS

Ubuntu active le démon avahi par défaut, système de découverte de services sur le réseau local. Il embarque un serveur DNS multicast qui n'est pas utilisé pour résoudre les noms de domaine Internet (comme google.com), mais pour la découverte de services. Ce système permet sans aucune configuration manuelle d'échanger des fichiers, d'écouter de la musique, de dialoguer, etc. avec les ordinateurs du réseau local.

Côté technique, avahi envoie et reçoit des paquets UDP sur le port « mDNS » (port 5353 vers 5353) et des des paquets IGMP (Internet Group Management Protocol, à ne pas confondre avec ICMP) en utilisant l'adresse destination 224.0.0.251. Cette adresse fait parti du réseau 224.0.0.0/24 : multicast local (réseau privé).

Note au sujet d'IPv6 : les paquets ICMPv6 multicast sont détectés comme invalides par -m state --state INVALID. Refuser tous les paquets invalides revient donc à se couper d'Internet... J'ai rapporté le bug à Netfilter.

lundi 18 août 2008

Attaquer un générateur de nombres pseudo-aléatoires

Je commence doucement à comprendre comment fonctionnent les générateurs de nombres pseudo-aléatoires (PRNG) et à voir quel est leur impact dans la sécurité informatique. En bref : la sécurité repose sur eux, trouver une faille dans le générateur revient à compromettre la sécurité !

Utilisations des PRNG

Cas où les PRNG empêchent qu'un espion puisse lire ou injecter des données dans un canal de communication (connexion réseau) :

  • L'établissement d'une connexion SSL (https dans firefox) ou SSH utilise un PRNG pour générer des clés de session
  • La sécurité du protocole TCP repose sur un PRNG

Cas où un PRNG sert à prouver l'identité d'une personne :

  • Création d'un mot de passe (ex: programme pwgen) ou d'une clé SSH utilisés pour l'authentification
  • Création d'un certificat X.509 (ex: identité d'un serveur avec https)
  • Création de clés GPG (courriels)
  • etc.

Attaquer la graine

La graine est un nombre de N bits utilisé pour construire l'état interne (M bits) du PRNG. N est souvent plus petit que M. Exemple : time(NULL), 32 bits voir beaucoup moins, est utilisé pour construire l'état interne de l'algorithme Mersenne Twister (20.000 bits). Il est plus facile de deviner N bits que M (32 vs 20.000 dans le cas de Mersenne Twister), c'est donc la première cible.

Obtenir la graine permet de reconstruire l'état interne initial complet.

Cas réel :

  • Dans la faille OpenSSL Debian, la valeur réelle de N était 15 : ce qui donne seulement 32.768 graines différentes
  • DSA-152-1 l2tpd -- missing random seed : la graine était tout simplement constante...

Une graine basée uniquement que sur time(NULL), getpid() et getppid() ne peut être utilisée dans un contexte de sécurité. Il faut utiliser de vraies sources d'entropies (ex: /dev/random sous Linux).

Reconstruire l'état interne

Si un attaquant arrive à obtenir suffisamment de bits générés par le PRNG, il pourra reconstruire l'état interne du PRNG. Dans le cas de Mersenne Twister, il suffit de 19.937 bits (ou 624 nombres de 32 bits) pour reconstruire l'état interne complet.

Pour éviter qu'un attaquant puisse reconstruire l'état interne du PRNG à partir de bits générés, on peut utiliser une fonction de hachage (ex: SHA-1) sur sa sortie. Un attaquant devra inverser la fonction SHA-1 pour obtenir l'état interne (ce qui est possible mais très coûteux).

Biais dans un générateur

Pour générer un nombre pseudo-aléatoires dans un intervalle, un mauvais PRNG va produire certaines valeurs plus fréquemment. En connaissant le biais, l'attaquant peut gagner du temps en testant ces valeurs en priorité.

Un bon PRNG donne une distribution uniforme : chaque nombre doit sortir avec une probabilité identique. Assurez-vous que le PRNG passe tous les tests statistiques connus (ex: Mersenne Twister ne passe pas tous les tests de Dieharder).

Porte dérobée dans un générateur

Il a été prouvé (lire Des Trappes dans les Clés) qu'il est possible de construire un PRNG valide (passant tous les tests statistiques) possédant une porte dérobée. Son auteur peut alors calculer les nombres générés sans pour autant connaître la graine. En pratique, l'attaquant devra toujours effectuer une recherche par force brute, mais avec un coût très inférieur au coût classique de recherche exhaustive (ex: 2^32 au lieu de 2^256).

La porte dérobée peut être introduite au niveau algorithmique (ex: On the Possibility of a Back Door in the NIST SP800-90 Dual EC PRNG) ou bien dans une implémentation.

Limiter l'effet d'une attaque dans le temps

Casser un PRNG permet de :

  • Calculer les nombres précédemment générés (passé)
  • Calculer les nombres qui vont être générés (futur)

L'opération de regénération de la graine, appelée reseed, limite la portée d'une attaque. Cette opération peut se faire après avoir généré N bits (ex: générer 1000 mots de passe) ou alors une fois que le pool d'entropie a atteint un certain seuil (dépend des générateurs matériels et non plus de la quantité de bits pseudo-aléatoires générés). Un pirate n'aura donc pas accès à tous les mots passe (certificats ou autre) précédents et suivants, mais seulement une partie.

Appeler reseed après chaque nombre généré est une très mauvaise idée si la graine est de mauvaise qualité. Exemple : appeler srand(time(NULL)) après chaque appel à rand() va avoir pour conséquence de générer plusieurs fois le même nombre durant la même seconde. Il est primordial d'utiliser une source ayant une excellente entropie.

Pour en savoir plus : Random number generator attack.

mardi 10 juin 2008

Usage des générateurs de nombres pseudo-aléatoires

Exigences vis à vis d'un générateur de nombres pseudo-aléatoires

Les générateurs de nombres pseudo-aléatoires sont utilisés principalement à deux fins :

  • Simulation : simulation physique, jeux vidéos, etc.
  • Sécurité : générer un secret pour garantir la confidentialité

Dans les deux domaines, la qualité du générateur est importante. Si un générateur est biaisé, c'est-à-dire que la distribution n'est pas équitable entre les différentes valeurs possibles, le résultat de la simulation sera invalide (ou bien simplement imprécis) et la confidentialité peut être comprise. Par contre, la simulation privilégie la vitesse du générateur. Alors que pour la sécurité on se donne les moyens pour qu'il soit difficile de deviner les nombres précédemment générés et les prochains nombres générés (quitte à ce que le générateur soit plus lent), ce qui revient à deviner quel est l'état interne du générateur étant donné qu'un algorithme est déterministe.

Générateur biaisé

Reprenons l'exemple de RANDU, un générateur biaisé (les bits de poids faible sont peu aléatoires). Si on utilise 1 + rand() % 6 pour simuler un lancé de dé, on va obtenir une suite du genre :

  1, 3, 1, 5, 3, 3, 1, 5, ...

Les faces 2, 4 et 6 ne sont jamais tirées !

Inverser un générateur congruentiel linéraire

Il est possible de deviner l'état interne d'un générateur congruentiel linéraire (LCG) en ne connaissant qu'un seul nombre généré. L'algorithme RANDU est :

  x(n+1) = (x(n) * 65539) % 2147483648

Avec x(0) : graine du générateur. On peut exécuter le générateur à l'envers en calculant :

  y(n+1) = (y(n) / 65539) % 2147483648

Sachant que diviser revient à multiplier par l'inverse, x / 65539 <=> x * (1 / 65539), on va calculer l'inverse 65539 modulo 2147483648 avec le théorème des restes chinois. En pratique, on utilise l'identité de Bezout. Fonction Python qui calcule les coefficients u et v tels quel a · u + b · v = 1 :

def bezout(a, b):
  u0 = 1; u1 = 0
  v0 = 0; v1 = 1
  while 1:
    q = a // b
    r = a % b
    u0, u1 = u1, u0 - q*u1
    v0, v1 = v1, v0 - q*v1
    if r != 0:
      a = b
      b = r
    else:
      break
  return (u0, v0)

On calcule alors bezout(65539, 2147483648) = (477211307, -14564), ce qui donne :

  65539 · 477211307 - 2147483648 · 14564 = 1

et

  (477211307 * 65539) % 2147483648 = 1

Finalement, y(n+1) = (y(n) * 477211307) % 2147483648.

Exemple :

 x(0) = 42
 x(1) = 2752638
 x(2) = 16515450
 x(3) = 74318958

On utilisant x(3), on va pouvoir générer les nombres précédents :

 y(0) = 74318958
 y(1) = 16515450
 y(2) = 2752638
 y(3) = 42

On a donc réussi à retrouver les nombres précédents jusqu'à la graine (42). Même si le générateur ne délivre que quelques bits de son état interne (ex: rand() = x(n) & 0xffff), on peut retrouver l'état interne en utilisant une recherche exhaustive (si on dispose de quelques nombres successifs).

Solutions

Pour rééquilibrer la distribution d'un générateur, on peut l'améliorer en utilisant différrentes techniques :

Lire la RFC 1750 pour les sources et les détails sur ces techniques.

Pour empêcher qu'un pirate arrive à deviner l'état interne du générateur, on peut rajouter une fonction de hachage sur la sortie du générateur (ex: MD5, SHA-1, ...). Il est toujours possible d'inverser la fonction de hachage, mais c'est beaucoup plus difficile !

lundi 9 juin 2008

Développement de la bibliothèque Hasard

Suite au bug OpenSSL de Debian, je me suis à nouveau intéressé de près aux générateurs de nombres pseudo-aléatoires (PRNG). J'ai commencé à écrire la bibliothèque Hasard qui contient plusieurs algorithmes pour générer des nombres et des fonctions de haut niveau : int(min, max), bool(), bytes(size), etc.

Outils ENT et Dieharder

J'ai utilisé le programme ENT pour tester la qualité des algorithmes. ENT utilise :

ENT n'accepte en entrée que des fichiers contenant des octets (pseudo-)aléatoires. On ne peut donc pas tester la qualité d'un générateur de nombre flottants par exemple. Exemple de sortie (algorithme RANDU avec l'opérateur pow2(8)) :

Entropy = 6.000000 bits per byte.

Optimum compression would reduce the size
of this 262144 byte file by 25 percent.

Chi square distribution for 262144 samples is 786432.00, and randomly
would exceed this value 0.01 percent of the times.

Arithmetic mean value of data bytes is 126.0000 (127.5 = random).
Monte Carlo value for Pi is 2.999862669 (error 4.51 percent).
Serial correlation coefficient is 0.322338 (totally uncorrelated = 0.0).

Il existe également le programme Dieharder qui accepte en entrée des nombres de 32 bits non signés dans un fichier binaire ou un fichier texte. Il utilise des tests bien plus rigoureux, mais comme je ne sais pas les interpréter, je ne vais pas commenter.

Représentation visuelle

C'est pas très rigolo tout ça, alors voyons un peu des dessins et schémas qui permettent de distinguer le bon chasseur du mauvais chasseur. Image générée à partir des 8 bits de poids faible de l'algorithme RANDU :

Chaque pixel utilise une valeur du générateur comme couleur. On voit clairement que l'algorithme n'est pas du tout aléatoire ! Pour reproduire l'image :

$ cd tests
$ ./gen_files.py --rng=randu --op=pow2 --bits=8 randu.dat
$ ./draw_pil.py --width=300 --height=300 randu.dat

Une autre façon de représenter des nombres est d'utiliser un axe 2D avec x=random() et y=random() :

On voit qu'il existe très peu de combinaisons (x, y) : j'en dénombre 16. Pour reproduire cette image :

$ ./gnuplot.py randu.dat --point=2

La page web graphics présente d'autres images.

Script de tests de la bibliothèque Hasard

Le script gen_files.py génère un fichier au format texte plat qui contient les nombres générés par l'algorithme RANDU pour l'opérateur pow2(8) (génère un nombre dans l'intervalle [0; 255]). Lire file_format.rst pour voir le format de ce fichier. Le script file_info.py calcule l'entropie des nombres générés, la valeur maximale et la valeur minimale.

$ ./file_info.py randu.dat
Engine: randu
Seed: linux_urandom
Range: 0..255
Operation: pow2
Count: 262144
Minimum: 1
Maximum: 251
Entropy: quality=75.00%, value=6.0000/8.0000

On voit que seul 6 bits sur 8 sont réellement aléatoires (2 bits sont invariants) et que l'intervalle annoncé est incorrect : 0..255 versus 1..251.

J'aime bien l'algorithme RANDU car il est vraiment mauvais et il permet donc de tester l'outillage de test :-)

Pour la suite

Ma bibliothèque est encore en chantier. J'ai beaucoup travaillé sur l'outillage pour tester la sortie des algorithmes en comparant avec d'autres bibliothèques comme Python, PHP ou la libc. La version 0.2 n'inclut par encore ce travail, ça sera le cas pour la prochaine version 0.3 qui n'est pas encore publiée. En attendant, vous pouvez utiliser le dépôt Mercurial.

Je ne sais pas trop ce qu'Hasard va donner au final, mais en tout cas ça avance :-)

lundi 10 mars 2008

Comment réaliser un fuzzer ?

Après de dizaines de projets d'articles avortés (mort-nés), j'ai enfin réussi à en finir un ! C'est l'article « Comment réaliser un fuzzer ? » qui est publié dans le magazine de sécurité informatique MISC numéro 36 (mars/avril 2008). J'explique quels sont les points critiques lorsqu'on écrit un fuzzer : génération des données, surveiller la cible, auto-configuration du fuzzer, etc. Image de la première des six pages de l'article :

L'article parle indirectement de mon travail sur le projet Fusil. Un deuxième article plus pratique et dédié à Fusil devrait suivre dans le prochain MISC (reste à l'écrire...).

Je regrette d'avoir oublié la section remerciements à la fin de l'article. Alors je profite de ce blog pour remercier Sebastien Tricaud, Feth Arezki, Stéphane Marchesin et ceux que j'ai oublié pour leurs relectures attentives et leurs conseils avisés. Merci aussi à Anthony Carré pour m'avoir encouragé à écrire pour des magazines papiers :-)

Au passage, si vous êtes amateur de sécurité informatique mais que vous ne connaissez pas encore MISC, je vous en conseille la lecture. Le prix élevé de 8€ est à diviser par deux car c'est un bimensuel : ce qui donne 4€/mois. D'ailleurs, il faut deux bons mois pour digérer les articles souvent complexes ;-)

mardi 4 mars 2008

Scanneur de ports PortBunny

Comme je n'ai pas pu aller au 24th Chaos Communication Congress (fin décembre 2007), je me suis rabattu sur les vidéos publiées quelques temps après. J'ai beaucoup apprécié les vidéos « (2279) Deconstructing Xbox 360 security » et « (2131) Port scanning improved ». Je voudrais parler plus particulièrement de PortBunny, un nouveau scanneur de port qui a pour logo un lapin rose. Le diaporama de la présentation est disponible en ligne.

Lire la suite

Historique de la faille vmsplice()

Le chercheur en sécurité Wojciech Purczynski, employé par COSEINC et membre de l'association iSEC Security Research, a trouvé divers bugs dans l'appel système vmsplice() du noyau Linux. Cette fonction, introduite avec Linux 2.6.17, sert à envoyer des données de l'espace utilisateur vers l'espace noyau pour alimenter un pipe. Lire mon précédent billet sur l'évolution des entrées/sorties dans Linux pour les détails. Les développeurs noyau sont prévenus dès le 1er février. Cet article relate la publication, l'exploitation, puis la correction de ces failles.

Lire la suite

lundi 4 février 2008

Sécurité des réseaux sociaux

Les années 2003 et 2004 ont vu fleurir un grand nombre de réseaux sociaux : LinkedIn, MySpace, del.icio.us, Orkut, Facebook, Flickr, ... La popularité de ces sites Internet a rapidement explosé : le nombre de membres se compte en millions. MySpace comptait 100 millions de membres en juillet 2007. Malheureusement, la sécurité de ces réseaux est régulièrement ébranlée. Pour le passage à l'année 2004, Annalee Newitz publiait l'article Defenses lacking at social network sites sur SecurityFocus. Extrait : « Williams explained that "XSS is amazingly widespread. Plus, XSS vulnerabilities are easy to discover and exploit" ». Les problèmes sont donc connus depuis longtemps. Voyons ce qu'il en est aujourd'hui.

Collecte d'informations : attaque ciblé et revente d'informations

Il y a un mois, Mary Landesman écrivait que les utilisateurs de réseaux sociaux donnent des détails sur leur vie, amour, travail, et loisirs qu'ils n'oseraient jamais dévoiler à un étranger dans un bar. Ces informations sont extrêmement utiles pour une attaque ciblée. L'attaque typique en ingénierie sociale est l'hameçonnage : envoi d'un courriel demandant à la victime d'aller sur un site Internet imittant l'habillage du site connu, ebay ou paypal par exemple, où elle sera invitée à saisir son identifiant, mot de passe ou autres informations confidentielles.

Certains réseaux sociaux indiquent ouvertement qu'ils revendent vos informations à des sociétés. Facebook en est un exemple : la section « Vie privée : la récolte et la vente des informations personnelles à des entreprises privées » de l'article Facebook de Wikipédia est forte instructive.

Heise-security vous conseille de ne pas noter d'information critique réelle telle que votre date de naissance ou votre adresse : posez-vous la question « est-ce que je dirai ça à un passant dans la rue ou à mon patron ? » avant de poster n'importe quel contenu. Petite anecdote amusante : How Facebook Ended My Marriage. Un couple ayant décidé de retirer leur fiançaille de leur profil Facebook, parce que c'était trop personnel, a provoqué une petite tornade à travers le monde. Plusieurs amis ont compris que leur mariage était annulé, certains allant même jusqu'à transmettre l'information par blog interposé.

Infection des sites Internet des réseaux

Comme on pouvait s'y attendre, les sites Internet des différents réseaux sociaux sont régulièrement infectés dans le but de prendre le contrôle du profil des membres ou bien d'infecter l'ordinateur des membres.

Samy is my hero

Le premier ver qui a fait parler de lui est le ver Samy is my hero injecté dans MySpace en octobre 2005. Le ver inscrit la victime comme ami de Samy et ajoute le message « but most of all, samy is my hero » au profil de la victime. L'auteur, Samy, a d'ailleurs monté une page web dédiée au ver. MySpace se protégait en bloquant le mot « javascript », mais Samy a outrepassé cette protection en écrivant le mot javascript avec un retour à la ligne entre java et script. Le code HTML est de la forme :

<div id=mycode style="background: url('java
script:eval(document.all.mycode.expr)')" expr="EXPLOIT"></div>

Le ver n'exploite pas vraiment de vulnérabilité de MySpace, mais plutôt du navigateur web. Pour que le ver fonctionne, ce dernier doit interprèter le Javascript inscrit dans le style CSS. Or un style CSS n'est pas prévu pour ça et le navigateur ne devrait pas le faire. Le ver fonctionnait sous Internet Explorer (Windows), mais ne touchait pas les versions récentes de Safari (Mac OS X) par exemple.

Le ver infectant automatiquement les amis des victimes (effet boule de neige), Samy a réussi à récolter un million d'amis MySpace en seulement 18 heures.

Failles Flash et WMF

En juillet 2006, MySpace a subi deux nouvelles infections. La première utilise une vulnérabilité du greffon Flash qu'Adobe avait corrigé il y à peine une semaine, lire MySpace Attacked by Flash Worm et Myspace Hack spreading like wildfire: SPAIRLKAIFS (16 juillet 2006). Le ver injecte du code HTML de la forme suivante dans le profil MySpace de la victime et installe un spyware sur l'ordinateur de la victime :

<embed allowscriptaccess="never" src="http://i105.photobucket.com/albums/mff225/yrkblack/redirecft.swf">

Quatre jours plus tard (20 juillet 2006), une publicité pour le site « DeckOutYourDeck.com » utilise une vulnérabilité WMF de Windows. Un cheval de troie est alors utilisé pour pour installer ClickSpring, un logiciel qui affiche de la publicité. Selon le Washington Post, ClickSpring a été installé avec succès sur un peu plus d'un million d'ordinateurs.

Livre d'or Orkut

En décembre 2007, c'est le réseau Orkut qui est infecté par un ver Javascript. Selon Symantec, le ver a modifié le profil d'environ 700.000 membres en 24 heures. Il modifiait le livre d'or (guest book) pour y injecter un lien vers du code Javascript. Des courriels ont également été envoyés aux victimes pour les persuader de visiter des livres d'or infectés. Les victimes étaient aussi inscrites automatiquement dans la communauté « Infectados pelo Vírus do Orkut » (infecté par le virus Orkut, en portugais). Le javascript génère du code Flash à la volé pour exécuter du code malicieux sans que la victime ne s'en rende compte. Le ver était resteint à Orkut, l'ordinateur de la victime n'était pas infecté.

Widget Facebook

Facebook propose à ses membres d'ajouter des widgets Javascript à leur compte. Début janvier dernier, un widget verollé installait le spyware Zango sur l'ordinateur des visiteurs. Le widget utilisait une <iframe> qui pointait sur l'installeur de Zango.

Infection par bandeaux publicitaires

Quelques jours après Facebook, un serveur de bandeaux publicitaires distribue du code verrolé exploitant des vulnérabilités des navigateurs web pour infecter l'ordinateur de la victime. De nombreux sites sont touchés, en particulier : MySpace, Excite.com et Blick.ch. Les bandeaux publicitaires installent des chevaux de troie comme RBot, SDBot et Spybot.

Vulnérabilités des barres d'outil

Pour faciliter la navigation, certains réseaux sociaux proposent des extensions pour votre navigateur web. Malheureusement, chacun s'est vu attribuer une faille. Petite liste d'exploits milw0rm :

Second Life

L'univers Second Life n'est pas en reste. De plus en plus populaire, il intéresse également les pirates.

Le premier événement majeur date de novembre 2006 : un ver appelé Grey Goo installe des anneaux d'or dans l'univers. Le ver a beaucoup ralentit Second Life qui a du être mis hors service pour se débarasser du ver.

En septembre 2007, une faille a été trouvée dans le client Second Life. Elle utilisait le scheme « secondlife:// » dans une <iframe> pour voler l'identifiant et le mot de passe de la victime en réalisant une fausse authentification.

Plus drôle, des chercheurs ont démontré que la vulnérabilité Quicktime pouvait être exploité dans Second Life. Ils ont fabriqué une vidéo qui volait 12 Linden dollars (5 cents américains) et faisait dire à sa victime « I got hacked » !

Comment se protéger

Les attaques présentées dans cet article utilisent souvent de multiple vulnérabilités pour monter une attaque complexe : navigateur web, Javascript, Flash, QuickTime, etc. D'une manière générale, il faut s'assurer que tous les logiciels de votre ordinateur sont à jour. Avec une distribution Linux, c'est trivial, des outils sont déjà prévu pour ça. Sous Windows, vous pouvez par exemple utiliser le logiciel Personal Software Inspector de Secunia. N'utilisant pas Windows, je ne l'ai pas testé.

Pour la navigation sur Internet, je vous conseille d'utiliser Firefox plutôt qu'Internet Explorer. Firefox est le seul navigateur web a avoir une protection Javascript efficace : l'extension NoScript.

Conclusion

Comme nous avons pu le voir, les réseaux sociaux deviennent une cible de choix car ils offrent aux pirates un moyen d'infecter un million de membres et/ou d'ordinateurs en une seule journée. Les premières attaques étaient l'œuvre d'hackers voulant démontrer leurs capacités d'outrepasser les protections. Aujourd'hui, c'est un marché lucratif qui sert à installer de la publicité et chevaux de troie sur l'ordinateur des victimes.

De nombreuses sociétés travaillent main dans la main pour rendre le web plus sûr : consultez le site OWASP pour en savoir plus.

Une fois n'est pas coutume, je voudrais dédicasser ce billet à Florent ;-)

samedi 26 janvier 2008

Failles de sécurité liées à Unicode

La complexité du jeu de caractères Unicode est source de nombreuses failles de sécurité. Cet article présente quelques failles récentes pour illustrer les problèmes qu'on peut rencontrer.

Écriture bidirectionnelle (RLO et LRO)

Le premier type que je veux présenter n'est pas un bug d'Unicode, mais une fonctionalité ! On peut changer l'ordre dans lequel est écrit le texte. Un dieu du CSS, Stu Nicholls, l'utilise pour afficher son adresse email en clair, alors qu'en fait elle est écrite à l'envers dans la souce HTML ! Le style CSS est « unicode-bidi:bidi-override; direction: rtl; ».

Sauf que des malins ont pensé à utiliser cette fonctionnalité pour tromper l'œil humain en cachant l'extension d'un nom de fichier. L'article Deceptive file names under Vista (septembre 2007) montre comment une programme Windows (.scr) est affiché comme une image JPEG (.jpg) dans Windows Vista. Windows XP ne supporte pas cette fonctionnalité et affiche donc les codes de contrôle Right-to-left override (RLO, U+202E) et Left-to-right override (LRO, U+202D), montrant alors la supercherie.

Halfwidth and Fullwidth Forms

Les failles de type « directory traversal » outrepassent les mesures de sécurité et permettent de lire un fichier arbitraire. En PHP, on trouve souvent des failles du type « index.php?page=../../../../etc/passwd ». Les webmestres se protègent en interdisant la chaîne « ../ » dans le nom du fichier utilisé. Quelques fois, il est possible d'outrepasser cette protection en spécifiant le chemin complet du fichier. Une variante est de jouer entre les caractères « / » et « \ » selon le système d'exploitation. Certains serveurs et/ou systèmes d'exploitations acceptent également « .../ » dans le nom du fichier.

Ce type de bug est aujourd'hui connu et corrigé dans la majorité des serveurs. Dumoins, c'est ce qu'on pensait jusqu'à cette annonce : Unicode encoding can be used to bypass intrusion detection systems (juin 2007). L'idée est d'utiliser les caractères halfwidth et fullwidth de la plage Unicode U+FF01-U+FFEE. Le soucis est que les URL sont normalisées après avoir été validées !

Exemple de normalisation (décomposition canonique) avec Python 2.5 :

>>> from unicodedata import normalize
>>> char=normalize('NFKC', u'\uFF0E'); print "%r (%s)" % (char, ord(char))
u'.' (46)
>>> char=normalize('NFKC', u'\uFF0F'); print "%r (%s)" % (char, ord(char))
u'/' (47)
>>> char=normalize('NFKC', u'\uFF3C'); print "%r (%s)" % (char, ord(char))
u'\\' (92)

Séquence UTF-8 invalide

Il faut savoir que 7 ans plus tôt, un bug similaire avait déjà été découvert dans Microsoft IIS (octobre 2000). Cette fois-ci, le problème était la normalisation de l'encodage UTF-8. IIS était trop laxiste : il acceptait les séquences invalides, c'est-à-dire lorsqu'un code a une séquence plus longue en octets que la taille normale. Exemple : le caractère point « . » (U+2E) s'encode « 0x2E » en UTF-8, mais peut également être encodé (0xC0, 0xAE) (forme invalide).

Note : Le langage Java utilise d'ailleurs une forme non standard d'UTF-8 : le caractère nul est encodé volontairement (0xC0, 0x80) pour éviter qu'une chaîne soit tronqué par une fonction C bas niveau (telle que strcpy).

Confusion entre octet et caractère

Depuis que le jeu de caractères ASCII a été inventé, il existe une confusion entre la notion d'octet et de caractère. C'est encore plus vrai avec les jeux de caractères ISO-8859. La très grande majorité des programmes mélangent allègrement octets et caractères sans se poser de question. D'une manière générale, ce n'est pas trop gênant. On retrouve cette problématique lorsqu'on manipule du HTML : si on tronque du texte HTML à une position donnée, il est possible qu'on coupe en plein milieu d'une balise ou d'un caractère écrit sous la forme « &nom; ». Exemple : « J'ai mang&eacute; ! » tronqué au 12e caractère donne « J'ai mang&ea ».

Exemple de vulnérabilité : WordPress Charset SQL Injection Vulnerability (décembre 2007). Le problème apparait lorsque la base de donnée utilise un jeu de caractère chinois : Big5 ou GBK. La fonction qui échappe les chaînes de caractères SQL utilise addslashes() qui travaille sur des octets et non pas des caractères. La séquence d'octets (0xB3, 0x27) est alors échappée en (0xB3, 0x5C, 0x27). Or 0xB35C est un caractère valide en Big5, et on obtient donc une apostrophe seule !

Exemple avec Python 2.5 :

>>> user='\xB3\x27'
>>> sql=user.replace("'", "\\'")
>>> print unicode(sql, "big5")
許'

Le problème de fond est que PHP ne supporte pas Unicode. Il va falloir attendre PHP6 qui est en cours de gestation. Notez que ce genre de bug touche également les programmes écrit en C, Java ou même en Python. Bien que Python propose le type unicode, il est rarement utilisé bien que complet. Le module re (expression régulières) supporte les expressions unicode. Python 3000 vise, entre autre, à encourager l'adoption d'Unicode comme type par défaut des chaînes de caractères.

Autres bugs des implémentations d'Unicode

La bibliothèque Qt de Trolltech calculait mal la longueur des chaînes UTF-8 (il manquait un "+1") : Bugzilla Bug 269001: CVE-2007-4137 QT off by one buffer overflow (rapport de bug avec patchs pour Qt3 et Qt4, août 2007).

La fonction repr() du langage Python n'allouait pas assez de mémoire pour les chaînes Unicode : buffer overrun in repr() for unicode strings (août 2006, lire aussi le CVE-2006-4980). La fonction repr() n'allouait que 6 octets par caractère en ne considérant que la forme « \uXXXX », or la forme « \Uxxxxxxxx » peut être nécessaire et consomme 10 octets par caractère.

Conclusion

Unicode regorge de fonctionnalités qui sont souvent méconnues. Mal utilisées ou utilisées à mauvais escient, ça peut faire très mal. Je pense qu'il manque des fonctionalités de sécurité dans les bibliothèques Unicode. Les encodages non standards doivent être rejettés ou une alerte doit être déclanchée. Le module mod_security d'Apache propose ce genre de fonctionnalité : voir SecFilterCheckUnicodeEncoding et @validateUtf8Encoding. Il faudrait pouvoir désactiver toutes les fonctionalités Unicode et n'activer que ce dont on n'a besoin pour éviter les effets de bord indésirables.

vendredi 18 janvier 2008

Analyser un fichier binaire ou un programme inconnu

Cet article explique comment analyser un fichier d'origine peu sûre (ex: Internet) ou dont le format est inconnu (rétro-ingénierie). Il n'est sûrement pas exhaustif, mais liste divers outils bien pratiques pour ce genre de travail.

Avertissement

Lorsqu'on traite un fichier d'origine inconnue, il faut être sur ses gardes. Il se peut que le fichier attaque volontairement les outils d'analyse cités dans cet article. Les virus sont connus pour cracher les débogueurs et/ou changer leur propre comportement lorsqu'ils sont analysés. Travaillez sur une machine dédiée aux tests (ex: machine virtuelle), ou bien avec des privilèges minimaux (ex: machine coupée du réseau).

Détecter le type d'un fichier inconnu

Quand on reçoit un fichier binaire d'un type inconnu, le programme le plus utile est file. Il détermine le format du fichier à partir d'une importante base de signature. Il sait extraire certaines méta-données (dimension d'une image, version du format, etc.) et sait également faire la différence entre les sous-formats (tel que AVI ou WAVE pour le format RIFF, et Theora et Vorbis pour Ogg).

D'autres programmes peuvent servir pour identifier le format ou plutôt extraire les méta-données :

  • hachoir-metadata : supporte un grand nombre de formats de fichiers
  • extract : supporte un grand nombre de formats de fichiers
  • Kaa : images, sons et vidéos
  • identify : images, fait parti de l'excellente suite Image Magick

Analyse manuelle d'un fichier binaire

Le programme strings sert à extraire des chaînes de caractères d'un fichier binaire. Il vous faudra peut-être tester différentes options (encodages des chaînes) pour obtenir satisfaction. Souvent, strings donne beaucoup de faux positifs (la sortie est assez bruitée).

Un éditeur hexadécimal est toujours pratique pour rechercher visuellement des motifs, des chaînes de caractères, informations cachées, etc. J'utilise « hexdump -C fichier » ou bien khexedit (programme KDE).

Quand un fichier semble vraiment trop aléatoire, il se peut qu'il soit compressé et/ou chiffré. J'ai écrit un petit script « entropy.py » qui calcule l'entropie des symboles (mot de 8 bits) d'un fichier. Quelques exemples :

  • Programme EXE PE : 4,11 bits/symbole
  • Page HTML : 4,89 bits/symbole
  • Document PDF : 7,75 bits/symbole
  • Image JPEG et PNG : 7,87 et 7,82 bits/symbole
  • Archive gzip (.tar.gz) et bzip2 (.tar.bz2) : 7,99 bits/symbole

Au delà de 7,5 bits/symbole, il y a de fortes chances que le fichier contienne des champs compressés. C'est le cas dans les exemples, mais cet outil n'est qu'une mesure empirique.

Pour trouver les blocs compressés, une solution est de tenter de décompresser à partir du 1er octet, puis du 2e, etc. Le script « find_deflate.py » implémente justement cet algorithme, lent mais il fonctionne.

Enfin, l'outil hachoir-subfile permet de rechercher les fichiers contenu dans un fichier binaire en recherchant des motifs (marqueur de début, marqueur de fin) et en vérifiant que le fichier trouvé est valide (pour limiter les faux positifs). Il existe beaucoup d'outils similaires tels que Photorec et Scalpel, ou encore TestDisk et Sleuth Kit qui sont eux dédiés à l'analyse d'images de disque dur.

Analyse statique d'un programme

Ayant majoritairement travaillé sous Linux, je ne parlerai que des programmes ELF. L'outil objdump affiche de nombreuses informations sur un fichier ELF tel que ses sections, les symboles (objdump -T fichier) et sait désassembler du code. L'outil nm liste les symboles des bibliothèques statiques (extension du fichier « .a »). L'outil ldd liste les bibliothèques importés par un programme ou une bibliothèque avec le chemin complet qui sera utilisé. Enfin, elfsh est une suite complète d'outils pour l'analyse de fichier ELF.

Analyse dynamique

Analyser un programme sans l'exécuter ne permet que d'extraire de maigres informations. Il est toujours plus instructif de l'exécuter. Il existe de nombreux outils pour tracer un programme. strace affiche les appels systèmes. ltrace affiche les appels aux bibliothèques dynamiques, mais sait également tracer les appels systèmes. gdb est le grand classique parmis les débogueurs, boîte à tout faire.

Pendant l'exécution du programme, « lsof -p pid » affiche les fichiers qu'il a ouvert et « netstat » permet d'afficher les connexions réseaux.

Autres outils que je n'ai pas testé :

J'ai écrit un binding Python pour ptrace qui permet d'écrire facilement son propre outil d'audit à la manière de strace ou ltrace. Enfin, mon bref article Syscall contient mes divers notes sur les appels systèmes Linux.

Sites Internet

Pour en savoir plus sur le sujet, voici quelques sites très instructifs :

dimanche 2 décembre 2007

Bugs des générateurs de nombres pseudo-aléatoires

Maintenant que vous savez ce qu'est qu'un générateur de nombres pseudo-aléatoires, voyons en les bugs historiques, ainsi que les utilisations incorrectes qui en sont faites. Une mauvaise initialisation de la graine peut compromettre la sécurité de votre application. De même, la fonction rand() doit également être utilisée correctement sous peine d'abaisser l'entropie du résultat. Ce billet présente de multiple exemples dont le numéro de séquence TCP, pour finir sur les fuites d'information.

Lire la suite

Générateurs de nombres pseudo-aléatoires

Récemment, plusieurs vulnérabilités ont été trouvées dans des générateurs de nombre pseudo-aléatoires : Linux, Windows, FreeBSD et OpenSSL. Je prépare un billet dessus, mais avant je vais m'attarder sur les générateurs : comment un ordinateur purement déterministe pourrait être aléatoire ? Ce billet présente les algorithmes utilisés, comment mesurer la qualité d'un générateur, le groupe de travail du NIST, le choix de la graine et enfin les générateurs matériels.

Lire la suite

samedi 27 octobre 2007

Analyse statique de code et audit de sécurité

Alors que je cherchais un outil d'analyse statique pour PHP, j'étais tombé sur la page Tool Survey du projet Software Assurance Metrics And Tool Evaluation. Ce projet, mené par le NIST et financé par le DHS, m'intéresse car il vise à tester et classer divers outils servant pour un audit de sécurité. Plus particulièrement, la page Source Code Security Analyzers dresse une longue liste d'outils d'analyse statique de code.

Rien ne vaut une relecture manuelle et attentive

Je garde un souvenir amer de l'analyse statique. J'avais testé SPlint, FlawFinder et RATS pour m'aider à relire le code source du pare-feu NuFW écrit en C. Ces outils sont peu efficaces car on est rapidement noyés sous une tonne de faux positifs. On perd finalement plus de temps à affiner leur configuration qu'à trouver des bugs. Je préfère encore une relecture manuelle et attentive du code.

Quand les ressources humaines sont insuffisantes pour relire toute la base de code, il faut choisir efficacement les portions de code à auditer. Il vaut mieux commencer par celles qui traitent les données venant de l'utilisateur. Une autre approche est de rechercher des erreurs classiques comme les dépassements de tampon, erreur de formatage printf, injection SQL, etc. Je vous conseille d'ailleurs d'aller lire les recommandations du CERT : Secure Coding Standards.

Trouver une erreur memset avec Google

On peut s'amuser à exploiter les moteurs de recherche de code pour trouver des failles connues : koders.com, krugle.com et Google codesearch. Seul Google codesearch supporte les expressions régulières, sans référence arrière malheureusement. On peut également trouver des mots de passe codés en dur et autres indiscrétions. C'est le moment de redécouvrir la Google Hacking Database pour vous donner des idées. Recherchons par exemple une utilisation incorrecte de la fonction memset() par l'inversion du 2e et 3e argument. Utilisez le motif suivant avec Google codesearch :

memset\([^,]+,[^,]+,\ *0\)

On trouve cette erreur dans divers projets dont certains très connus : OpenSSL, GnuPG, Prelude, Linux, Mozilla, Python, Parrot, Kaffe, aMule, µClibc, libgphoto2, ATI gatos, WINE, Blender, etc. Après vérification dans quelques projets (en particulier OpenSSL, GnuPG et Prelude !), l'erreur est corrigée dans la dernière version. Le fait qu'elle ait existé un temps prouve que de meilleurs outils d'analyse statique seraient utiles !

Autre exemple : erreur memcpy

L'instruction « memcpy(dest, source, sizeof(dest)); » est incorrecte quand dest est un pointeur. La taille copiée est celle du pointeur et non pas celles des données pointées ! Voici donc deux commandes pour rechercher des utilisations incorrectes des fonctions memcpy(), memmove(), strncpy(), g_memdup(), memset() et wmemset() :

find DOSSIER -name "*.c"|xargs egrep -H '(memcpy|memmove|strncpy|memset|g_memdup)\(([^,]+), .*sizeof\(\2\)\)'
find DOSSIER -name "*.c"|xargs egrep -H 'w?memset([^,]+,[^,]+, *0)'

Sauf que strncpy() (et ses voisines) peuvent fonctionner pour « strncpy(dest, source, sizeof(dest)); » quand dest n'est pas pas un pointeur mais un tableau de taille fixe comme « char buffer[256]; »...

Complexité McCabe d'une fonction

Pour finir, un ami (misc) m'a fait découvrir aujourd'hui l'outil pmccabe par le billet The Cyclomatic Horror From Outer Space. Ce programme sert à estimer la complexité d'une fonction sachant qu'une note supérieure à 50 indique une fonction « non testable (risque très élevé) ». Je me suis amusé à lancer pmccabe sur la GNU libc avec la commande :

rm /tmp/out; find . -name "*.c"|xargs pmccabe >>/tmp/out; sort -nr /tmp/out|head

Voici les pires fonctions :

  • 494 : scanf()
  • 230 : fnmatch()
  • 222 : strtod()
  • 200 : collate_read()
  • 197 : dl_main()
  • 175 : wordexp()

Dans les commentaires du blog, on apprend que gcc explose le record avec une fonction à plus de 1000. Une scéance de refactoring cuisse-abdo-fessier ne ferait pas de mal à la libc et à gcc...

mercredi 8 août 2007

Sécurité des systèmes virtualisés

La virtualisation est décidément à la mode. Niveau logiciel (libre) : QEMU, Xen, Bochs et VirtualBox offrent un large panel de possibilités. Niveau matériel, les microprocesseurs grand public gagnent des jeux d'instructions supplémentaires avec Intel VT (Virtualization Technology) et AMD-V (AMD Virtualization).

La virtualisation présente un intérêt en sécurité pour isoler les composants logiciels. C'est une alternative aux prisons logicielles existantes : chroot(*), BSD Jail, zones Solaris, etc.

Un nouveau moyen de protection attire forcément des hackers qui vont chercher à le contourner. Joanna Rutkowska a proposé en juin 2006 une technique appelée Blue Pill (la pillule bleue du film Matrix) qui permet d'être totalement invisible pour le système d'exploitation. En avril 2007, Rutkowska fonde la startup InvisibleThingsLab, et récemment le code source de Blue Pill est rendu disponible sur bluepillproject.org (vous y trouverez présentations et détails techniques). Lire également l'annonce par heise Security.

En février dernier, Tavis Ormandy, de l'équipe sécurité de Google, publie un papier sur la sécurité des hôtes virtualisés pour CanSecWest 2007. Il a trouvé plusieurs failles de sécurité comme par exemple dans la carte réseau de Bochs : NE2000 RX Frame Overflow (mai 2007). N'ayant pour l'instant que survollé le papier, j'ai cru comprendre qu'il a utilisé le fuzzing avec deux outils : crashme et iofuzz (écrit à l'occasion ?). Il va falloir que je les teste un de ces jours, ainsi qu'ioctlfuzz et syscallfuzz de la Digital Dwarf Society :-)

(*) chroot est une prison logicielle écrite pour des raisons pratiques et non pas pour répondre à une problématique de sécurité, il existe de nombreuses manières de s'en évader !

mercredi 6 juin 2007

Bilan du SSTIC 2007

Me voilà de retour de Rennes où j'étais avec 3 collègues « nupik ! » pour le SSTIC. Ces trois journées de conférences étaient denses en information et vraiment très intéressantes. J'ai été agréablement surpris de recevoir les actes sur papier dès notre arrivée : plus de 300 pages contenant les publications de toutes les conférences ! Ça va me permettre d'en savoir plus à tête reposée. Je vous présente les conférences qui m'ont le plus marqué.

Mercredi : authentification Windows

Aurélien Bordes nous a offert une excellente présentation des « Secrets d'authentification sous Windows ». Il a réussi à vulgariser des techniques et concepts obscurs de Windows. En résumé : même si les hashs LM ne sont plus stockés sur disque dur, ils persistent en mémoire ! On peut également s'authentifier uniquement en connaissant un hash. Et enfin, si j'ai bien compris : il suffit d'être administrateur sur sa machine pour réussir à passer administrateur sur tout le domaine Windows.

Ce qui est drôle, c'est qu'il a supposé pour commencer qu'on est administrateur de la machine. Comprendre : c'est tellement facile de passer admin sous Windows, que je ne donne pas la peine de vous montrer comment ! Effectivement, les exploits Windows pleuvent ! Sortez les parapluies (hop, petite réferrence cachée à la météo Bretonne où il ne pleut que sur les ....).

Mercredi : analyse de canaux cachés

Christophe Clavier m'a enfin éclairé sur les canaux cachés dans sa conférence « Attaques par analyse de canaux cachés et de fautes. Applications aux algorithmes à spécifications secrètes ». En enregistrant la consommation de courant d'une puce électronique et en la bombardant avec un laser (c'est Starwars !), il en extrait des informations juteuses. Il a montré qu'on peut reconnaître des motifs dans les mesures de courant et deviner quels bits (d'une clé privée RSA) sont à 0 ou à 1. Avec ses schémas ça semblait clair... mais n'empêche que l'unité était un cycle processeur : on travaille donc à l'échelle de la nanoseconde !

Jeudi : fuzzing wifi

Alors que je n'avais pas terminé ma nuit, voilà que Laurent Butti et Julien Tinnès débarquent en parlant de « Recherche de vulnérabilités dans les drivers 802.11 par techniques de fuzzing ». Wow, je suis tout à vous les gars ! Toady m'avait déjà passé la présentation la semaine précédente, mais c'était bien plus clair avec les explications.

Je pense que tout le monde a été bluffé dans la salle lors de la démo : avec quelques paquets wifi, un exploit noyau est installé sans faire tomber le pilote wifi... alors qu'il utilise justement un bug dans le pilote. Le pire est que l'exploit se fait lors de la première phase de connexion wifi : avant l'authentification ! S'insérer dans un noyau sans contact physique, ça laisse quand même songeur.

J'étais aussi heureux de voir le travail d'autres personnes sur le fuzzing. Comme quoi cette technique est très hype ;-)

Jeudi : Metasm

Yoann Guillot nous a présenté son framework Ruby « Metasm, l'assembleur ». Cette boîte à outil permet d'insérer du code dans un programme en cours de fonctionnement, d'assembler des blocs de code, de compiler en assembleur à la volée, et bien d'autres choses.

J'ai surtout retenu qu'on peut écrire des shellcodes en assembleur en laissant des « trous » dans le code : des variables qui seront remplacées à la compilation ! Metasploit 3, également écrit en Ruby, va se baser sur Metasm pour les shellcodes. Ce qui évitera les BLOBs (shellcodes binaires peu souples) et vilains hacks.

Jeudi : analyse de la mémoire vive

J'entend de plus en plus parler d'attaques « tout en mémoire ». Nicolas Ruff nous le confirme dans sa présentation « Autopsie d'une intrusion « tout en mémoire » sous Windows ». Il y détaille toutes les méthodes pour dumper la mémoire : firewire, fichier d'échange (swap), génération d'écran bleu de la mort, hibernation, reset physique de la carte mère, etc. J'étais surpris par l'idée d'un reset de la machine mais selon ses dires, la mémoire est inchangée même après 15 secondes hors tension ! On pourrait donc débrancher puis rebrancher le PC tout en conservant le contenu de la RAM. Il parle alors de système d'exploitation minimaliste (il ne faut pas écraser la RAM !) pour dumper la mémoire.

Après le dump, il faut analyser l'image. Il présente deux méthodes pour retrouver l'organisation de la mémoire : recherche du registre CR3 ou recherche de motifs en mémoire (ex: recherche des structures des processus). Ses travaux sont très prometteurs et de plus en plus d'outils sont disponibles.

Jeudi : projet Fusil

Durant les rump sessions, j'ai eu l'honneur de parler de mon projet Fusil : présentation Fusil en PDF. C'était un exercice de style pour arriver à tenir dans quatre minutes et pas une seconde de plus. J'étais frustré de ne pas avoir plus de temps, mais au final c'est vrai que c'est tout bénef' : les rumps ennuyeuses sont vites zappées et on peut en voir un plus grand nombre en un temps minimal ;-)

Vendredi : OpenDocument et OpenXML

Philippe Lagadec confirme mes craintes : sa présentation « Sécurité d'OpenDocument et Open XML (OpenOffice et MS Office 2007) » montre que les protections des macros dans OpenOffice et Microsoft Office sont inefficaces. La politique de sécurité est incohérente et il est facile de tromper l'utilisateur (Social Engineering en force !). J'ai appris qu'OpenOffice peut embarquer des documents OLE2 de n'importe quel type. C'est sûrement pour des raisons de compatibilité avec Microsoft Office (sic). Or il existe un objet OLE2 permettant de lancer une ligne de commande arbitraire.

Bilan

Je rentre du SSTIC la tête grosse comme une pastèque. Je vais devoir creuser tout ça pour en savoir plus.

À peine rentré du SSTIC, je fonce dormir chez un ami pour y préparer mes présentations Python car le lendemain (Samedi) j'attaque les Journées Python Francophones.

Merci encore à toady qui nous a évité de dormir sous les ponts la première nuit. Il nous a dépanné en nous acceptant dans sa chambre d'hôtel. Tout de suite, ça crée des liens :-D