Nicolas SURRIBAS

Développement / Réseau / Sécurité Informatique

programmation

ClipTray (C#)

Rédigé par devloop - -

Pour joindre l'utile au pédagogique, j'ai écrit mon premier "vrai" programme en CSharp à l'aide de l'IDE #develop.

J'avais besoin d'un programme me permettant de mettre rapidement des valeurs textes dans le presse-papier (clipboard) pour pouvoir les recopier sur une interface qui ne permettait malheureusement la pré-saisie des champs ni leur mémorisation.

Au niveau de l'interface homme-machine, j'ai retenu le choix d'une liste accessible depuis le systray (zone de notification). Ca s'est avéré beaucoup plus simple que je pensait avec #develop étant donné qu'il proposait un template pour les applications iconifiés et qu'il s'uffisait de remplir le reste.
De même la fonction pour insérer des données dans le clipboard était simple à utiliser.

Dernier trucs à régler, je voulais que le gestionnaire de l'événement pour les différents choix de la liste (copiant chacun une valeur différente dans le clipboard) soit le même : ce n'est pas très propre de réécrire la même fonction pour chaque entrée de la liste.

Ca m'a pris plus de temps que je pensais de trouver comment faire, finalement j'ai utilisé un cast sur l'objet envoyant l'événement pour déterminer son identité et retourner dans le clipboard la valeur correspondante.

Pour ceux que ça intéresse, le code est sur pastebin.com.
#develop génère lui-même un fichier de ressource pour spécifier l'icone à placer dans le systray. Mais avec le compilateur csc.exe de Microsoft une option /win32icon permet de spécifier le fichier icone (donc ça devrait marcher).

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

Rédigé par devloop - -

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.

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.

Python Opera Cache Dumper

Rédigé par devloop - -

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.
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.

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) 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)
Le résultat ressemble à ça (extrait) :

Le code est ici : opcadump.py.

Python Opera Cookie Dumper

Rédigé par devloop - -

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.
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.

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

Pseudo-terminaux, portes dérobées, telnet et tunneling

Rédigé par devloop - -

J'ai toujours trouvé aux réseaux un petit côté "magique", l'idée de pouvoir éditer un fichier qui se trouve à des milliers de kilomêtres de chez moi ma toujours parue fabuleuse. C'est peut-être pour cela que j'ai toujours admiré les TTYs (ouah c'est beau un terminal, surtout avec du ncurse !) et ils sont restés longtemps pour moi un grand mystère, au moins jusqu'à ces derniers jours, moment fatidique où j'ai décidé de me lancer dans l'épineuse aventure des TTY/PTY.

Un terminal, kezako ?
Sous Linux, un terminal est avant tout un périphérique. On en trouve dans /dev (pty*, tty*) et dans /dev/pts (terminaux esclaves).
Le terminal se charge de faire le lien entre le clavier et le programme qui recevra les commandes. Il transforme la frappe de certaines touches du clavier en un "code d'échappement" que le programme saura comment interpréter.
Ces codes d'échappements sont définis dans des standards comme le VT100.

L'utilité d'un terminal par rappel à une ligne de commande "brute" est bien évidemment de pouvoir exécuter des programmes interactifs comme Vim, Emacs, top, nethack et bien d'autres... c'est pour dire à quel point ils sont devenus indispensables ;-)
Même si la plupart des backdoors existantes n'offrent pas le support des ttys, quelques-unes le proposent avec plus ou moins de réussite.

Canonique or not canonique ?
Dans la plupart des cas, elles sont inutilisables telles quelles car il n'y a pas de client associé. L'utilisation de netcat pour s'y connecter ne permet pas de profiter du terminal côté serveur car netcat ne gère pas le tty. Notamment netcat lit les données tappées de façon canonique (attente d'une fin de ligne pour traiter les données) alors qu'un terminal les lit de façon non-canonique : les données sont traitées à chaque frappe du clavier.
Le mode non-canonique est indispensable pour les programmes cités plus tôt (par exemple pouvoir taper simplement q pour quitter top sans avoir à appuyer sur entrée).

Toi comprendre ce que moi dire ?
Vient alors un autre problème : le terminal client et le terminal serveur doivent parler la même langue. Une bonne partie des lnormes existantes sont compatibles car basées sur les codes d'échappement ANSI mais ce n'est pas toujours le cas.
Le standard utilisé est habituellement défini par la variable d'environnement TERM sous le shell.

Deux solutions sont possibles pour faire dialoguer correctement le client et le serveur :
  • Utiliser un client et un serveur utilisant le même langage
  • Utiliser un protocole permettant au client et au serveur de se mettre d'accord sur le type de terminal à utiliser

Implémentations
La première solution est celle utilisée dans sorshell.c. Le client et le serveur se voient fixer la variable d'environnement TERM à "vt100". Le client est configuré en mode non-canonique est sans échos (voir plus loin).

