Skip navigation.

Sign up | Lost password? | Help

devloop :: blog

Blog sur la sécurité informatique, la programmation, Linux et le Web

Posts tagged with "python"

Démo de Wapiti

, , ,

Wapiti SVN - scan with terminal colors

L'occasion pour tester à la fois recordMyDesktop et la plateforme d'hébergement de vidéos Vimeo.
Le résultat est largement satisfaisant. Les vidéos ogg sont légères tout en restant de bonne qualité et le réencodage sur Vimeo ne détériore pas trop la vidéo :smile:

Dans cette vidéo vous pouvez voir Wapiti (dans la version actuelle sur le SVN) lancé sur mes scripts de test perso (ne faites donc pas attention au noms de variables...)
Vous pouvez constater que les messages sont en bonne partie français (internationalisation en fr, es, en) :smile: Je pense en avoir terminé avec l'affichage en mode console (couleurs, niveau de bavardage).
En revanche je vais devoir travailler sur la génération des rapports qui pose problème (on ne peut pas insérer de la même façon certains caractères dans un rapport au format txt et un rapport au format html).

Encore et toujours des modifications à apporter p:

Wapiti 1, MySQL 0

, , , ...

Le développement de Wapiti continue doucement mais sûrement. J'ai mis en place le système de module dont j'avais parlé, ça commence à avoir de la gueule :smile:
Dans les dernières révisions sur le SVN j'ai pu réimplémenter l'injection SQL en aveugle et la recherche des XSS permanents qui avaient été virés dans les changements.

Aujourd'hui j'ai joué un peu avec Mutillidae (après quelques minutes à modifier le code car pas assez "strict" pour ma config).
Ca a permis de trouver quelques problèmes stupides avec l'injection en aveugle, notemment l'utilisation du mot clé "AND" qui est bien moins efficace qu'un bon "OR" p: et aussi quelques payloads simples que j'ai rajouté.

Sans ces 2/3 payloads simples, Wapiti injectait la fonction SQL "benchmark(10000000,MD5(1))" alors qu'il aurait pu tout aussi bien le faire avec un "sleep()" (si un caractère supplémentaire n'avait pas été présent).
La différence entre ces deux fonctions c'est que la première prend beaucoup de ressources. On voit les requêtes s'entasser sous MySQL ("show processlist;") et le process passe à plus de 95% d'utilisation du CPU... et finalement MySQL semble baisser les bras : le processus tourne encore mais le serveur ne traite plus les requêtes.

Wapiti vainqueur par K.O. p:

Maintenant je compte vérifier la façon dont est géré la verbosité dans les différents modules puis je vous reparlerais prochainement du système de modules et comment en créer de nouveaux ;-)

Pour obtenir la dernière version du SVN :
svn co https://wapiti.svn.sourceforge.net/svnroot/wapiti wapiti

Installez Python sans droits d'administrateur

, ,

Un outil bien pratique que j'ai cherché à une époque sans jamais trouver et sur lequel je suis finalement tombé : Portable Python

Comme son nom l'indique ça permet de disposer d'un environnement "portable" de Python et ça peut s'installer sans disposer de droits d'administrateur (pratique pour se la péter au taff) :cool:

Python : Chargement dynamique de modules - un système d'extensions

, , ,

Je cherchais une méthode pour mettre en place un vrai système "modulaire" de modules en Python, comme un système de plugins où les extensions seraient chargées dynamiquement sans que les informations concernant ces modules soient "hardcodées" dans le programme.
On pourrait ainsi rajouter directement les modules dans un répertoire sans avoir à les déclarer à un autre endroit dans le code. Bien sûr ça suppose que chaque module soit créé sur un même modèle car il faut tout de même savoir à quoi on souhaite accèder p:

Finalement j'ai trouvé l'astuce suivante qui peut vous intéresser ;-)
Soit l'arborescence suivante :
.
|-- launcher.py
`-- modules
    |-- __init__.py
    |-- abcd.py
    |-- plop.py
    `-- truc.py

Le répertoire modules est le dossier qui contient les extensions. Le fichier __init__.py contient uniquement une ligne qui définit les modules présents dans le dossier (pour se simplifier la tâche) :
__all__ = ["plop", "abcd", "truc"]

