Insomni’hack 2011

8 mars 2011 – 1:38

Ce week-end s’est déroulé Insomni’hack 2011, événement de sécurité se tenant à Genève. 4h de conférences, 6h de challenges, une bonne organisation et une ambiance détendue, tout ce qu’il fallait pour passer une bonne soirée. Comme Bruno, je me lance dans un compte-rendu des conférences et du challenge.

[UPDATE 03/03/2011 : la résolution de l'épreuve du padding oracle a été présentée au dernier meeting HZV, les slides sont en ligne ici]

Conférences

Sourcing and Management in IT Security

L’événement commence par une conférence orientée RSSI présentée par Sébastien Bombal, ayant pour thème la gestion du sourcing en matière de sécurité. Celle-ci aborde les problèmes causés par la cascade de sous-traitance, depuis les débuts de l’out-sourcing jusqu’au cloud-computing, ainsi que les normes associées. Autant le dire tout de suite : cela n’étant pas mon domaine, je n’ai pas vraiment réussi à suivre de bout en bout…

Mobile Malware In Practice

Axelle Apvrille expose dans cette conférence 3 exemples de virus ciblant les smartphones, et plus précisément Symbian. Le message est clair : les concepteurs de virus ne développent pas leurs bestioles pour le fun, mais pour se faire de l’argent. Le principe KISS s’applique aussi aux malwares ! Les exemples montrés se contentent d’envoyer des SMS à des numéros surtaxés, ou d’intercepter des codes d’accès bancaire.  Ce que qui parait fou, c’est que dans certains cas, ces malwares sont mêmes signés par Symbian ! En effet, comme l’a soulignée la chercheuse, toutes les applications ne sont pas systématiquement analysée avant d’être déployées sur le marché. On apprécie aussi les API obscures de Symbian, telles que celle permettant à toute application de désactiver les messages de confirmation utilisateur lorsque celle-ci souhaite avoir accès à Internet, ou encore celle qui installe silencieusement une application…

La cryptographie

Avec un tel titre, tout le monde s’attendait à un cours de crypto à la sauce universitaire, bourrée de formules dans tous les sens. C’est d’ailleurs ce que semble proposer son auteur, Pascal Junod.

La crypto, c'est cool !

Mais heureusement, après avoir exposé son sommaire qui se révèle être factice, l’auteur dévoile le véritable but de la conférence : faire un état de l’art de la cryptographie actuelle et de ses échecs pratiques. Une présentation extrèmement intéressante, qui appuie où ça fait mal : l’implémentation des algorithmes de chiffrement, et leur choix. Tout y passe : La XBox et TEA, WEP et RC4, MD5, IPSEC en mode encrypt-only, suites de chiffrement TLS 1.0 avec des tailles de clés minuscules, le secure boot des TI (RSA-512 cassé en 73 jours par un quad-core), et même l’ultra-célèbre LM-hash. Que reste-t-il au final ? AES, SHA-2 (bientôt SHA-3), RSA-OAEP et RSA-PSS.

Mais c’est sans compter les attaques side-channels, qui regroupent timing-attacks, injection de fautes, mesures de consommation et d’émanations électro-magnétiques. Et ces dernières sont d’autant plus critiques sur les plate-formes embarquées. Pascal termine alors par un benchmark des librairies opensource de crypto implémentant RSA-OAEP, vis-à-vis de leur résistance à l’attaque de Manger. Et le résultat est pour le moins… alarmant.

Résultats du benchmark

Aucune librairie (pas même OpenSSL) n’est à ce jour complétement protégée, et il s’agit d’un véritable problème sur plateformes embarqués. La faute en partie aux multiples brevets détenus par des sociétés spécialisées disposant d’algorithmes side-channel-proof…

Les outils du mentalisme au service du social engineering

Dominique Clementi montre dans cette conférence comment le mentalisme peut aider un social-engineer à arriver à ses fins. Le but du mentaliste étant d’embrouiller (fuzzer, si je puis dire !) ses victimes pour leur faire admettre l’idée que quelque chose de surnaturel s’est produit. Ses armes : des gimmicks, des outils automatiques, et surtout beaucoup de psycologie. Entre autres : suggestion, PNL, forçage, hypnose, cold reading… Une conférence très sympa qui m’a beaucoup rappelé les quelques années durant lesquelles j’ai pratiqué le close-up (que je pratique toujours, mais très accessoirement).

ASP.NET et la sécurité du Viewstate

Alexandre Herzog expose les failles de sécurité concernant le composant Viewstate des plateformes IIS. Il s’agit d’une variable d’état envoyée entre le navigateur et le serveur qui recense entre autres les différents contrôles sérialisés d’une page Web. Celle-ci est généralement envoyée par le biais d’un champ caché, mais également par l’URL. Le problème se pose alors lorsque le filtrage de cette variable fait défaut côté serveur, et mène à des XSS volatiles. Heureusement, par défaut un HMAC est effectuée sur cette dernière, ce qui la rend tamper-proof, et c’est d’ailleurs ce qui est conseillé afin d’éviter les attaques. L’auteur montre par ailleurs que le module de validation de requêtes d’ASP.NET est inefficace sur cette dernière, car il possède des filtres très minimaux. Il en est de même pour certaines versions du module d’encodage HTML (équivalent de htmlentities()), qui ne filtre pas les apostrophes avant .NET 4…

 

Un filtre à toute épreuve... ou pas.

Reinventing Old School Security

Bruno Kerouanton termine en beauté par une conférence très axée old-school. Il y montre que la plupart des techniques de protections logicielles actuelles se basent sur celles utilisées par la demoscene à l’époque des MO5, Comodore 64 et autres Atari ST. Il faut dire que ça fait bizarre de voire de telles bestioles qui datent d’avant ma naissance :) . Au programme : anti-debug, compression et obfuscation du code, utilisation du DMA, écriture entre les pistes des disquettes, et désynchronisation de l’écran pour pouvoir afficher une résolution plus grande que celle prévue (les prémices de la HD ?). On y découvre même les origines du fuzzing (utilisation d’opcodes non documentés), du GPGPU (utilisation du lecteur de disquette pour accélérer le traitement) et des hooks d’interruptions. Vraiment sympa !

