Nicolas SURRIBAS

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

réseau

BittOratio v3 : Boostez vos statistiques BitTorrent

Rédigé par devloop - -

Mise à jour du 13 septembre 2015 :
La version 3 de BittOratio est disponible.
Elle supporte désormais les réponses HTTP en mode chunked qui l’empêchait de recevoir correctement les réponses de certains trackers.
Téléchargez cette nouvelle version ici : bittoratio3.py

Mise à jour du 25 avril 2014 :
Une nouvelle version de BittOratio est disponible.
Elle corrige des problèmes d'affichage et surtout un bug dans le renvoi des headers HTTP qui provoquait la transmission de réponses HTTP invalides vers le client BitTorrent.
BittOratio devrait donc maintenant fonctionner avec tous les trackers.
Nouvelle version à télécharger ici : bittoratio2.py

Article original (11 janvier 2011) :
Dans la même optique que pour le code statsliar qui permet de booster les statistiques d'un site en envoyant des requêtes multiples à travers des proxys, je me suis mis à écrire un programme capable de booster ses statistiques d'upload sur les communautés BitTorrent qui s'appuient sur le principe du ratio.

Le principe de ces sites est simple : on peut continuer à télécharger tant que l'on veut du moment que l'on partage dans une certaine proportion. Le ratio est une variable dont la régle est "quantité de données downloadées" divisée par "quantité de données uploadées".
Le ratio exigé est lié au réglement du site, son minimum autorisé est généralement de 0.5.

C'est plus l'envie que le besoin qui m'a amené à développer cet outil. C'était l'occasion de reprendre les spécifications BitTorrent étudiées pour mon article Utilisations alternatives du protocole BitTorrent et de voir ce qu'il était possible de faire.

La première partie du document est la suite de la traduction de la spécification. La seconde partie explique le fonctionnement de l'outil.
Je vous invite à vous pencher d'abord sur l'article cité précédemment et à consulter la partie concernant les requêtes envoyées par le client sur le tracker sans quoi la compréhension peut s'avérer difficile.

Première partie : Réponse émises par le tracker

Le tracker répond avec un document "text/plain" correspondant à un dictionnaire bencodé contenant les clés suivantes :
  • failure reason : Si présent, alors il est possible qu'aucune autre clé ne soit présente. Sa valeur est un message d'erreur expliquant pourquoi la requête a échouée (chaîne).
  • warning message : (nouveau, optionnel) Similaire à failure reason, mais la réponse est toujours traitée normalement. L'avertissement est affiché au même titre qu'une erreur.
  • interval : Intervalle en secondes que le client devrait attendre entre l'envoi de requêtes régulières au tracker.
  • min interval : (optionnel) Intervalle minimum d'annonce. Si présent, les clients ne doivent pas annoncer plus fréquemment qu'à cet intervalle.
  • tracker id : Une chaîne que le client devrait renvoyer sur chaque prochaine annonce. S'il est absent et si une précédente annonce a envoyé un tracker id, ne pas rejeter cette ancienne valeur mais la conserver.
  • complete : nombre de peers qui disposent du fichier entier, c'est à dire les "seeders" (entier).
  • incomplete : nombre de peers qui ne seedent pas, c'est à dire les "leechers" (entier).
  • peers : (modèle dictionnaire) La valeur correspondante est une liste de dictionnaires, chacun avec les clés suivantes :
    • peer id : Un identifiant que le peer a choisi lui-même, comme décrit plus tôt pour la requête au tracker (chaîne).
    • ip : l'adresse ip du peer, soit en Ipv6 (forme hexadécimale), soit en ipv4 (séparation par des points) ou un nom DNS (chaîne).
    • port : le numéro de port du peer( entier).
  • peers : (modèle binaire) Au lieu d'utiliser le modèle de dictionnaire décrit précédemment, la valeur peut être une chaîne constituée de multiples de 6 octets. Les 4 premiers octets correspondent à l'adresse IP et les deux suivants au numéro de port. Le tout en notation réseau (big endian)

Comme mentionné plus tôt, la liste des peers est de 50 peers par défaut. S'il y a moins de peers sur le torrent, alors la liste sera plus petite. Dans le cas contraire, le tracker renvoit une liste de peers choisis aléatoirement.
Le tracker a le droit d'implémenter un méchanisme plus intelligent de sélection des peers à retourner comme réponse. Il pourrait par exemple éviter de retourner la liste des seerders à un seeder.

Les clients peuvent envoyer une requête plus tôt qu'au boût de l'intervalle spécifié si un événement se produit (par exemple stopped ou completed) ou si le client a besoin de découvrir plus de peers. Toutefois il est mal vu de "marteler" un tracker pour obtenir d'avantage de peers. Si un client souhaite une plus large liste de peers dans la réponse, alors il devrait spécifier le paramêtre numwant.

Note pour l'implémentation : Même 30 peers est suffisant, la version 3 du client officiel n'établit en réalité de nouvelles connexions que s'il y a moins de 30 peers et va refuser les connexions s'il y en a 55. Ces valeurs sont importantes pour des raisons de performance. Quand un morceau (piece) a terminé de télécharger, des messages HAVE (obtenu) devront être envoyés aux peers les plus actifs. La conséquence est que l'utilisation de la bande passante augmente proportionnellement au nombre de peers. Au delà de 25, les nouveaux peers n'auront que très peu d'influence sur l'augmentation de la vitesse de téléchargement. Les créateurs d'interfaces graphiques sont fortement conseillés de dissimuler cela et d'empêcher de telles configurations qui de toute façon se montrerait très rarement utiles.

Convention "scrape" des trackers

Par convention la plupart des trackers supportent un autre type de requête, permettant de connaître l'état d'un torrent donné(ou de tous les torrents) que le tracker gère. On y fait référence en tant que "page de scrape" car elle automatise le traitement laborieux d'extraction des statistiques du tracker.