Les fichiers abcd.py, plop.py et truc.py contiennent chacun une classe dont le nom correspond au fichier avec un constructeur et une méthode run().
La classe plop a la particularité de contenir une fonction special(). Pour l'exemple, le contenu du fichier plop.py est le suivant :
class plop:
  def __init__(self):
    print "Constructeur de plop"

  def run(self):
    print "run() de plop"

  def special(self):
    print "Fonction special() de plop"

Enfin, le contenu du fichier launcher.py qui a pour rôle de charger dynamiquement ces modules en aveugle est le suivant :
#!/usr/bin/env python
import modules
for mod_name in modules.__all__:
  mod = __import__ ("modules." + mod_name, fromlist=modules.__all__) # on charge le module
  mod_instance = getattr(mod, mod_name)() # on appelle le constructeur
  mod_instance.run()
  if hasattr(mod_instance, "special"):
    mod_instance.special()

Son lancement provoque la sortie suivante :
Constructeur de plop
run() de plop
Fonction special() de plop
Constructeur de abcd
run() de abcd
Constructeur de truc
run() de truc

On pourrait s'affranchir encore plus du code en lisant le contenu du répertoire modules pour obtenir le nom des fichiers (par glob par exemple).
On pourrait aussi mettre dans chaque classe un attribut définissant sa priorité de lancement pour effectuer un système de chargement dans le même style que init.d :smile:

Les navigateurs ont bien du mérite

, , , ...

Ce n'est pas nouveau !
Quand on voit les différents standards (X)HTML, les normes ECMAScript, les histoires d'encodage des caractères, les flux RSS, les images, les animations Flash, le format CSS et que tout ça avance à une vitesse monstre, c'est déjà pas facile.
Mais quand en plus chacun y va de ses "solutions" propriétaires, ses balises à lui, les webmestres qui s'improvisent du code HTML entre deux standards (en supposant que les balises en question soient fermées), l'interprétation du code qui diffère d'un navigateur à un autre et les astuces à ajouter dans les pages pour réparer les erreurs... on s'en sort plus.

On pouvait penser que la base (le protocole HTTP) était, lui, correctement implémenté... mais après avoir travaillé sur une nouvelle implémentation des cookies dans Wapiti, force est de constater qu'on est loin du compte :frown:

Premier sujet d'énervement : les dates utilisées dans les entêtes. Quand un serveur déclare un cookie auprès du client, il utilise l'entête "Set-Cookie" avec un champ nommé "expires".
Ce champ donne la date à laquelle expira le cookie associé. Or cette date n'est pas une date relative mais une date "absolue" dans ce style : "Sat, 27 Jun 2009 16:33:21 CET".
C'est déjà énervant de devoir jongler avec les fuseaux horaires mais en plus il faut jongler avec le format de la date.

Lors de mes tests j'ai trouvé quatres formats de date différents présents dans le champ expires des cookies ou les entêtes HTTP Date et Expires.
Avec ou sans virgule, avec le décalage horaire sous forme numérique ou sous la forme de trois lettres (GMT, CET), avec ou sans tirets, avec le mois placé avant ou après le numéro du jour.
Parfois un même serveur (comme www.amazon.com) utilise deux formats de date différents, juste histoire de compliquer les choses :|

Le plus drôle c'est que si on jette un coup d'oeil aux RFC 2109 et RFC 2965 qui définissent le bon usage des cookies dans le protocole HTTP, on apprend qu'il faut utiliser un champ baptisé "max-age" pour l'expiration des cookies et que sa valeur est... relative au moment présent !! (par exemple max-age=500 définie une durée de vie de 500 secondes)

Seulement presque aucun serveur, CGI et compagnie ne respectent ces spécifications. Au lieu d'utiliser les méthodes revues et corrigées, ils continuent depuis plus de 10 ans de se baser sur les anciennes specs de Netscape.
La raison serait principalement le non respect de ces specs par le navigateur de Microsoft et il semble que ce dernier persiste même arrivé à la version 7 :frown:

Malgré l'existence d'un entête "Set-Cookie2" compatible avec l'ancienne méthode de Netscape, les navigateurs ne semblent pas bien pressés de suivre les bonnes règles. Il n'y a qu'Opera qui fait figure de bon élève dans cette histoire :frown:

La librairie libcookie de Python montre aussi des défaillances. Pour le moment j'ai pu réécrire le script cookie.py, en analysant directement les entêtes et abandonnant le format LWP/Netscape pour un format XML au niveau du stockage :smile: Vu comment les choses se présentent ça va se terminer sous la forme d'une librairie.
Les modifications ne sont pas encore visibles dans le SVN.

Hors sujet :
Qu'est-ce qui est arrivé en premier ? L'extravagance de Michael Jackson ou sa dépendance aux médicaments et autres substances ?
La réponse en vidéo. How !

Stéganographie et Unicode (UTF-8)

, , , ...

Mise en bouche

Jetez un oeil à ce texte, extrait d'une page Wikipedia :

The frequency of letters in text has often been studied for use in cryptography, and frequency analysis in particular. No exact letter frequency distribution underlies a given language, since all writers write slightly differently. Linotype machines sorted the letters' frequencies as etaoin shrdlu cmfwyp vbgkqj xz based on the experience and custom of manual compositors.

Et maintenant jetez un autre oeil au texte suivant :

Thе frequеncy of lettеrѕ in tеxt Һas oftеn beеn studіed for uѕe in сryptogrарhу, and freqυеncy analуѕis in pаrticular. Νо eхасt lettеr frequеncy dіstribution underlies a given language, since all writers write slightly differently. Linotype machines sorted the letters' frequencies as etaoin shrdlu cmfwyp vbgkqj xz based on the experience and custom of manual compositors.

Vous semblent-ils différents ? Peut-être avez vous remarqué une particularité dans l'un des texte... car l'un d'entre eux contient un message secret :sherlock:
Jeter un coup d'oeil au code HTML de cette page vous donnera certainement un indice, pourtant j'aurais très bien pu vous montrer deux textes bruts (plain text) et vous n'aurez pas forcément remarqué plus de particularités.

L'unicode : qu'est-ce que c'est et pourquoi on nous emmerde avec

L'unicode est une norme de codage des caractères destinée à être utilisée massivement pour rassembler et remplacer les normes existantes de différents pays.
On pourrait représenter l'unicode comme une énorme table de corresponde entre les caractères et leurs valeurs informatique comme on peut en trouver pour l'ASCII.
Pendant longtemps, chacun y allait de sa norme de codage des caractères : ASCII et ISO 8859-1 pour les occidentaux, le Big5 pour les chinois, le KOI8-U pour les russes, le Shift-JIS ou encore le ISO 2022 pour les japonais.
On peut imaginer que cette diversité de normes n'arrange pas les développeurs qui souhaitent rendre leurs logiciels accessibles au plus de personnes possibles, quelque soit leur langue.

Unicode se "décline" en plusieurs formats : UTF-8, UTF-16 et UTF-32.
Il y a aussi l'UTF-7 utilisé par des protocoles d'envoi de courrier et on parle de UTF-5 ou d'UTF-6, chacun ayant pour spécificité d'être compatible avec "alphabet" prédéfini comme ceux (limités) utilisés pour composer une adresse email ou un nom de domaine.
Le nombre situé derrière les caractères UTF correspond au nombre de bits minimum nécessaire pour l'encodage d'un caractère.

Le format dont cet article parlera sera l'UTF-8 qui est principalement utilisé par nous autre européens ou américains.
Ce format là a en effet le grand avantage d'être rétro-compatible avec l'ASCII, c'est à dire que la plupart des caractères que l'on utilise sont encodés sur un octet, exactement comme ils l'étaient auparavant :up:
Ce "miracle" tient sur le fait que l'UTF-8 utilise les premiers bits de chaque octet pour déterminer s'il a affaire à nos bons vieux caractères où s'il doit chercher dans des tables plus exotiques.
Ainsi notre petit "a" sera codé 0x61 en hexa comme c'était le cas en ASCII mais le "à" avec accent tiendra sur deux caractères et se codera 0xC3A0.
La page Wikipedia sur l'UTF-8 montre quels bits sont utilisés sur chaque octet.