Challenge

Cette année, 200 participants sont au rendez-vous pour le challenge ! Et cette fois, les équipes étaient autorisées ; c’est donc au sein de HZV que j’ai concouru. Cette année, SCRT a amélioré l’organisation en distribuant des badges RFID aux équipes afin d’accélérer la validation des points. Non, le pétage de ces badges ne faisait pas partie du challenge (et il n’aurait de toutes façon rien apporté :) ). Par contre, gros point noir : comme l’année dernière, il n’y avait pas d’accès Internet. Seules les équipes prévoyantes (et disposant d’un forfait suisse) avaient prévues une connexion 3G…

Peu de temps après l’installation, l’interface contenant la liste des épreuves est disponible. Au programme : crypto, prog, réseau, reversing, stéganographie, et web. Voici un résumé de ce que j’ai pu faire.

Crypto 1

Ayant laissé le Web aux autres, je me suis personnellement acharné dans un premier temps sur les épreuves de crypto (personne ne voulait les faire…), à commencer par la première. Il s’agissait d’un texte chiffré, le but étant de le déchiffrer. Un indice : une table de fréquences de texte anglais traînait à côté du fichier, indiquant que certaines lettres était bien plus fréquentes que d’autres… La substitution mono-alphabétique était dont tout indiquée. Cependant, n’ayant pas retrouvé Cryptool tout de suite, je me suis galéré à calculer les fréquences du texte dans un premier temps en Python, et à commencer la substitution à la main, puis avec l’outil une fois retrouvé (ou comment perdre du temps lorsqu’on est pas réveillé). Finalement, le texte déchiffré n’était autre que le Hacker’s Manifesto, de The Mentor… Souvenir souvenir :) .

Reverse 4

Il s’agissait d’une application Android à keygenner : validate.apk. On sort le SDK ainsi que dex2jar et jd-gui, et zou !