L'URL de scrape se base aussi sur la méthode GET, similaire à ce qui a déjà été décris. Cependant l'URL de base est différente. La génération de cette URL de scrape se fait selon les étapes suivantes : Commencer avec l'url d'annonce. Trouver le dernier '/' dans cette chaîne. Si le texte suivant immédiatement ce '/' n'est pas 'announce' alors on considère que le tracker ne supporte pas la convention de scrape. Si c'est le cas, on substitue 'scrape' par 'announce' pour obtenir l'URL de scrape.

Exemples : (URL d'annonce ? URL de scrape)
  ~http://example.com/announce          ? ~http://example.com/scrape
  ~http://example.com/x/announce        ? ~http://example.com/x/scrape
  ~http://example.com/announce.php      ? ~http://example.com/scrape.php
  ~http://example.com/a                 ? (scrape non supporté)
  ~http://example.com/announce?x2%0644 ? ~http://example.com/scrape?x2%0644
  ~http://example.com/announce?x=2/4    ? (scrape non supporté)
  ~http://example.com/x%064announce     ? (scrape non supporté)

L'URL peut être complétée par le paramètre optionnel info_hash, une variable de 20 octets décris plus tôt. Cela restreint le rapport du tracker à un torrent particulier. Dans le cas contraire les statistiques de tous les torrents gérés par le tracker sont retournés. Les développeurs de logiciels sont fortement encouragés à utiliser le paramètre info_hash dès que c'est possible, afin de réduire la charge et bande passante du tracker.

Vous avez aussi la possibilité de spécifier plusieurs paramètres info_hash aux trackers qui les supportent. Bien que cela ne soit pas indiqué dans les spécifications officielles, c'est devenu un standard dans la pratique – par exemple :

http://example.com/scrape.php?info_hash=aaaaaaaaaaaaaaaaaaaa&info_hash=bbbbbbbbbbbbbbbbbbbb&info_hash=cccccccccccccccccccc

La réponse de cette requête HTTP GET est un document "text/plain" ou encore une version compressée par gzip qui correspond à un dictionnaire bencodé contenant les clés suivantes :

  • files : un dictionnaire comportant des paires clés / valeurs pour chaque torrent pour lequel il existe des statistiques. Si info_hash est spécifié est est valide, le dictionnaire contiendra une seule paire clé / valeur. Chaque clé correspond au info_hash, une suite de 20 octets. La valeur de chaque entrée du dictionnaire est composé des éléments suivants :
    • complete : nombre de peers qui disposent du fichier entier, c'est à dire les seeders (entier)
    • downloaded : nombre total de fois où le tracker a enregistré une terminaison (complétion) ("event=complete", c'est à dire quand un client termine de télécharger un torrent)
    • incomplete : nombre de peers non-seeders, c'est à dire les "leechers" (entier)
    • name : (optionnel) le nom interne du torrent, tel que défini dans la section d'info du fichier torrent (name)

Notez que cette réponse a 3 niveaux de profondeur de dictionnaire (sur le même principe que les poupées russes). Voici un exemple :

d5:filesd20:....................d8:completei5e10:downloadedi50e10:incompletei10eeee

"...................." est le info_hash de 20 octets avec 5 seeders, 10 leechers, et 50 téléchargements complets.

Extensions non-officielles au scrape

Ci-dessous sont les clés de réponse qui sont utilisés non-officiellement. Comme elles sont non-officielles, elle sont aussi optionnelles.

  • Failure reason : Un message d'erreur expliquant pourquoi la requête a échouée (chaîne). Clients connus pour utiliser cette clé : Azureus.
  • Flags : un dictionnaire constitué de drapeaux divers. La valeur des clés de drapeau est un autre dictionnaire à niveaux, pouvant contenir l'information suivante :
    • min_request_interval : La valeur de cette clé est un entier correspondant au nombre de secondes que le client doit attendre avant de scraper à nouveau le tracker. Trackers connus pour utiliser cette clé : BNBT. Clients utilisant cette clé : Azureus.

BittOratio : Comment tricher sur ses statistiques d'upload

A bien regarder la façon dont sont formé les requêtes et réponses du tracker on remarque que tricher est simple : il suffit d'exagérer la valeur passée au paramêtre uploaded sur les requêtes announce.

Pour le tracker il est beaucoup plus compliqué de savoir si le client triche ou non. Toutefois certaines vérifications peuvent être faites :
  • Vérifier que le client a envoyé une requête annonce de démarrage du torrent ("event=started") avant de prétendre avoir partagé des données.
  • Surveiller s'il n'y a pas des incohérences (quantité d'upload qui diminue au lieu d'augmenter). Mais c'est incohérences peuvent être non-intentionnelles (utiliseur qui efface par mégarde un fichier que le client torrent va retélécharger etc).
  • Vérifier que le client n'est pas seul sur le torrent : pour uploader il lui faut forcément un autre client.
  • Vérifier que le client a bien ses ports de partage ouvert. Il ne peut pas partager s'il refuse toute connexion. Mais cette vérification serait difficile à mettre en place pour le tracker).
  • Calculer la vitesse d'émission du client en se basant sur la quantité d'upload qu'il prétend envoyer entre deux moments successifs et vérifier si cette vitesse est informatiquement réaliste.

Des outils existent déjà pour simuler l'upload de données : RatioMaster et RatioMaster-NG.
Le second se veut être une correction du premier. Ils ne téléchargent pas réellement les fichiers mais envoient des suites de requêtes au tracker pour faire croire qu'ils participent aux transferts.
Je n'ai pas cherché à étudier leur fonctionnement précis mais certains trackers tentent de détecter ce type de fraude par les vérifications citées plus haut.

Mon logiciel BittOratio fonctionne autrement. C'est un proxy HTTP qui modifie au vol les requêtes d'annonce à destination du tracker, multipliant la quantité de données envoyée par un facteur (nombre entier) modifiable dans le code source.
Pour qu'il fonctionne il faut donc que les fichiers soient réellement téléchargés et mis à disposition mais vous n'aurez pas à uploader autant que vous ne téléchargez pour obtenir un bon ratio.
L'avantage de cet outil est que ce système de triche est indécelable.

Il suffit de lancer le proxy qui écoute sur le port tcp 8080 et de configurer votre client BitTorrent pour passer par le proxy. Par exemple avec Transmission sous Linux, il faut modifier le fichier .config/transmission/settings.json pour placer les réglages suivants :
"proxy": "localhost",
"proxy-auth-enabled": false,
"proxy-auth-password": "",
"proxy-auth-username": "",
"proxy-enabled": true,
"proxy-port": 8080,
"proxy-type": 0,
Les requêtes interceptées sont affichées de façon succinte à l'écran (identité du tracker, hash du torrent, quantité d'upload).

La difficulté principale lors de l'écriture du programme a été de gérer les erreurs de connexion. Comme la bande-passante est bouffée par le client BitTorrent, le proxy rencontre pas mal d'erreurs de timeout (connexion au tracker indispo ou résolution dns qui prend trop de temps).
On pourrait penser que les requêtes du tracker importe peu mais elles ont un effet important sur le client BitTorrent. La première technique que j'ai employé pour gérer ces erreurs était de renvoyer au client un message HTTP 504 (Gateway Timeout) correspondant parfaitement aux problèmes de ce type mais le client avait une mauvaise tendance à abandonner au boût d'un moment.
Finalement j'ai mis en place un système de cache qui renvoit au client le résultat de la requête précédente (pour le même info_hash) et qui améliore largement la stabilité de l'ensemble.

Quand on stoppe le proxy (Ctrl+C), celui-ci "flushe" le cache et envoit les annonces d'upload qui n'auraient pas été envoyées sur le tracker faute de capacité réseau.

Parmi les modifications apportées durant l'écriture du code, j'ai du abandonner urllib2 (vraiment à la masse en terme de performances) au profit de httplib.

Pour terminer (et puisque certains se posent la question) il semble d'après mes observations qu'il est préférable de stopper (mettre en pause) les torrents avant de fermer complètement le client BitTorrent au lieu de le fermer directement : en effet le client n'envoit pas forcément les requêtes d'annonce à la fermeture pour synchroniser ses statistiques avec le tracker alors que la mise en pause envoit ces informations avec l'event "stopped".

Le code se trouve ici : bittoratio.py

J'avais d'abord cherché une analogie à Bit Torrent pour le nom du programme, je suis arrivé à "Dick Rivers"... je me suis abstenu.

Je reprendrais peut-être un jour la suite de la traduction de la spécification.

ProtoScan

Rédigé par devloop - -

Introduction

ProtoScan est un logiciel en ligne de commande qui permet de déterminer quels sont les protocoles supportés par une machine distante. ProtoScan ne fonctionne que sous plateforme Linux et se base sur la librairie du C standard (glibc).

Partie Technique

Rappels sur le protocole IP


Le Protocole Internet (IP) permet à plusieurs machines de communiquer entre elles). Toutefois ce protocole ne permet pas d'échanger des données, il sert de support à des protocoles de plus haut niveau qui se chargent du transport des données.
Le système de support entre protocoles est le suivant : le logiciel que vous utilisez va générer des données dans un protocole spécifique. Dans l'exemple d'un navigateur de page web, ce dernier va générer du HTTP. Celui-ci est un protocole de haut niveau et ne peux pas transiter pur sur le réseau. L'ordinateur va donc rajouter l'entête du protocole servant de support (TCP) puis rajouteras ensuite l'entête du protocole servant de support au TCP, à savoir IP.