L'UTF-8 est donc, comme d'autres formats d'unicode, extensible. Comme dit sur cette page, on pourrait qualifier le format UTF-8 de raciste : quand nos caractères sont encodés sur un ou deux octets, les thaïlandais ou les koréens ont droit à 3 octets par caractère ! nervous
Pas de quoi les motiver à utiliser UTF-8, heureusement l'UTF-16 et l'UTF-32 mettent tout le monde à un niveau d'égalité (toutefois l'UTF-32 prend comme son nom l'indique 4 octets par caractère :frown: )

UTF-8 : représentation, encodage et décodage en Python...

Quand on veut nommer un caractère en unicode, on utilise généralement la forme "U+XXXX""XXXX" est un nombre hexadécimal dont la taille peut varier.
Pour "fouiller" dans l'unicode, il y a trois sites quasi-indispensables :
decodeunicode.org : une mine d'or à l'interface soignée.
FileFormat.Info : très pratique, le sites offrent aussi des ressources en dehors d'unicode
Unicode.org : le site officiel avec les tables de caratères au format PDF.
Unimap (unipad.org) : assez simple mais efficace.

Si vous programmez, vous vous êtes peut-être déjà arraché les cheveux devant des problèmes de mauvais encodages. Mon expérience m'a montré que le mieux est encore d'aller à la source et de corriger directement le mauvais caractère (par exemple dans une page html) au lieu de tenter de le convertir/corriger.
Je vous donne tout de même quelques commandes pratiques en Python pour jouer avec les caractères unicode :smile:

Définissons un caractère s dont la valeur est "é" et observons son type :
>>> s='é'
>>> type(s), repr(s)
(<type 'str'>, "'\\xc3\\xa9'")

s est un caractère "brut" : c'est un type 'str' et non unicode. On voit toutefois qu'il est codé sur deux octets, il est donc bien au format UTF-8 mais n'offre pas les avantages de la classe unicode.
Transformons ce caratère au type unicode :
>>> u=unicode(s,"UTF-8")
>>> type(u), repr(u)
(<type 'unicode'>, "u'\\xe9'")

Le caractère unicode auquel on a à faire est donc le U+00E9.
Renseignons nous sur ce caractère :
>>> import unicodedata
>>> unicodedata.name(u)
'LATIN SMALL LETTER E WITH ACUTE'
>>> unicodedata.lookup('LATIN SMALL LETTER E WITH ACUTE')
u'\xe9'

Le type python unicode ne permet pas de tout faire, on pourrait le qualifier de "virtuel". On est obligé de le mettre en "dur" (l'encoder) pour effectuer certaines opérations comme écrire dans un fichier.
>>> u.encode("UTF-8")
'\xc3\xa9'

D'autres commandes pratiques :
>>> ord(u)
233
>>> unichr(233)
u'\xe9'
>>> u.encode('ascii', 'xmlcharrefreplace')
'é'

UTF-8 et stéganographie

L'idée d'utiliser l'unicode pour dissimuler des données m'est venue en me demandant s'il y avait pas plusieurs façons d'encoder le même caractère :idea: Après tout, chaque format (ASCII, UTF-8, UTF-16...) offre différents encodages pour un même caractère alors pourquoi pas le même caractère dans un même format ? Avec deux encodages possibles on peut donc glisser un bit (valeur 0 pour un encodage, valeur 1 pour le second).
Mes recherches sur Internet m'ont montré que non seulement je n'étais pas le seul à y avoir pensé mais en plus certains l'avaient déjà implémenté. Je n'invente donc rien mais je vous fournis les techniques utilisées.

La première technique est proposée par MockingEye et son implémentation unisteg.py.
Elle se base sur les diacritiques. Typiquement ce sont les accents et cédilles qui peuvent être attachés à un caractère.

Si vous vous rendez sur le bloc des diacritiques sur decodeunicode.org, vous verrez tout de suite de quoi je veux parler :smile:
Unicode permet donc d'encoder de deux façons différentes nos lettres à accent. Soit sous leur forme fixe (le "é") soit sous une forme combinée (la lettre "e" combinée avec le diacritique de l'accent aigue).
Le passage de la forme composée à la forme décomposée (et vice-versa) peut se faire par la fonction normalize de la librairie unicodedata en python :
>>> unicodedata.normalize("NFD",u)
u'e\u0301'
>>> unicodedata.normalize("NFC",u'e\u0301')
u'\xe9'
>>> unicodedata.normalize("NFD",u).encode("UTF-8")
'e\xcc\x81'