public void performCheck()
{
 if (this.mEditText_number != null);
 int k;
 try
 {
 String str1 = this.mEditText_number.getText().toString();
 Integer localInteger1 = Integer.valueOf(
    Integer.parseInt(str1.substring(0, 4)));
 Integer localInteger2 = Integer.valueOf(
    Integer.parseInt(str1.substring(4, 8)));
 byte[] arrayOfByte1 = { 76, 76, 147, 123, 103, 204, 141,
    120, 92, 234, 30, 66, 204, 234, 24, 92 };
 int i = localInteger1.intValue();
 int j = localInteger2.intValue();
 String str2 = Integer.valueOf(i + j).toString();
 byte[] arrayOfByte2 = getMD5(str2);
 k = 0;
 if (k >= 16)
 {
 label185: this.mEditText_number.setText("validated!");
 label201: return;
 }
 int l = arrayOfByte2[k];
 int i1 = arrayOfByte1[k];
 if (l == i1)
 break label241;
 label241: this.mEditText_number.setText("fail!");
 }
 catch (Exception localException)
 {
 localException.printStackTrace();
 break label201:
 k += 1;
 break label185:
 }
 }
}

Le code généré est un peu crado, mais on comprend très vite le traitement effectué. Le serial rentré doit être composé de chiffres, ceux-ci sont découpés en 2 groupes de 4 pour former 2 entiers, et le md5 de leur somme est ensuite comparé à une valeur constante. Autrement dit, il suffit de bruteforcer de la sorte en Python :

import md5
MAGIC = "4c4c937b67cc8d785cea1e42ccea185c"

for i in range(10000):
  for j in range(10000):
    if(md5.new(str(i+j)).hexdigest() == MAGIC):
      print i, j

Le résultat tombe au bout d’un dixième de seconde : MAGIC == MD5(« 6012″). Toute combinaison dont la somme fait 6012 est valide ; on choisit donc « 60120000″ qui fonctionne à merveille.

Programmation 4

Les épreuves les moins validées rapportant plus de points, je me suis lancé dans celle-ci. Il s’agissait de se connecter sur un port, et de résoudre un challenge 100 fois de suite. Celui-ci était simple : 4 séries de lettres A, B, C et D étaient affichées sous la forme d’un carré, avec une lettre par ligne remplacée par un X. Le but : répondre au serveur la liste des 4 lettres manquantes. Rien à voir avec le hack, mais fun quand même :) . Voici le code du client qui résout l’épreuve (désolé, je n’ai pas celui du serveur) :

import socket

s = socket.socket()
s.connect(("epreuves2.insomni.hack", 4567))

def get_challenge():
  r = s.recv(4096).split("\n")[:-1]
  print r
  return [i.split() for i in r]

def solve_line(line):
  for i in range(4):
    l = chr(ord('A')+i)
    if(l not in line):
      return l

def solve_one_challenge():
  c = get_challenge()
  res =  "".join([' '.join(solve_line(c[i])) for i in range(4)])
  print res
  s.send(res+"\n")

for i in range(110):
  solve_one_challenge()

Oui, le programme crashe lamentablement une fois les 100 challenges résolus, et alors ? Le flag est bien retourné par le serveur et apparaît de façon bien visible (désolé, je n’ai pas de screenshot).

Crypto 2

A priori, on s’attend à un niveau légèrement plus élevé que la crypto 1. Que nenni, il s’agit en réalité d’une exploitation de Padding Oracle… En gros, elle consistait à déchiffrer un texte chiffré en 3DES utilisé en mode CBC avec un padding PKCS #5. Bien entendu, sans connaître la clé. Seules information disponible : on disposait d’une URL permettant de chiffrer un texte choisi, et une 2ème permettant de vérifier si le chiffrement était valide (comprendre padding) ou non. En d’autres termes, ce que l’on appelle un oracle de padding.

Je m’y étais intéressé vaguement il y a quelques mois, mais je n’avais à présent jamais réalisée en pratique. Faisable, me direz-vous… sauf que sans accès à Internet, cela l’est nettement moins. Je me suis donc acharné sur cette épreuve, en essayant de me souvenir des détails de l’algorithme de bruteforce intelligent. Au bout de plus de 2 heures, j’ai finalement abandonné. La suite plus bas dans cet article…

Web 1