A l'opposé, le serveur dont vous désirez lire les pages web va devoir retirer les entêtes les un après les autres pour parvenir à ce qui l'intéresse, à savoir la requête HTTP générée par votre navigateur.

Seulement comment le serveur fait-il le tri parmi toutes les données qu'il reçoit ? Une fois qu'il a retiré l'entête IP, il est tout à fait possible que ce qui suit ne soit pas du TCP mais du UDP, du GGP ou un autre protocole...
En fait c'est l'entête IP qui indique quel est le protocole qui suit. L'entête IP possède en effet un champ 'protocol' qui identifie le protocole supérieur par un chiffre bien définit ; ainsi si ce champ a pour valeur 6 alors le serveur saura qu'il s'agit du protocole TCP.

Protoscan se base sur ce champ pour envoyer des données. Nous verrons tout à l'heure pourquoi.

Rappels sur le protocole ICMP


Le protocole IP n'est pas un protocole 'fiable' (voir Etude IP sur SupInfo). Il se fiche bien se savoir si les données arrive effectivement à destination et si elles arrivent en bonne état. Pour combler cette lacune le protocole ICMP (Internet Control Message Protocol) a été créé. Comme son nom l'indique il se charge d'informer des problèmes rencontrés par les paquets IP.
Bien que IP et ICMP soient des protocoles a part entière, ils sont indissociables l'un de l'autre. Un constructeur informatique ne peux pas implémenter l'IP sans ICMP et vice-versa.

Le protocole ICMP permet entre autres de savoir si la machine que l'on souhaite contacter est en marche (commande ping), ou de connaître les routeurs par lesquels passent nos données (commande traceroute). Quand on connait mieux le protocole ICMP on s'appercoit qu'il permet d'obtenir des informations intéressantes sur une machine, comme les ports UDP fermés, si la machine se situe derrière un firewall, ou encore si un protocole est supporté ou non par la machine en question.