On remarque que le diacritique ne précéde pas le caractère mais le suit. Pour faire simple :
e combining diacritical

La seconde méthode est proposée par Antonio Alcorn. Elle est implémentée en PHP mais le code source n'est pas disponible. Toutefois en analysant le résultat on se rend compte que la technique se base sur l'utilisation de caractères différents mais visuellement très proche.
Par exemple, notre "e" est très proche du petit IE cyrillique.
Les caractères cyrilliques sont plus bruts que nos caractères et sont généralement représentés sans serif (voir empattement). En fonction de la police utilisée pour afficher les caractères, on ne verra donc pas la différence. On se servira pour ce faire d'une police sans-serif comme Arial.
Dans le second texte au début de cet article, vous avez peut-être remarqué que le "h" de "has" était plus large. C'est tout simplement parce qu'il s'agit du caractère cyrillique SHHA (U+04BA).

Ayant créé moi même mes outils de stéganographie UTF-8 (voir çi-dessous), la recherche des caractères similaires m'a pris un bon moment. J'ai rassemblé ça dans ce fichier.
Les caractères où la différence n'est pas visible (avec la bonne police) sont placés directement. Ceux où l'on peut se laisser prendre sont entre parenthèses avec le signe + accolé. Ceux entre parenthèses ressemblent d'assez loin.

utf8hide et utf8reveal

utf8hide.py permet comme son nom l'indique de cacher des données dans un texte.
Il demande deux arguments : le fichier dont il faut cacher le contenu et un fichier texte au format ISO-8859-1 au ASCII. Un troisème argument ("html") peut être passé si on souhaite ensuite injecter le résultat dans une page web.
J'ai repris l'idée de l'implémentation PHP qui propose d'utiliser seulement 5 bits pour coder un caractère à cacher en la poussant plus loin et sans les limitations.
Le programme fait une analyse du fichier à dissimuler et détermine le plus petit nombre de bits nécessaire pour coder un octet. Si le message secret est court, il défini un charset (alphabet) lui permettant ainsi de "comprimer" un octet sur seulement quelques bits (3, 4, 5, 6). Au-delà, le charset serait trop gros et le programme préfère utiliser les codes habituels des caractères.

Le résultat obtenu est placé dans le fichier out.txt. L'affichage donne le nombre de bits pour un octet (nbit), le charset utilisé et la taille du fichier secret. Il faut conserver ces paramêtres pour l'opération inverse.

Un peu à l'instar de ThumbStego qui nécessitait une image et sa signature, utf8reveal.py a besoin du texte ascii/iso8859 qui a servi à dissimuler le fichier et la version UTF-8 dans laquelle sont cachées les données.
On lui passe aussi en argument les variables vues plus tôt et le programme recréé le fichier secret dans "secret.xxx".

Les deux programmes affichent les bits à l'écran (0 ou 1) ce qui peut-être ennuyeux pour les gros fichiers... mais une telle utilisation est à éviter.
En effet, le programme a beau compresser comme il peut les données en entrées et utiliser la méthode des diacritiques ainsi que celle des ressemblances entre caractères, tous les caractères ne sont malheureusement pas exploitables. Le programme passe alors aux caractères suivant jusqu'à tomber sur un caractère exploitable. Le ratio est donc bien plus faible que 1/8ème mais c'est suffisant pour y passer quelques mots :smile:

Arghhh

J'ai eu la peur de ma vie (c'est quelque peu exagéré) en développant le code. En voulant recopier mes scripts vers un répertoire de sauvegarde, j'ai bêtement tapé "rm -r" au lieu de "cp", supprimant ainsi les codes et le répertoire de sauvegarde :cry:
A défaut de faire n'importe quoi, je me suis félicité d'avoir quelques connaissances en inforensique, ce qui m'a permis d'extraire le code de la partition :D Pourtant comme le code fait plus de 4096 octets, il était sur deux blocs mémoire et sur du ext3... merci à grep, dd et hexdump :rolleyes:

Afficher la "kill" liste de votre système Linux

, , ,

Pour revenir au billet précédent et aux fichiers /proc/[pid]/oom_score et /proc/[pid]/oom_adj...