Ne voulant pas rester sur un échec, je décide alors de donner un coup de main à mes collègues sur l’épreuve Web 1. Il s’agissait de générer le QR-code d’un billet pour une place dans un match qui se trouvait à un emplacement précis, un billet existant étant fourni. Armé de mon Android, le décodage du QR-code n’a pas pris longtemps,celui-ci contenait le numéro de place en clair, ainsi qu’un md5 calculé à partir de ce dernier. Le modifier semblait donc trivial. Le problème a commencé à se poser en ce qui concerne la génération du nouveau QR-code. De multiples bibliothèques existent, mais encore une fois, le problème de l’absence d’accès à Internet se faisait cruellement sentir… Jusqu’au moment ou un organisateur nous fasse remarquer qu’un outil (zxing) est visiblement disponible sur le repository dédié aux outils. Après avoir mis un bon quart d’heure à comprendre comment lancer l’interface flash de l’outil (aucun jar n’était fournit et nous ne disposions pas d’ant pour compiler les sources…) nous avons finalement réussi à générer une place valide.

Résultats du challenge

A 1h du matin, les scores sont freezés : l’équipe bles (l’équipe Nibbles était fragmenté en 2 teams Ni et bles…) arrive en tête, et HZv en 2ème. Nous restons toutefois très satisfaits de notre performance, vu que nous n’étions que 5, et relativement peu entraînés (première participation à un CTF pour 4 des participants), et sans accès Internet. Dans la table des scores qui suit, les breakthroughs correspondant aux épreuves qui ont été validées en 1er par la team en question.

Scores finaux

Scores finaux (merci Shell-Storm !)

Padding Oracle, motherfucker, can you sploit it??

Ce n’est que le surlendemain, après avoir été hanté par l’épreuve crypto 2 (comme le lapin blanc de l’année dernière), que je m’y suis repenché sérieusement, lors de mon retour en France. Suite à une discussion avec le concepteur de l’épreuve, celui-ci a accepté de me fournir les sources et a accepté de les rendre publiques, ce qui était très sympathique de sa part. Ayant profité du week-end pour récupérer le papier exposant l’algorithme d’attaque (également repris ici), mon défi est alors de résoudre l’épreuve avant d’arriver Gare de Lyon (et que la batterie de mon laptop soit accessoirement épuisée).

Et finalement, j’y suis parvenu ! Le script decrypt.py brute-force l’oracle jusqu’à ce que le texte soit totalement déchiffré.

$ decrypt.py
107 100 14 189 34 129 105 16
45 183 29 222 187 198 199 73
186 149 253 197 187 249 250 132
122 234 55 178 99 117 171 142
C'est_bon_Peggy_je_gere!

Dans la foulée, j’ai réalisé une petite présentation que j’ai donnée au meeting HZV du samedi 2 avril. Les slides sont en ligne ici. Concernant le script, j’ai essayé de commenter suffisamment le script pour qu’il soit compréhensible. Et de toutes façon, je n’ai rien inventé puisque je me suis intégralement basé sur le papier cité ci-dessus.

En tout cas, on peut dire que ce challenge aura été riche en apprentissage. Je regrette juste que l’accès à Internet n’ait pas pu être donné, ou à défaut, un repository un peu plus fourni d’outils et de documentation… En tout cas bravo à l’équipe d’organisation, et à l’année prochaine !

  1. 4 réponses à “Insomni’hack 2011”

  2. Merci pour ton compte-rendu détaillé qui vient compléter le mien. J’en profite pour féliciter votre équipe qui a réalisé une belle performance tout de même ;)

    Par Bruno Kerouanton le 8 mars 2011

  3. En passant, j’ai publié quelques explications supplémentaires sur le challenge crypto « padding oracle » sur mon blog. En tout cas, il m’a fait bien transpirer !

    Par Pascal junod le 8 mars 2011

  4. Merci pour le write-up et les références disséminées dans l’article. Rien d’autre à dire à part ça. :)

    Par Ge0 le 9 mars 2011

  1. 1 Trackback(s)

  2. 18 mars 2011: Insomni’Hack 2011 | Fortinet Security Blog

Écrire un commentaire