C'est ce dernier cas qui nous intéresse.

Fonctionnement de ProtoScan

Le rôle de ProtoScan est de déterminer quels sont les protocoles supportés par une machine distante. Pour cela, il va délibéremment provoquer des erreurs qui lui permetront de recevoir les messages ICMP concernant les protocoles supportés.
ProtoScan va envoyer des paquets malformés. Ces paquets ne sont toutefois pas générés au hazard : il s'arrêtent à l'entête IP (aucun protocole ne se trouve après).

Pour observer le comportement d'une machine face à ces paquets malformés, nous allons faire des tests avec Hping, un logiciel qui permet de générer ses propres paquets (il faut être root).
# hping3 -c 1 --rawip --ipproto 6 127.0.0.1
HPING 127.0.0.1 (lo 127.0.0.1): raw IP mode set, 20 headers + 0 data bytes
[|tcp]
--- 127.0.0.1 hping statistic ---
1 packets tramitted, 0 packets received, 100% packet loss
round-trip min/avg/max = 0.0/0.0/0.0 ms
D'abord quelques éclaircissement sur la ligne de commande :
-c 1 permet de n'envoyer le paquet qu'une seule fois
--rawip permet de générer soit même les entetes IP
--ipproto 6 fixe le champ 'protocol' de l'entête IP à 6. Cela correspond à TCP la ligne se termine bien évidemment avec l'adresse de la machine destinataire.

Dans cette exemple les statistiques nous montrent clairement que la machine qui a reçu notre paquet n'a pas généré d'erreur. Il faut dire aussi que le protocole TCP est un protocole supporté par presque toutes les machines réseaux.

Essayons maintenant avec un protocole moins connu, par exemple HMP (Host Monitoring Protocol) qui est désigné par la valeur 20.
# hping3 -c 1 --rawip --ipproto 20 127.0.0.1
HPING 127.0.0.1 (lo 127.0.0.1): raw IP mode set, 20 headers + 0 data bytes
ICMP Protocol Unreachable from ip=127.0.0.1 name=localhost
--- 127.0.0.1 hping statistic ---
1 packets tramitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 0.0/0.0/0.0 ms
Cette fois, la machine nous a renvoyé un paquet. Il s'agit d'un paquet ICMP dont le libellé est 'Protocol Unreachable' qui nous informe clairement que le protocole que nous avons demandé (HMP) n'est pas supporté par la machine.

Pour connaître tous les protocoles supportés par une machine distante il suffirait donc de répéter la commande pour toutes les valeurs possibles du champ 'protocol' fixé par l'option ipproto de Hping.
Théoriquement cela fait 256 possibilités. En pratique il n'y a que 136 protocoles reconnus pour l'insant (Les protocoles et leur numéro sont définis dans le fichier /etc/protocols sur un système Linux).

ProtoScan se charge de tester toutes les possibilités et affiche les résultats dans un format compréhensible. Pour utiliser correctement Protoscan il suffit de passer l'adresse de la machine en argument :
# ./protoscan 127.0.0.1
        Launching scan...
Protocol icmp [1] supporte
Protocol igmp [2] supporte
Protocol ipv4 [4] supporte
Protocol tcp [6] supporte
Protocol udp [17] supporte
        Scan done!
Protoscan a été programmé en C et repose sur les librairies standard, par conséquence il ne nécessite pas l'installation d'un autre logiciel ou d'une librairie spéciale.

Compilation : gcc -o protoscan protoscan.c
Télécharger le code source protoscan.c

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

Utilisations alternatives du protocole BitTorrent

Rédigé par devloop - -

Le protocole BitTorrent est un protocole de transfert de données Poste à poste (P2P) qui est devenu très populaire. Il supporte le multisourcing et permet de télécharger parallèlement plusieurs blocks d'un même fichier.
Il est centralisé autour d'un "tracker" qui correspond à un annuaire de personnes qui partagent un fichier à un moment donné. Quand un client récupère cet annuaire il peut se connecter à chaque client pour négocier le téléchargement d'un ou plusieurs morceaux de fichiers.
La recherche de fichier est elle complétement décentralisée, on ne demande pas au tracker s'il possède le fichier que l'on recherche. Il faut pour télécharger une ressource, disposer d'un fichier métainfo .torrent qui décrit les fichiers et le tracker.

Le protocole BitTorrent est aussi connu pour sa facultée à charger la bande passante des peers. Le protocole est fait de telle façon que le tracker n'a que peu d'informations à échanger avec les clients.

Dans cet article je vous présenterais une partie du protocole BitTorrent qui correspond à la traduction du début de la spécification puis je parlerais des utilisations alternatives qui peuvent être faites de ce système.

Spécifications du Protocole Bittorrent v1.0

Présentation

BitTorrent est un protocole d'échange de fichier peer-to-peer conçu par Bram Cohen. Visitez son site à http://www.bittorrent.com. BitTorrent est conçu pour faciliter les transferts de fichiers entre plusieurs peers sur des réseaux non fiables.

Objectif

L'objectif de cette spécification est de documenter la version 1.0 du protocole BitTorrent dans les détails. La page de spécification du protocole faite par Bram expose les grandes lignes du protocole dans des termes simples, et manque de détails sur certains points. Notre souhait est que ce document devienne la spécification officielle, écrite à l'aide de termes clair et sans ubiquité, qui peut aussi bien être utilisée comme base pour des discussions que pour des implémentations futures.

Ce document se veut être tenu à jour et utilisé par le communauté de développement BitTorrent. Tout le monde est invité à contribuer à ce document, en gardant à l'esprit que son contenu doit représenter le protocole actuel, déjà déployé dans un bon nombre d'implémentations clientes.