La page de manuel de proc(5) nous dis informe de différents points intéressants :
  • oom_adj contient une valeur numérique située entre -17 et 15. Plus la valeur est grande (et positive) plus le processus a des chances d'être candidat à un "suicide" décidé par le kernel.
  • On peut jouer sur cette valeur, par exemple en faisant un echo -5 > /proc/[pid]/oom_adj
  • oom_score contient une valeur numérique bien plus grande qui est calculée à partir de différents éléments comme l'utilisation CPU du processus, sa priorité, s'il tourne avec des privilèges etc. Le résultat est ensuite décalée à l'aide de la valeur oom_adj (bit shift). Cette opération est définie par la fonction badness
  • la valeur -17 dans oom_adj est particulière et permet de marquer le processus comme indestructible
  • Tout ce mécanisme peut être activé ou désactivé par /proc/sys/vm/panic_on_oom. Si ce fichier contient "1" alors le système fait un kernel panic en cas de débordement de mémoire. S'il est à 0, oom-killer est appelé à la rescousse.


Pour afficher la liste des processus par ordre croissant de risque de se faire tuer, j'ai créé un script python : oom_score.py

Il donne un résultat de ce style :
oom_score pid   process name                   oom_adj                              
        0  3593 /sbin/auditd -s disable            -17                              
        0   665 /sbin/udevd --daemon               -17                              
       13  2610 /sbin/acpid                          0                              
       15  3622 /usr/sbin/avahi-dnsconfd -D          0
...
    34560  5187 /usr/bin/krunner                     0
    83495  5108 kdeinit4: kdeinit4 Running...        0
    90882     1 init [5]                             0
   103294  5614 /bin/sh /usr/bin/firefox             0

Gare à toi Firefox !! p: Par contre qu'init soit en seconde position, ça fait plus peur :D

Quelqu'un a fait un script bash ici mais il donne moins d'informations (uniquement oom_score et nom du process)

Python Opera Cache Dumper

, , , ...

Après le cookie dumper, voici le "cache dumper" qui comme son nom l'indique permet d'analyser le cache du navigateur Opera.

Sous Opera, on trouve deux dossiers de cache.
L'un s'appelle "opcache" et sert uniquement aux sites liés à Opera lui-même comme les requêtes à destination du "sitecheck" (système anti-fishing du navigateur), les requêtes vers DragonFly (API pour développeurs web intégré au navigateur) et requêtes vers les autres sous-domaines d'Opera.
Le second dossier est le dossier "cache4" et conserve la grande majorité des fichiers gardés en mémoire par Opera.

Dans les deux cas, les fichiers mis en cache sont renommés sous la forme oprXXXXX (où "XXXXX" est composé de chiffres et lettres majuscules) et un fichier nommé "dcache4.url" présent dans le même dossier est un index qui garde les informations spécifiques à chacun des fichiers.

Il est important de noter que si vous avez coché "Empty on exit" dans les préférences, Opera n'utilisera pas le fichier d'index mais stockera tout de même des fichiers en cache. Les associations entre les fichiers présents dans le cache et les sites Internet correspondants sont alors impossible à déterminer (Opera garde ces associations en mémoire mais ne les écrit pas sur le disque).

Comme pour l'analyse du fichier cookies4.dat la dernière fois, j'ai rencontré certains tags non documentés dans la documentation officielle.
On peut par exemple trouver le tag 0x00 dont la valeur est toujours la même (0x8000000B) et qui ne semble pas avoir de signification :confused:
Il y a un tag 0x2e qui représente la langue utilisée pour un fichier de cache donné (par exemple "fr_FR").
Tout aussi énigmatique, le tag 0x3a qui me semble lié à l'entête HTTP "Age" reçu comme réponse des serveurs. Un entête qui m'était inconnu et qui m'a fait penser au ver Conficker qui se base sur un autre entête HTTP pour déterminer la date et l'heure actuelle. Le header "Age" semble renvoyer une valeur numérique dans une intervalle prédéfinie et augmente avec le temps... ça pourrait être pratique comme moyen de trouver un nombre aléatoire :idea:

Enfin on trouve des tags qui permettent au navigateur d'intégrer directement des entêtes HTTP et leurs valeurs dans l'index de cache. Le tag racine est le 0x3b, suivi par le 0x3c. Le 0x3d défini le nom de l'entête et le tag 0x3e sa valeur.

Le programme que j'ai fait prend comme argument le chemin vers le fichier dcache4.url ainsi qu'un dossier où sera placé l'analyse du cache. Si ce dernier dossier n'existe pas, il est créé automatiquement.
Pour chaque fichier référencé dans le cache, le programme détermine sa nature à partir de sa valeur "mime" (image/png, text/html, application/x-javascript, etc) et s'il est compressé ("Content-Encoding" défini par le tag 0x20). Si c'est le cas, le fichier est décompressé (si possible car certains fichiers sont compressés en deflate et non par gzip :worried: ) avant d'être copiée dans le dossier de destination.
Le programme génère ensuite un fichier HTML qui permet d'afficher facilement l'ensemble des fichiers du cache et leurs métadonnées associées. S'il s'agit d'une image, elle est affichée. Sinon un lien permet d'accèder à la ressource. Les javascripts et codes html ont l'extension txt pour éviter qu'ils soient interprétés.

Comme les descriptions et les images sont chargées dans la même page HTML, ça peut prendre du temps à charger en fonction du cache analysé (il faut mieux un système avec une bonne mémoire p: )
Le résultat ressemble à ça (extrait) :

Le code est ici : opcadump.py penguin

Python Opera Cookie Dumper

, , , ...

Je me suis intéressé au format de fichier utilisé par le navigateur Opera.
Bien que les spécifications soient disponibles, on trouve peu d'outils pour gérer les historiques, caches et cookies du navigateur :worried:
Finalement je suis parvenu à écrire un script Python qui extrait les données du fichier cookies4.dat et les affiche dans un format texte plus conpréhensible que le format binaire d'Opera.
La sortie générée ressemble à ça :
Opera Cookie Dumper
File version: 1.0
Application version: 2.1
Cookie file
Size of idtags: 1 bytes
Size of payload length fields: 2 bytes
MSB: 0x80
===================================
dsource.org
        dsource-auth_data  = a%3A11%3A%22autologinid%22%3Bs%3A6%3A%22userid%22%3Bi%3A-1%7D
        path = /projects
        path = /dsss
        trac_session  = e7f77d380bc7290b2347490c
        path = /tutorials
        trac_session  = 49ca56cec7f142d58d2201ea
userfriendly.org
        __qca  = 1239814048-45958653-24014683
...

Toutes les informations n'apparaissent pas. Actuellement il n'est affiché que le domaine, le path sur le serveur et les noms et valeurs des cookies.

Durant mes recherches j'ai découvert un tag non documenté (tag id 0x26) qui correspond en réalité au champ "Delete new cookies when exiting Opera is checked" dans les préférences de site. Si la valeur est à 2 cela signifie que la suppression est activée. Si la valeur est à 1 ou si le tag est omis cela signifie que la suppression n'a pas lieu.
Il y a aussi un flag 0x27|MSB que j'ai rencontré mais je n'ai pas pû découvrir sa signification :confused:

Dans tous les cas ça peut être utile pour faire du Web Browser Forensics et le code est disponible ici :
opcodump.py penguin

Nouvelle version de Wapiti

, , , ...

Comme promis précédemment, une nouvelle version stable de Wapiti est disponible au téléchargements dans différents formats d'archivage (toujours l'appréhension de merder en les faisant alors j'ai vérifié à deux fois p: )

Peu de changements depuis le dernier post sur le sujet et sa version SVN correspondante mais ça vaut quand même le coup de se mettre à jour puisque j'ai rajouté quelques "patterns" pour la détection de failles PHP et modifié les utilitaires cookie.py et getcookie.py pour qu'ils fonctionnent sur les applications web qui s'entêteraient à gérer les entête HTTP de façon peut conventionnelle.

D'ailleurs je devrais remercier au passage les auteurs des applis my little forum (juste un petit XSS) et iDB (une petite inclusion locale à l'exploitation limitée) sur lesquels j'effectuais mes tests en plus des petits scripts que j'ai fait à l'occasion :smile:

Maintenant il va falloir que je mette le site web à jour :whistle:

Télécharger Wapiti sur SourceForge.net