L'autre solution a fait son apparition il y a maintenant longtemps avec le protocole telnet. Par ce protocole, le client et le serveur négocient le type de terminal ainsi que d'autres règles de transmission.
L'implémentation de telnet dans un logiciel ne facilitant pas les choses, certains ont préférés ruser comme pour Tiny Shell qui se contente de l'envoi du type de terminal utilisé.
Il existe tout de même un code en perl où toute la couche telnet est utilisée.
Terminal client
Un dehors du mode canonique (ou non), le client ne doit pas interpréter certains signaux comme un Ctrl+C afin de les traduire pour les transmettre au serveur.
Les deux parties doivent aussi se mettre d'accord sur qui renvoie les données. Quand on frappe sur une touche depuis le client, on s'attend à ce qu'elle apparaisse à l'écran. Soit elle s'affiche au moment où on appuie sur la touche, soit quand le serveur nous la renvoit (il a fait un "écho"), soit les deux en même temps (dans ce cas là, le caractère s'affiche en double).
Le serveur renvoyant généralement plus de données que le client n'en envoit, le client est habituellement passé en non-écho et les caractères s'affichent alors lorsque le serveur les renvoit au client (le client ne renvoie pas les données du serveur).
Dans une session telnet, la taille de la fenêtre de terminal sera aussi négociée (généralement 80x25 par défaut).

Le rôle du terminal client consiste à lire les touches tapées sur l'entrée standard pour les restransmettre vers le serveur, le tout avec les caractéristiques citées précédemment.
Sa programmation est simple. Il faut d'abord récupérer une structure termios définissant le modèle du terminal actuel à l'aide de la fonction tcgetattr(), modifier cette structure selon nos souhaits pour enfin mettre à jour le terminal avec les nouveaux paramêtres (fonction tcsetattr()).

Le code utilisé en C dans sorshell est le suivant :
struct termios deftt,tt;

/* terminal init */
tcgetattr(0, &deftt); /* récupére les préférences du terminal */
tt = deftt; /* copie */
tt.c_oflag &= ~(OPOST);
tt.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); /* pas d'écho, ne recoit pas les signaux, mode non-canonique... */
tt.c_iflag &= ~(ICRNL); /* transforme les CR en NL */
tt.c_cc[VMIN] = 1; /* lit caractère après caractère */
tt.c_cc[VTIME] = 0; /* pas de délai de lecture */
tcsetattr(0, TCSADRAIN, &tt); /* mettre à jour le terminal */
...
tcsetattr(0, TCSADRAIN, &deftt); /* restauration */

stty
Sous Linux, la commande stty permet de modifier les paramêtres d'un terminal. On peut alors utiliser un client "brut" en modifiant le terminal courant, ce qui donnerait un script dans ce style :
stty -icanon -echo -isig
netcat serveur 9999 -v
stty icanon echo isig
clear

Un shell avec terminal pour pas un rond
Petite astuce trouvée sur pentestmonkey : on peut utiliser la richesse des API Python pour associer facilement un shell avec un pseudo-terminal.
La commande pour lancer un serveur fonctionnant avec le script client précédent sera la suivante :
netcat -e "python -c 'import pty; pty.spawn(\"/bin/sh\")'" -v -l -p 9999

Terminal serveur
Du côté du serveur, le terminal doit fonctionner en sens inverse : lire les codes d'échappement reçus et les transformer en signaux, déplacement de curseur etc.
La programmation du serveur est plus compliquée et se base sur l'utilisation d'un pseudo-terminal dont le fonctionnement est proche des pipe.
On a alors un pseudo-terminal (pty) maître sur lequel on écrit les données reçues par le réseau et un pty esclave (pts) qui reçoit les données et les filtre avant de les renvoyer au shell.

La programmation suivra la procédure suivante :
  1. Ouverture du périphérique /dev/ptmx qui va chercher un pty maître disponible et retourner son descripteur de fichier
  2. Appel à la fonction grantpt() pour modifier les droits d'accès au terminal esclave (le pts)
  3. Utilisation de la fonction unlockpt() pour dévérouiller le pts
  4. ptsname() permettra ensuite de récupérer le nom du pts
  5. Ouverture du pseudo-terminal esclave

Pour terminer, le programme fait un fork(), le processus fils redirige ses entrées/sorties vers le pts avant de lancer un shell et le processus père gère les entrées réseau.
C'est ce que fait sorshell.c (mis à part qu'il le fait en bas niveau).

Tunneling à travers Jabber (GTalk)
En Python, le nombre d'étapes a été fortement réduites et côté serveur, il suffit d'appeller les fonctions openpty() et fork() du module pty.
Pour illustrer tout cela j'ai développé un petit programme permettant de faire passer un shell/tty à travers une session XMPP. Le code se base sur l'utilisation des serveurs GoogleTalk mais peu être facilement modifié. Il utilise la librairie xmpppy
Le protocole XMPP ne permettant pas de faire facilement transiter des données brutes, celles-ci sont envoyées sous leur forme hexadécimale (encodage pour le caractère 'A' sera '61'). Quelques temporisations ont dû être rajoutées car le serveur GTalk n'hésite pas à couper la communication si les envois successifs sont trop rapprochés.

Ca fonctionne plutôt bien, si ce n'est que c'est assez lent. J'ai réussi à éditer avec VIM, naviguer dans une page de manuel etc. Par contre il faut faire attention avec les commandes qui envoient continuellement des données à l'écran (c'est le cas de top) qui risquent de faire bloquer le programme dans une boucle (avec un top -n <nombre d'affichages> ça passe).

Télécharger jabbertunnel.tar.bz2

Références :
www.iakovlev.org
sorshell.c
emse.fr : Saisies de données au clavier
win.py (Terminal Emulator)
Python Library Reference : termios
Python Library Reference : pty