Conventions

Dans ce document, différentes conventions sont utilisées afin de présenter les informations de façon concise et sans équivoque.
  • peer contre client : Dans ce document, un peer est n'importe quel client BitTorrent participant au téléchargement. Le client est aussi un peer, cependant il s'agit du client BitTorrent fonctionnant localement sur la machine.
  • piece contre block : Dans ce document, un "piece" (morceau) correspond à une portion des données téléchargées, décrite dans le fichier métainfo, qui peut-être validé par un hash SHA1. Un block est une portion de données qu'un client peut demander à un peer. Un "piece" est constitué de deux blocks ou plus, qui peuvent ensuite être validés.

bencodage

Le bencodage est une méthode permettant de spécifier et d'organiser les informations dans un format court. Cette méthode supporte les types suivants : chaine de caractères, entiers, listes et dictionnaires.

Chaines de caractères
Les chaines de caractère sont codées de la façon : <longueur de la chaine en base 10 ASCII>:<chaine de caractères> Notez qu'il n'y a aucun délimiteur de début et de fin.

Exemple: 4:spam représente la chaine "spam"

Entiers
Les entiers sont encodés de a façon suivante : i<entier en base 10 ASCII>e
Le i initial et le e de fin sont les délimiteurs. Il est possible d'avoir des entiers négatifs comme i-3e. Il est interdit de préfixer le nombre avec un zéro comme dans i04e. Cependant i0e est valide.

Exemple : i3e représente l'entier 3

Note : le nombre maximum de bits pour cet entier n'est pas spécifié mais il s'agit généralement d'un entier signé de 64 bits qui permet de manipuler des "gros fichiers" (dépassant les 4Go).

Listes
Les listes sont encodées selon le format : l<valeurs bencodées>e
Le l et le e sont les délimiteurs de début et de fin. Une liste peut contenir n'importe qi'elle type bencodé, y compris d'autres listes...

Exemple : l4:spam4:eggse représente une liste composée de deux chaines : ["spam", "eggs"]

Dictionnaires
Les dictionnaires sont encodés selon le modèle suivant : d<chaine bencodée><élément bencodé>e
Le d et le e servent de délimiteurs. Notez que les clés doivent être des chaines bencodées. Les valeurs peuvent être de n'importe quel type bencodé, aussi bien les entiers, les chaines, les listes ou d'autres dictionnaires. Les clés doivent être des chaines et apparaître dans l'ordre alphabétiques. Les chaines doivent pouvoir être comparées par une comparaison binaire, en opposition à une comparaison spécifique à une culture.

Exemple : d3:cow3:moo4:spam4eggse représente le dictionnaire {"cow"=>"moo","spam"=>"eggs"}
Exemple : d4:spaml1:a1:bee représente le fictionnaire {"spam"=>["a","b"]}
Exemple : d9:publisher3:bob18:publisher.location4:home17:publisher-webpage15:www.exemple.come


Structure de fichier métainfo

Toutes les données dans le fichier métainfo sont bencodées. La spécification pour le bencodage est définie ci-dessus.
Le contenu d'un fichier métainfo (un fichier avec une extension ".torrent") est un dictionnaire bencodé, contenant les clés listés ci-dessous. Toutes les chaines de caractères sont encodées en UTF-8.

  • info : un dictionnaire qui décris les fichiers du torrent. Il y a deux formes possibles : une dans le cas où le torrent correspond à un seul fichier sans structure de répertoire, et le cas où l'on a affaire à un torrent "multi-fichier" (voir détails plus loin)
  • announce : l'url d'annonce du tracker (chaine de caractères)
  • allounce-list : (optionnel) c'est une extension de la spécification officielle, qui est aussi à compatibilité ascendante. Cette clé est utilisée pour implémenter une liste de trackers de secours. La spécification complète peut être lue ici
  • creation date : (optionnel) la date de création du torrent, dans le format de date standard UNIX (nombre de secondes dpuis le 1/1/1970 à 00:00:00 UTC)
  • comment : (optionnel) commentaires de l'auteur (chaine de caractères)
  • created by : (optionnel) nom et version du programme utilisé pour créer le .torrent (chaine de caractères)

Dictionnaire info

Cette section contient les champs qui sont communs aux deux modes, "fichier seul" et "fichier multiple".
  • piece-length : nombre d'octets dans chaque piece (entier)
  • pieces : chaine qui consiste en la concaténation de tous les hashs SHA1 de 20 octets, un par piece (chaine d'octets)
  • private : (optionnel) ce champ est un entier. S'il est à "1", le client DOIT publier sa présence pour obtenir d'autres peers SEULEMENT via les trackers déclarés explicitement dans le fichier métainfo. Si ce champ est à "0" ou s'il n'est pas présent, le client peut obtenir des peer par d'autres moyens, par exemple l'échange de peer PEX, DHT. Ici "privé" doit être compris comme pas de peers d'origine extérieure.

info dans le mode "fichier seul"

Dans le cas du mode "fichier seul", le dictionnaire info contient la structure suivante :
name : le nom du fichier. A titre purement consultatif. (chaine)
length : longeur du fichier en octets (entier)
md5sum : (optionnel) une chaine de 32 caractères hexadécimaux correspondant à la somme MD5 du fichier. Elle n'est pas du tout utilisée par BitTorrent, mais certains programmes l'utilisent pour une meilleure compatibilité.

info dans le mode "fichier multiple"

Dans le cas du mode "fichier multiple", le dictionnaire info contient la structure suivante :
  • name : le nom du répertoire dans lequel stocker tous les fichiers. A titre purement consultatif. (chaine)
  • files : une liste de fictionnaires, un pour chaque fichier. Chaque dictionnaire contient les clés suivantes :
    • length : longueur du fichier en octets (entier)
    • md5sum : (optionnel) une chaine de 32 caractères hexadécimaux correspondant à la somme MD5 du fichier. Elle n'est pas du tout utilisée par BitTorrent, mais certains programmes l'utilisent pour une meilleure compatibilité.
    • path : une liste contenant une ou plusieurs chaines qui regroupées représentent le chemin (path) et le nom du fichier. Chaque élément de cette liste correspond au nom d'un répertoire ou (dans le cas du dernier élément) au nom du fichier. Par exemple, un fichier "dir1/dir2/file.ext" consistera de trois chaines : "dir1", "dir2", et "file.ext". Le tout est encodé comme une liste bencodée de chaines tel que l4:dir14:dir28:file.exte

Tracker HTTP/HTTPS Protocol

Le tracker est un service HTTP/HTTPS qui répond à des requêtes HTTP GET. Les requêtes incluent les paramêtres des clients qui permettent au tracker de générer des statistiques générales. La réponse inclue une liste de peer qui aide le client à la diffusion du torrent. La base de l'url se compose d'une url d'annonce telle que définit dans le fichier métadonnée (.torrent). Les paramêtres sont alors ajoutés à l'url, en utilisant la méthode CGI standard (c'est à dire avec un '?' après l'url d'annonce, suivie par des séquences 'param=valeur' séparées par '&').

Notez que toutes les données binaires dans l'url (particulièrement info_hash et peer_id) doit être proprement échappées. Cela signifie que toutes les valeurs ne se trouvant pas dans l'ensemble 0-9,a-z,A-Z,'.','-','_' et '˜', doivent être encodées sous le format "%nn" où nn est la valeur hexadécimale de l'octet.

Pour le hash de 20 octets suivant :
\x12\x34\x56\x78\x9a\xbc\xde\xf1\x23\x45\x67\x89\xab\xcd\xef\x12\x34\x56\x78\x9a
la forme encodée correcte sera %124Vx%9A%BC%DE%F1%23-g%89%AB%CD%EF%12%34Vx%9A.

Paramêtres de Requête Tracker
Les paramêtres utilisées dans les requêtes client->tracker sont les suivants :
  • info_hash : hash SHA1 de 20 octets urlencodé de la valeur de la clé info du fichier métainfo. Notez que cete valeur sera un dictionnaire bencodé, comme dis dans la définition de la clé info ci-dessus.
  • peer_id : chaine de 20 octets urlencodée et utilisée comme ID unique du client, générée par le client à son lancement. N'importe qu'elle valeur est acceptée et peut être des données binaires.
  • port : le numéro de port sur lequel le client écoute. Les ports réservés pour BitTorrent sont typiquement 6881-6889. Les clients peuvent choisir d'abandonner s'ils ne parviennent pas à écouter sur un port dans cette plage.
  • uploaded : la somme totale uploadée (depuis que le client à envoyé l'évennement 'started' au tracker) en base 10 ASCII. Bien que cela ne soit pas explicitement déclaré dans la spécification officielle, il s'agit du nombre total d'octets téléchargé.
  • left : La somme des octets que le client doit encore télécharger, encodé en base 10 ASCII.
  • compact: Indique que le client accepte une réponse compacte. La liste de peer est remplacée par une chaine de peers avec 6 octets par peers. Les 4 premiers octets représentent l'hôte (avec les octets dans l'ordre réseau), les deux derniers sont le port (là encore dans l'ordre réseau). Notez que certains trackers ne supportent que les réponses compactes (pour économiser de la bande passante) et refusent les requêtes normales.
  • no_peer_id : Indique que le tracker peut ommettre le champ peer id dans le dictionnaire de peers. Cette option est ignorée si compact est activé.
  • event : si spécifié, il doit être l'un des états started, completed ou stopped (ou vide qui revient à non spécifié). Si non spécifié, alors cette requête est faite à intervalles réguliers.
    • started : la première requête au tracker doit inclure la clé event avec cette valeur.
    • stopped : Doit être envoyé au tracker si le client s'arrête proprement.
    • completed : Doit être envoyé au tracker quand le téléchargement est complet. Cepandant, ne dois pas être envoyé si le téléchargement était déjà à 100% au lancement du client. Vraisemblablement, cela permet au tracker de garder des statistiques des téléchargements complets.
  • ip : Optionnel. La véritable adresse IP du client, dans le format standard (4 nombres décimaux de 0 à 255 séparés par des points). Ce paramêtre est utile seulement si l'adresse IP qui envoit les requêtes n'est pas la même que celle du client.
  • numwant : Optionnel. Nombre de peers que le client veut bien recevoir du tracker. Cette valaur peut être à zéro. Si omise, elle veut généralement 50 peers.
  • key: Optionnel. Une identification addtionnelle qui n'est partagée avec aucun utilsateur. Cela permet à un client de s'identifier même si son adresse IP change.
  • trackerid : Optionnel. Si l'annonce précédente contenait un tracker id, il doit être placé ici.

Utilisations alternatives

Découvertes des ports NATés
Une application qui a besoin de communiquer avec des machines sur Internet est souvent bloquée par un routeur/firewall. Il faut alors que l'utilisateur de la machine configure lui même son équipement afin de NATer certains ports pour que l'application soit joinable. Mais ces applications préfèrent parfois trouver une "porte" elles même pour simplifier la vie de l'utilisateur ou parce qu'elles ne veulent rester "discrètes".
Pour les ports UDP, il existe le protocole STUN qui permet à une application, en effectuant une suite de communications pré-établies, de déterminer derrière quel type d'équipement elle se trouve.

La première utilisation alternative de BitTorrent à laquelle j'ai pensée, c'est la détection de ports TCP NATés. Le principe est très simple : à partir d'un fichier torrent valide et partagé par un grand nombre de peer, on calcule les paramêtres nécessaires pour envoyer une requête au tracker et pour chaque port que l'on veut tester on envoie cette requête, avec comme seule modification le paramêtre port.

Pour décoder les fichiers torrent, j'ai utilisé une implémentation Python qui était citée dans la spécification.
L'utilisation du langage Python est d'autant plus agréable que les différents types présents dans un fichier métainfo sont les même que ceux proposés par Python (str, int, list et dict).

Les paramêtres nécessaires à l'envoir d'une requête valide sont info_hash (à calculer), peer_id (aléatoire, 20 octets), compact (que nous fixerons à 1), port (à fixer nous même) ainsi que uploaded (0), downloaded (0) left (taille totale du ou des fichiers) et event (started) qui nous déclarent comme nouveau peer et permettent notre entrée dans la liste du tracker.

Le plus compliqué a été le calcul du hash SHA1 du dictionnaire info bencodé... Au départ je pensais chercher un moyen de le lire directement en analysant le fichier torrent au fûr et à mesure, mais devant la difficulté j'ai préféré trouvé une autre solution en utilisant le code déjà implémenté.
Pour cela j'ai rajouté des fonctions de bencodage à celles déjà présentes de bdécodage. Une fois la structure info re-bencodée, il suffisait de calculer la somme SHA1... en théorie.
La spécification dit que les clés du dictionnaire info doivent être ordonnées alphabétiquement. Visiblement les logiciels qui génèrent les fichiers torrent ne l'on pas pris en compte. Mais quelque soit l'ordre des clés, le résultat dans le fichier fera toujours la même taille.
J'ai donc ré-encodé le dictionnaire info, calculé sa taille, puis extrait le nombre d'octets correspondant du fichier torrent pour calculer le SHA1.

Le calcul de la taille totale des données est assez simple. Dans le mode "fichier seul" il nous suffit de lire le champ length, sinon il faut boucler sur chaque dictionnaire files et faire des additions.

Le résultat est un script torrentscan.py qui prend comme arguments le nom du fichier torrent à utiliser et la liste des ports sur lesquels écouter.

Voici le résultat de deux essais, sachant que mes ports 21-25, 6666 et 6881 sont redirigés depuis mon routeur vers ma machine :
python torrentscan.py aaf.torrent 21,80,6666,6880-6884
Tracker: http://denis.stalker.h3q.com:6969/announce
Hash: %27%CD%C0%D5%D2%0A%F4%01%E3X%9C%A9%21a%EAn%F6%B7%E8%40
Total length: 368701440
Announced port 6884
Announced port 80
Error listening on port 80
(98, 'Address already in use')
Announced port 6666
Announced port 6880
Announced port 21
Announced port 6882
Announced port 6881
Announced port 6883
[6881, 6666, 21]
Ici le programme a été lancé en root. Apache occupait déjà le port 80, ce qui explique le message "Address already in use". En simple utilisateur on aurait eu un "Permission denied" pour les ports 21 et 80.
Les ports accessibles de l'extérieur sont affichés à la fin.
python torrentscan.py aaf.torrent 21,24,4444-4446,6666
Tracker: http://denis.stalker.h3q.com:6969/announce
Hash: %27%CD%C0%D5%D2%0A%F4%01%E3X%9C%A9%21a%EAn%F6%B7%E8%40
Total length: 368701440
Announced port 4446
Announced port 24
Announced port 4445
Announced port 21
Announced port 4444
Announced port 6666
[21, 6666, 24]
Le programme est assez long à s'exécuter. La partie qui attend les connexions demande à être améliorée. Pour chaque port, un thread est lancé qui envoit une annonce au tracker puis se met en écoute. Les threads ont jusqu'à 10 secondes pour s'exécuter mais la getion des sockets fait dépasser ce temps (même si le programme s'arrête tout seul au boût d'un moment).
Ensuite j'ai remarqué que les clients BitTorrent communiquent très peu avec le tracker. Pour un gros fichier, j'ai compté que Azureus a envoyé moins de 5 requêtes. Les clients ne cherchent pas vraiment à mettre leur liste de peers à jour. Il faut alors compter sur les peers arrivant après nous pour qu'ils nous contactent.
C'est pour cela qu'il est très important d'utiliser un torrent très demandé (le torrent pour les isos d'une distribution Linux juste après sa sortie est l'idéal)

Attaque par amplification
Un envoyant des requêtes d'annonce sur un grand nombre de trackers pour un nombre tout aussi important de torrent très demandé et en spécifiant l'adresse IP d'une machine dans le paramêtre "ip" de la requête, on peut effectuer une attaque DDoS (déni de service distribué) par amplification (comme le célèbre Smurf).
L'attaque est dite par amplification car en effectuant une seule connexion à une seulle machine (le serveur d'un tracker), on provoque la connexion de plusieurs peers sur la victime.
L'efficacité de cette attaque est liée à tout un tas de paramêtres à prendre en considération : nombre de fichiers torrent, demandes pour ces données, réaction du serveur (va t-il fermer la connexion immédiatement en voyant arriver des données malformées ? sinon combien de temps va t-il attendre ?) et du client BitTorrent (va t-il laisser au serveur le soin de fermer la communication ? combien de nouvelles tentatives va t-il faire après le premier échec) etc.


Utilisation par les Botnets
Un bot pourrait très bien utiliser le tracker d'un torrent anodin pour se déclarer à son "bot herder". La majorité des bots existant utilisent le protocole IRC et se connectent à un channel avec un pseudo ayant une forme caractéristique (par exemple "botXXXXXXXX" où les 'X' sont plus ou moins aléatoires).
Le paramêtre "peer_id" est l'identifiant du peer et doit faire 20 octets. C'est largement suffisant pour y placer des informations qui permettront au bot herder de le reconnaitre des peers légitimes. Quand au port déclaré auprès du tracker il pourrait en fait s'agir du port d'une backdoor, port choisi par exemple parce qu'il a été découvert comme NATé en utilisant le protocole BitTorrent et la porte dérobée attendant des commandes, à tout hazard une attaque BitTorrent-plifiée.

Je reviendrais surrement un jour sur les protocoles décentralisés, parce qu'il y a tout un tas de choses passionantes (Distributed Hash Table et compagnie).

Mise à jour :
Un cas de malware utilisant les données de trackers public a été révélé. Il génère des noms de fichiers en se basant sur le "top" du site ThePirateBay.
Au 27ème Chaos Communication Congress, une technique de DDoS basée sur l'utilisation des DHT du protocole BitTorrent a été présenté..

Classé dans : Non classé - Mots clés : réseau

Bypass de firewall sur le port source

Rédigé par devloop - -

Un firewall n'est rien de plus qu'un ensemble de règles qui détermine ce qui passe sur un réseau et ce qui ne passe pas.
Si les règles sont mal écrites, ou si le logiciel firewall est buggé ou peu évolué, il peut être possible de passer à travers ces règles pour accèder à des services normalement filtrés.

Fixer un port source pour ses connexions est l'un des méthodes utilisées pour passer un firewall. Les pare-feux vérifient principalement les adresses IP ainsi que les ports source et destination de chaque paquet qui passe, ce qui permet d'écrire des règles assez facilement
On distingue ensuite deux catégories de firewall : les stateless et les statefull.
Les firewalls "statefull" mémorisent l'état des connexions (demande de connexion, connexion établie...), ce que les "stateless" ne font pas.

Mais certaines applications ou certains protocoles comme FTP sont de vrais casse-têtes pour le filtrage des paquets.
Le protocole FTP indique en effet que le serveur renvoit les données depuis son port 20 vers un port du client. Par conséquent si un administrateur veut permettre aux personnes sur son réseau d'utiliser des clients FTP il va probablement autoriser tout paquet dont le port source est 20.

Un attaquant pourra alors découvrir des services normalement cachés en effectuant un scan de port avec des paquets dont le port source est 20. L'option -g de Nmap permet de faire ça très facilement.

Une fois que l'intrus aura trouvé un service accessible, il va devoir faire en sorte que ses connexions viennent du port source lui ouvrant l'accès à la machine.
Pour cela j'ai relevé différentes solutions plus ou moins efficaces. Par la suite on considérera que l'attaquant possède l'adresse IP 192.168.0.2 et tente d'accèder au port 80 de la machine 192.168.0.3 en fixant son port source à 20.

Netcat 1.10
Netcat est le "couteau suisse TCP/IP". Ce serait trop long de décrire toutes les opérations que l'on peut faire avec mais vous trouverez différents documents sur le sujet sur Internet.
Notre visiteur l'utilisera de la façon suivante :
netcat -v -p 20 192.168.0.3 80

Cela va ouvrir une connexion à destination du port 80 de la machine filtrée et avec le port source 20.
Le premier souci c'est que netcat récupère ce qu'il doit envoyer sur l'entrée standard. Pour les protocoles utilisant des caractères ASCII ça va encore mais dans la majorité des cas il faut avoir recours à des redirections d'entrées/sorties.
On pourrait alors chainer deux netcat, l'un écoutant sur un port en renvoyant ce qu'il reçoit vers l'entrée du second netcat (avec la commande précédente).
Le second souci c'est que netcat ne fonctionne pas comme un serveur qui tournerait en fond pour réceptionner en continu les requêtes et convient mal à certains protocoles qui effectuent des connexions courtes et nombreuses (HTTP par exemple).
La version Windows de netcat a bien une option -L pour l'écoute continue mais on croise à nouveau le problème des redirections.

KevProxy
KevProxy comble avec succès les lacunes que l'on avait avec netcat.
Le programme a certes moins de fonctionnalités mais fait exactement ce dont on a besoin pour cet exercice. Il ouvre un port en local en créant un thread pour chaque connexion. Chaque connexion est renvoyée sur une ip et un port spécifié en prenant soin de modifier le port source.
Pour l'utiliser dans notre cas on utilisera la commande suivante :
kp 8080 192.168.0.3 80 20 v

Il n'y a plus qu'à lancer son navigateur sur localhost:8080 et on accèdera par notre tunnel au port 80 de la machine filtrée. Le "v" final est optionnel et ajoute de la verbosité.
Un excellent outil qui mérite à être connu.

Fpipe 2.1
Fpipe est le célèbre outil de redirection de port de Foundstone et fonctionne sous Windows. Il fait tout ce que KevProxy peut faire et offre quelques options supplémentaires.
La commande à lancer sera :
fpipe -v -l 8080 -s 20 -r 80 192.168.0.3

Le principe est exactement le même que pour KP.

Netfilter / iptables
Quoi de mieux qu'un firewall pour passer un autre firewall ?
Ici on va se servir des fonctionnalités de SNAT de Netfilter pour réécrire au vol le port source des paquets à destination du port 80 de la machine 192.168.0.3 :
iptables -t nat -A POSTROUTING -p TCP -m tcp -s 192.168.0.2 --dport 80 -j SNAT --to-source 192.168.0.2:20

Tout se fait de façon complétement transparente.

Des documents sur le même sujet :
Phrack : Breaking through a Firewall using a forged FTP command
Nmap : Évitement de pare-feux/IDS et mystification
Pyramid BenHur Firewall Active FTP Portfilter Ruleset Results in a Firewall Leak

Classé dans : Non classé - Mots clés : réseau