IP Personality Gaël Roualland - Jean-Marc Saffroy $Id: ippersonality.sgml,v 1.27 2001/07/23 22:19:36 g_roual­ land Exp $ Documentation IP Personality (List : ippersonality-devel@lists.source­ forge.net) ______________________________________________________________________ Table des matières 1. Introduction 2. Problèmes 3. IP Personality 3.1 Fonctionnalités 3.2 Trajet d'un paquet dans PERS 3.2.1 Cas d'un paquet TCP 3.2.2 Cas d'un paquet UDP 3.2.3 Partie commune des packets IP 4. Configuration 4.1 Options en ligne de commande 4.2 Fichier de configuration 4.2.1 Identification 4.2.2 Paramètres génériques TCP 4.2.3 Paramètres de générateur de numéros de séquence 4.2.4 Paramètres de générateur d'Identifiants IP 4.2.5 Paramètres de rééordonnancement des options 4.2.6 Paramètres du leurre TCP 4.2.7 Paramètres du leurre UDP 4.3 Langage 5. Exemple 5.1 Fichier de configuration 5.2 Réseau de test 6. Pseudo Code 6.1 Généralités 6.2 Instructions 6.2.1 TEST 6.2.2 JMP 6.2.3 PUT 6.2.4 SET 6.2.5 RET 6.3 Options TCP 7. Outils de développement 7.1 Debogage 7.2 Osdet 7.3 Perscc 8. Bibliographie ______________________________________________________________________ 11.. IInnttrroodduuccttiioonn En dehors des comportements specifiés par les RFC, chaque pile TCP/IP possède ses propres spécificités (choix d'implémentation, bugs, améliorations, ...) en particulier concernant la manière de réagir face aux paquets anormaux, c'est à dire ne respectant pas les RFC. Ces spécificités sont exploitées par des logiciels afin de deviner, à distance via le réseau, quel système d'exploitation tourne sur une machine donnée. Ils fonctionnent en émettant des paquets anormaux (en jouant notamment sur la fragmentation, les flags TCP, les champs inutilisés, la taille des paquets, ...) à destination des machines cibles, et analysent les réponses en les comparant à une base de comportements connus des différents systèmes d'exploitation. Ces logiciels sont utilisés par les administrateurs réseau pour recenser leurs parcs hétérogènes de machines, mais aussi par les pirates qui cherchent à apprendre le maximum d'informations sur une machine ou un réseau de machines pour adapter leurs attaques et maximiser leurs chances de réussite. _I_P _P_e_r_s_o_n_a_l_i_t_y est un module pour _n_e_t_f_i_l_t_e_r dont l'objectif est de pouvoir émuler différentes "personalités" réseau, en changeant les caractéristiques du traffic réseau, selon différents paramètres. En particulier cela permet de tromper de tels outils en faisant passer un système pour un autre, afin par exemple de masquer ou de protéger des systèmes autrement vulnérables, ou encore pour mettre en place des "pots de miel" (honey pots). 22.. PPrroobbllèèmmeess L'examen des tests effectués par nmap montre qu'ils s'appuient sur des paquets sinon anormaux, du moins étranges. Ceux-ci sont donc facilement détectables et peuvent déclencher des actions appropriées. Ainsi on peut aisément envisager de modifier les réponses d'une machine locale lorsque l'on reçoit de tels paquets. Mais ces modifications ne sont pas sans conséquences : · certaines caractéristiques d'OS sont dûes à l'architecture sur laquelle ils fonctionnent, par exemple les tailles de page mémoire sur différents processeurs, ce qui peut éventuellement poser des problèmes de performances ; · certaines des modifications reposent sur des choix "politiques" lors de l'implémentation de la pile IP (choix des numéros de séquence, taille des fenêtres, options IP supportées). Les modifier permet de tromper le détecteur mais peut poser problème lors de communication légitimes, en détériorant les capacités de la pile IP ou en affaiblissant sa sécurité, par exemple si la qualité générale de la pile IP du système émulé (conformité avec la RFC, robustesse) est inférieure à celle de la machine l'émulant. Malgré tout, ces modifications sont réalisables dans la plupart des cas pour la machine locale. En revanche, il n'en est pas de même pour les machines routées, en effet : · tous les tests ne peuvent être gérés par la machine locale, celle- ci ne connaissant pas l'état précis de la pile IP des machines routées, nécessaire pour générer les réponses adéquates ; · les décisions prises par les machines routées ne peuvent être que partiellement modifiées par la machine locale et ce de manière permanente, la machine locale n'ayant pas de moyen de communiquer ses modifications aux machines routées ; · toute information perdue par les machines routées ne pourra être restituée par la machine locale (à moins de conserver tout le traffic, ce qui n'est pas réalisable) ; · la machine locale ne doit pas inventer des informations. Par exemple, si l'on prend le cadre d'un test auquel les machines ne donnent aucune réponse, l'envoi d'une réponse par la machine locale en lieu et place de la machine routée permettrait éventuellemnt de répondre pour une machine inexistante. 33.. IIPP PPeerrssoonnaalliittyy Au vu des contraintes précédentes, nous avons donc opté pour une solution basée sur _n_e_t_f_i_l_t_e_r et _i_p_t_a_b_l_e_s : en effet, l'architecte d'_i_p_t_a_b_l_e_s a prévu une table mangle, justement prévue pour les manipulations de paquets (par opposition aux tables filter et nat, prévues pour le filtrage et la translation d'adresse, respectivement). Nous avons donc créé une nouvelle target PERS (pour IP Personality), qui effectue certaines opérations de réécriture sur les paquets qu'on lui passe. Le système des règles permet de laisser à _i_p_t_a_b_l_e_s le soin de sélectionner les paquets IP en fonction de leurs adresses et ports source et destination, et les paramètres passés à la target PERS lui donnent un comportement variable, réglable par l'administrateur, qui peut ainsi définir quel type de réécriture il veut voir appliquer à une catégorie de paquets. 33..11.. FFoonnccttiioonnnnaalliittééss Une fois installé et configuré correctement, IP Personality offre la possibilité de leurrer nmap, et de lui faire croire que la machine hôte fait tourner un système librement spécifié par l'administrateur. Les paquets de test qu'envoie nmap sont pour la plupart anormaux, et ceux qui ne le sont pas sont envoyés à des ports fermés, donc ils n'influencent pas l'état de la pile TCP/IP locale : nous pouvons donc les détourner sans scrupule, et émettre les réponses qui nous conviennent. Le système de configuration de PERS permet de couvrir tout l'éventail des possibilités de réponses, et ainsi nous pouvons renvoyer à nmap des paquets caractéristiques de n'importe quel système décrit dans sa base de signatures. Certaines des opérations effectuées pour leurrer nmap (pas toutes hélas) peuvent également être exécutées sur des paquets routés par notre machine. Si nous perdons la capacité de tromper complètement nmap, nos manipulations sont suffisamment efficaces pour l'empêcher de détecter le système utilisé sur sa cible. Les opérations possibles sur des paquets routés sont la réécriture des numéros de séquence et des options TCP. Au passage, nous gagnons également en robustesse dans le cas de certaines réécritures. En particulier, les machines dont les générateurs d'ISN trop simplistes les rendent vulnérables à des attaques par prédiction de numéros de séquence peuvent ainsi être protégées par notre target, qui leur offre un ISN parfaitement aléatoire. De plus, grâce à la souplesse qu'offre la syntaxe du fichier de configuration, les possibilités d'émulations ne sont pas limitées aux outils de prise d'empreintes réseau existants : il devient très facile sinon de tromper, du moins de perturber n'importe quel outil raisonnant sur les mêmes bases que nmap, puisque nous avons le contrôle des éléments caractéristiques d'un paquet. Pour tenir compte des nombreuses possibilités de comportement d'une pile IP, la configuration s'effectue via un fichier de configuration complet détaillant les valeurs de différents paramètres. Ce fichier est interprété et chargé dans l'espace noyau via une extension au programe de configuration de _n_e_t_f_i_l_t_e_r, _i_p_t_a_b_l_e_s. En particulier, pour les cas de réécritures complexes dépendant de nombreux paramètres, le fichier contient deux sections de "code" qui sont interprétés dans le noyau (sous forme de pseudo code) pour analyser plus finement les paquets selon des algorithmes analogues aux systèmes émulés. 33..22.. TTrraajjeett dd''uunn ppaaqquueett ddaannss PPEERRSS +----->---->---+----+--->---->-----+ | +---<----<---| VM |---<----<---+ | | | +----+ | | +--+-+--+ | | +->-| Decoy |->-+ | | | +-------+ | +-----+ +-----+ +-+-+-+ +-->--+->--->--->--->-+-| SEQ |->-| WIN |->-| OPT |-+ +-----------+ | TCP +-----+ +-----+ +-----+ | | IP Tables |->-+ |--+ +-----------+ | UDP +---------+ | | | +-->---->-----| Unreach |------>------>-------------+ | | +---------+ | +-------<---------<--------<---------<----------<----------<------+ <==================== IP Personality ====================> La cible PERS peut modifier les paquets qui lui sont passés par l'architecture _n_e_t_f_i_l_t_e_r. Aussi, il est logique de l'utiliser au sein de la table mangle spécialisée dans la modification des paquets. Cette table accède à deux des points d'entrée de _n_e_t_f_i_l_t_e_r, PRE_ROUTING et LOCAL_OUT. Afin de pouvoir réécrire correctement les connexions, le module PERS a besoin de voir les deux sens d'une connexion (nous verrons pourquoi par la suite). Pour ce faire, on utilise deux règles configurées de manière identique mais dont les critères sources et destinations sont symétriques. Pour les paquets routés, les deux règles doivent se situer au le point d'entrée PRE_ROUTING, puisque les paquets des deux directions sont d'origine extérieure à la machine locale. En revanche en ce qui concerne les communications avec la machine locale, si les paquets qui lui sont envoyés passent bien par le point d'entrée PRE_ROUTING, il n'en est pas de même pour les paquets qu'elle émet, ceux ci passant par LOCAL_OUT. Pour chacune des règles utilisées pour réécrire un type de communication, on précise au module si l'on souhaite protéger la destination de la règle ou sa source à l'aide d'une option. En effet selon le sens du paquet, certaines réécritures ne sont pas faites de la même manière. 33..22..11.. CCaass dd''uunn ppaaqquueett TTCCPP Si le paquet est destiné à la machine locale (c'est une option de la target), on commence par l'envoyer dans le code de génération des leurres : là le pseudo-code de la section tcp_decoy du fichier de config détermine si le paquet peut continuer tel quel, ou sinon (c'est-à-dire si le paquet a été identifié comme étant pathologique), s'il faut répondre, avec un leurre construit en fonction du paquet. Si le paquet continue, il peut être modifié de plusieurs façons. En particulier, le sens de circulation, qu'on peut déterminer grâce aux informations du module conntrack d'_i_p_t_a_b_l_e_s et aux paramètres de la règle en cours, définit le sens de la réécriture. Les altérations possibles sont : · llaa rrééééccrriittuurree ddeess nnuumméérrooss ddee ssééqquueennccee :: si l'on veut pouvoir simuler des générateurs de numéros de séquence initiaux, on veut aussi que ce qui suit l'établissement d'une connexion fonctionne convenablement. Il faut donc réécrire les numéros de séquence et d'acquittement de tous les paquets d'une connexion dont on a changé l'ISN. La première réécriture se fait au moment du choix de l'ISN par l'un des générateurs de PERS (le fichier de configuration détermine lequel et avec quels paramètres) : à ce moment, on sauvegarde la différence entre l'ISN original et celui choisi par PERS. Cette différence entre les numéros de séquence utilisés par les deux parties restant constante, il suffit de l'ajouter aux numéros de séquence dans un sens et de la soustraire aux acquittements dans l'autre ; · llaa rrééééccrriittuurree ddeess ttaaiilllleess ddee ffeennêêttrree :: la taille de fenêtre initiale étant un élément caractéristique, nous voulons pouvoir la contrôler. Mais comme dans le cas des numéros de séquence, il faut ensuite assumer ce choix et limiter la taille de fenêtre en conséquence ; · llaa rrééééccrriittuurree ddeess ooppttiioonnss :: lorsqu'une connexion est établie, les piles TCP échangent des informations utiles par le biais d'options : ce sont des champs optionnels de l'en-tête TCP, placés entre l'en-tête normale et les données. Le type des options utilisées et leur ordre est un élément caractéristique que nous pouvons modifier : c'est ce qui est fait en interprétant le pseudo- code de la section tcp_options. Ce code effectue des tests sur le type et la valeur des options, ainsi que sur les flags du paquet TCP, pour construire un nouveau bloc d'options qui remplace l'ancien dans le paquet TCP. 33..22..22.. CCaass dd''uunn ppaaqquueett UUDDPP Les paquets UDP qui sont simplement routés sont ignorés. Ceux qui sont destinés à la machine locale sont examinés pour vérifier qu'ils sont bien destinés à un port UDP ouvert : si c'est bien le cas, ils continuent leur chemin tels quels ; dans le cas contraire, ils sont détruits et PERS prend en charge l'émission d'un message ICMP de type "Port Unreachable", car nmap examine les caractéristiques de ce message. Ce genre de message est un paquet IP contenant une en-tête ICMP suivie du début du paquet IP ayant provoqué l'erreur. Le fichier de configuration permet de contrôler chacun des éléments du paquet utilisés par nmap pour identifier un système. 33..22..33.. PPaarrttiiee ccoommmmuunnee ddeess ppaacckkeettss IIPP Après la réécriture potentielle des paquets UDP/TCP, l'ensemble des paquets IP peuvent également être modifés. Une seule modification est apportée pour le moment et consiste à modifier l'identifiant du paquet (IP ID) pour une valeur générée selon un modèle prédéfini (de manière analogue aux numéros de séquences TCP). 44.. CCoonnffiigguurraattiioonn La configuration de PERS s'effectue en espace utilisateur à l'aide de la commande _i_p_t_a_b_l_e_s et d'une bibliothèque dynamique associée permettant de lui passer tous ses paramètres spécifiques. Cette bibliothèque ajoute à _i_p_t_a_b_l_e_s de nouvelles options applicables à chaque règle dont la cible est PERS ; l'une de ces options permet l'utilisation d'un fichier de configuration détaillé regroupant l'ensemble des paramètres nécessaires à l'émulation d'un système d'exploitation particulier. Via l'utilisation de fichiers de configuration différents pour chaque règle différente on peut donc très librement choisir d'émuler un système particulier en fonction d'adresses sources et destinations, de l'interface, et autres critères de sélection dans les règles. 44..11.. OOppttiioonnss eenn lliiggnnee ddee ccoommmmaannddee Les options de la ligne commande sont passées à la cible lors de l'ajout d'une règle l'utilisant par exemple avec une syntaxe du type : iptables -A -s -d -j PERS [Se référer à la documentation d'_i_p_t_a_b_l_e_s pour plus de détails sur la syntaxe globale] Les options reconnues par la bibliothèque sont : · _-_t_w_e_a_k _{_s_r_c_|_d_s_t_} : Cette option permet de spécifier le sens de réécriture pour la règle considérée. Si elle vaut src cela signifie que l'on souhaite protéger la source des paquets (et ainsi on va par exemple réécrire les numéros de séquence des paquets d'une connexion). Si elle vaut dst, alors on souhaite protéger la destination de la règle (et ainsi on réécrirait par exemple les acquittements de la connexion). · _-_l_o_c_a_l : Cete option spécifie que la source ou la destination de la règle (selon la valeur de l'option tweak) est locale, ce qui a pour effet d'activer les modules "decoy" et "udp" (si ceux-ci sont définis dans le fichier de configuration) permettant ainsi de tromper complètement des outils de type nmap en local. · _-_c_o_n_f _<_f_i_c_h_i_e_r_> : Cette option permet de spécifier le fichier de configuration à utiliser pour le système émulé au sein de cette règle (cf ci après). 44..22.. FFiicchhiieerr ddee ccoonnffiigguurraattiioonn Les paramètres concernant l'émulation d'un système particulier se définissent au sein d'un fichier. Ce fichier utilise une syntaxe proche de named.conf, inspirée du langage C. Les options de configuration sont regroupées dans des blocs (délimités par des { et }) et chaque bloc de configuration correspond à un type de réécriture différent. Chaque option est constituée d'un identifiant suivi d'un ou plusieurs arguments et terminée par un symbole ;. Les options et les blocs peuvent être spécifiés dans n'importe quel ordre. 44..22..11.. IIddeennttiiffiiccaattiioonn Le premier élément d'un fichier de configuration est une identification du système qu'il décrit. Il s'agit d'une chaîne d'au plus 20 caractères décrivant le système. La syntaxe est la suivante : id "FakeOS"; 44..22..22.. PPaarraammèèttrreess ggéénnéérriiqquueess TTCCPP Ces paramètres sont regroupés au sein d'une section nommée _t_c_p. Exemple : tcp { incoming yes; outgoing no; max-window 65536; } Le paramètre _i_n_c_o_m_i_n_g définit si l'on souhaite activer les modifications opérant sur des connexions TCP (ISN, taille de fenêtre, et Options) pour les connexions entrantes vers la zone protégée. Il peut prendre les valeurs _y_e_s ou _n_o. Le paramètre _o_u_t_g_o_i_n_g est analogue pour les connexions sortantes de la zone protégée. Le paramètre _m_a_x_-_w_i_n_d_o_w contrôle la réécriture de la taille de fenêtre sur les paquets TCP des connexions correpondant aux réglages précédents. Si il est défini à une valeur non nulle, alors pour toute nouvelle connexion dont la taille de fenêtre lui est supérieure, un décalage est calculé et la taille de fenêtre est ramenée à une valeur inférieure à ce paramètre sur toute la durée de la connexion. 44..22..33.. PPaarraammèèttrreess ddee ggéénnéérraatteeuurr ddee nnuumméérrooss ddee ssééqquueennccee Ces paramètres sont regroupés au sein d'une section nommée _t_c_p___i_s_n. Exemple : tcp_isn { type random-inc 10000; initial-value 2600; } Le paramètre _t_y_p_e décrit le type de générateur à émuler ainsi qu'une éventuelle option de cet émulation. Les types suivants sont implémentés : · _f_i_x_e_d_-_i_n_c _<_n_u_m_b_e_r_> : Il s'agit du générateur le plus simple. Le numéro de séquence initial de chaque connexion est tout simplement incrémenté d'une valeur constante (passée en argument) à chaque nouvelle connexion. L'utilisation de la valeur 0 comme incrément permet d'émuler les systèmes utilisant des numéros de séquence initiaux constants. · _r_a_n_d_o_m_-_i_n_c _<_n_u_m_b_e_r_> : Il s'agit d'un générateur semi-aléatoire. A chaque nouvelle connexion le numéro de séquence initial est incrémenté d'une valeur aléatoire entre 0 et le paramètre fourni. C'est le type de générateur utilisé sur les systèmes Linux, FreeBSD, etc... La robustesse d'un tel générateur est déterminé par la taille du paramètre. · _t_r_u_e_-_r_a_n_d_o_m : Il s'agit d'un générateur complètement aléatoire. A chaque nouvelle connexion, le numéro de séquence est généré de manière purement aléatoire (en utilisant le générateur aléatoire à entropie variable du noyau). · _b_u_i_l_t_i_n : Il s'agit du générateur de base du système courant. Sous Linux il s'agit donc d'un gérérateur à incréments aléatoires. · _t_i_m_e_-_d_e_p _<_n_u_m_b_e_r_> : Il s'agit d'un générateur dépendant du temps. Le nombre passé en paramètre indique la fréquence de progression du générateur (en Hz). Par exemple, une valeur de 25000 permet d'implémenter le générateur décrit dans la RFC 793 : le numéro de séquence initial est alors incrémenté de 1 toutes les 4 micro- secondes. (la granularité du générateur dépend toutefois de la précsion des "ticks" du système, 100 Hz par défaut sous linux/x86) Le paramètre _i_n_i_t_i_a_l_-_v_a_l_u_e décrit la valeur initiale à utiliser pour le générateur de numéro de séquence. Il peut s'agir d'une valeur numérique ou bien du mot-clé _r_a_n_d_o_m qui choisira une valeur aléatoire lors de l'insertion de la règle. Ce paramètre a peu d'importance pour les types de générateurs peu prédictibles. 44..22..44.. PPaarraammèèttrreess ddee ggéénnéérraatteeuurr dd''IIddeennttiiffiiaannttss IIPP Ces paramètres sont regroupés au sein d'une section nommée _i_p___i_d. Exemple : ip_id { type broken-inc 1; initial-value 2600; } Le paramètre _t_y_p_e décrit le type de générateur à émuler ainsi qu'une éventuelle option de cet émulation. Les même types que pour les générateurs de numéros de séquences sont acceptés, et un choix supplémentaire, _b_r_o_k_e_n_-_i_n_c _n_u_m_b_e_r est disponible : il s'agit d'un compteur incrémenté de la valeur spécifiée à chaque utilisation, mais dont le résultat est stoqué dans le paquet au format "little endian", au lieu de l'ordre réseau. 44..22..55.. PPaarraammèèttrreess ddee rrééééoorrddoonnnnaanncceemmeenntt ddeess ooppttiioonnss Ces paramètres sont regroupés au sein d'une section nommée _t_c_p___o_p_t_i_o_n_s. Exemple : tcp_options { keep-unknown yes; keep-unused no; isolated-packets yes; timestamp-scale 100; code { } } Cette section définit comment les options TCP d'un paquet doivent être réécrites. La sous-section _c_o_d_e contient un programme dans un langage proche du C (décrit par la suite) qui est compilé par le module _l_i_b_i_p_t___P_E_R_S_._s_o. Ce code est passé à la machine virtuelle qui remplit le buffer d'options constituant son état au fer et à mesure de l'exécution. Lorsque l'exécution est achevée, le buffer d'options résultant est utilisé pour remplacer les options initiales du paquet. Le paramètre _k_e_e_p_-_u_n_k_n_o_w_n spécifie si les options "inconnues" présentes dans le paquet initial, et donc non manipulables par le programme de réécriture, doivent être rajoutées à la fin du buffer final afin d'être présentes dans le paquet final. Ce paramètre peut prendre les valeurs _y_e_s ou _n_o. Le paramètre _k_e_e_p_-_u_n_u_s_e_d spécifie si les options du paquet original qui n'ont pas été utilisées (testées ou recopiées) pendant l'exécution du programme doivent être recopiées à la fin du buffer afin d'être présentes dans le paquet final. Ce paramètre peut prendre les valeurs _y_e_s ou _n_o. Ceci permet d'utiliser un code assez simple pour réordonner seulement quelques options tout en conservant toutes les options du paquet original. Le paramètre _i_s_o_l_a_t_e_d_-_p_a_c_k_e_t_s spécifie si la réécriture des options doit être appliquée aux paquets n'appartenant à aucune connexion connue. Ce paramètre peut prendre les valeurs _y_e_s ou _n_o (valeur par défaut). Le paramètre _t_i_m_e_s_t_a_m_p_-_s_c_a_l_e spécifie si l'on souhaite changer la valeur des "timestamp" TCP dans les paquets communiquant avec la machine locale. Il prend en argument la fréquence à utiliser pour les nouveaux "timestamp". (si la valeur est nulle ou égale à la fréquence nominale, l'option est ignorée). 44..22..66.. PPaarraammèèttrreess dduu lleeuurrrree TTCCPP Ces paramètres sont regroupés au sein d'une section nommée _t_c_p___d_e_c_o_y. Exemple : tcp_decoy { code { } } Cette section se résume à une sous-section _c_o_d_e analogue à celle de la section précédente, qui définit un certain nombre de tests à effectuer sur le paquet initial afin de reconnaître des paquets caractéristiques d'outils de détection et de répondre en conséquence. Le langage utilisé est le même que précédemment (décrit ci après). 44..22..77.. PPaarraammèèttrreess dduu lleeuurrrree UUDDPP Ces paramètres sont regroupés au sein d'une section nommée _u_d_p___u_n_r_e_a_c_h. Exemple : udp_unreach { reply yes; df no; max-len 56; tos 0; mangle-original { ip-len 21; ip-id same; ip-csum zero; udp-len 308; udp-csum zero; udp-data same; } } Le paramètre _r_e_p_l_y détermine si l'on souhaite émettre un message ICMP de type "port unreachable" lorsque l'on reçoit un message UDP pour un port non ouvert localement. Il peut être défini à _y_e_s ou à _n_o. Les autres paramètres de cette section s'appliquent si il est activé. Le paramètre _d_f spécifie si le bit "Don't Fragment" de l'entête IP du paquet ICMP doit être activé ou non. Le paramètre _m_a_x_-_l_e_n spécifie la longueur maximum du message ICMP généré en réponse. Le paramètre _t_o_s spécifie la valeur du champ "Type Of service" dans l'entête IP du paquet ICMP retourné. Lors de l'envoi d'un message ICMP de type "port unreachable", une portion du paquet initial est retournée dans le message. La section _m_a_n_g_l_e_-_o_r_i_g_i_n_a_l permet de définir des modifications de cette portion du message initial. Elle comprend les paramètres suivants : · _i_p_-_l_e_n _{_s_a_m_e_|_<_n_u_m_b_e_r_>_} : définit les modifications à apporter au champ longueur de l'entête IP du paquet initial. Peut valoir _s_a_m_e (dans ce cas la valeur est inchangée) ou une valeur numérique (dans ce cas elle remplace la valeur initiale). · _i_p_-_i_d _{_s_a_m_e_|_m_a_n_g_l_e_|_z_e_r_o_} : définit les modifications à apporter au champ id de l'entête IP du paquet initial. Peut valoir _s_a_m_e, _z_e_r_o (la valeur est mise à zéro), _m_a_n_g_l_e (la valeur est changée pour une valeur différente). · _i_p_-_c_s_u_m _{_s_a_m_e_|_m_a_n_g_l_e_|_z_e_r_o_} : définit les modifications à apporter au champ checksum de l'entête IP du paquet initial. Peut valoir _s_a_m_e, _z_e_r_o, _m_a_n_g_l_e. · _u_d_p_-_l_e_n _{_s_a_m_e_|_<_n_u_m_b_e_r_>_} : définit les modifications à apporter au champ longueur de l'entête UDP du paquet initial. Peut valoir _s_a_m_e ou une valeur numérique. · _u_d_p_-_c_s_u_m _{_s_a_m_e_|_m_a_n_g_l_e_|_z_e_r_o_} : définit les modifications à apporter au champ checksum de l'entête UDP du paquet initial. Peut valoir _s_a_m_e, _z_e_r_o, _m_a_n_g_l_e. · _u_d_p_-_d_a_t_a _{_s_a_m_e_|_m_a_n_g_l_e_|_z_e_r_o_} : définit les modifications à apporter au premier octet de la zone de donnée du paquet UDP initial. Peut valoir _s_a_m_e, _z_e_r_o, _m_a_n_g_l_e. 44..33.. LLaannggaaggee Les sections _t_c_p___o_p_t_i_o_n_s et _t_c_p___d_e_c_o_y possèdent toutes deux un paramètre code pouvant contenir un programme. Comme vu précédemment, ce programme est compilé par la bibliothèque dynamique de _i_p_t_a_b_l_e_s dans un pseudo-code interpété au sein du module noyau par une machine virtuelle simple. Celle-ci opère sur un paquet TCP en entrée et gère un état interne. Son état est composé de : · Un buffer de stockage d'options TCP · Plusieurs "registres" : _f_l_a_g_s, _m_s_s, _w_s_c_a_l_e, _w_i_n, _a_c_k et _d_f correspondants aux champs TCP du même nom pour un éventuel paquet de réponse. Le code de la section _t_c_p___o_p_t_i_o_n_s est appliqué à un paquet TCP entrant, et en fin de programme, le buffer d'options dans l'état de la machine virtuelle est utilisé comme nouvelle liste d'options TCP pour le paquet. Le code de la section _t_c_p___d_e_c_o_y est également appliqué à un paquet TCP entrant, mais le paquet n'est pas modifié. En fonction du type de terminaison du programme, un nouveau paquet peut être construit à partir de l'état de la machine virtuelle et renvoyé à la source du paquet initial. Le paquet inital peut aussi être rejeté, ou continuer son cheminement normal au sein de la cible. Ces programmes peuvent être décrits avec un langage de syntaxe proche du C. Des test conditionnels peuvent être effectués sur le paquet initial afin de gérer le comportement en fonction de son contenu. Un test a l'allure générale suivante : if (test) { } ou if (test) { } else { } Un test est constitué d'une ou plusieurs conditions, séparées par les opérateurs && et ||, et groupées par des parenthèses si besoin. Les conditions reconnues par le langage sont : · _o_p_t_i_o_n_(_o_p_t_) : Vrai si l'option _o_p_t est présente dans le paquet initial. · _f_l_a_g_s_(_f_l_a_g_) : Vrai si _f_l_a_g est activé dans le paquet initial. · _f_l_a_g_s_(_f_l_a_g_1_&_f_l_a_g_2_&_._._._) : Vrai si tous les flags spécifiés sont activés dans le paquet initial. · _f_l_a_g_s_(_f_l_a_g_1_|_f_l_a_g_2_|_._._._) : Vrai si au moins un des flags spécifiés est activé dans le paquet initial. · _a_c_k_(_v_a_l_) : Vrai si le champ acquittement de l'entête TCP du paquet initial vaut _v_a_l. · _l_i_s_t_e_n : Vrai si le port destination du paquet initial est ouvert sur la machine locale. Le langage dispose de plusieurs instructions afin de manipuler l'état interne de la machine virtuelle : · _c_o_p_y_(_o_p_t_) : Ceci provoque la copie de l'option _o_p_t du paquet initial vers le buffer d'options de l'état interne de la machine virtuelle si une telle option est disponible dans le paquet initial. · _i_n_s_e_r_t_(_o_p_t_, _v_a_l_) : Ceci permet d'insérer une option dans le buffer d'état en spécifiant sa valeur précisement. Une valeur numérique peut être passée, ou alors une expression de type _t_h_i_s _+ _<_n_u_m_b_e_r_> qui aura pour effet de donner à l'option la valeur qu'elle a dans le paquet initial ajoutée à la valeur spécifiée. Cette instruction ne supporte que les options _m_s_s, _w_s_c_a_l_e et _t_i_m_e_s_t_a_m_p (dans ce cas la valeur "this" correspond à la valeur courante utilisable pour le timestamp local). · _i_n_s_e_r_t_(_o_p_t_) : équivalent à _i_n_s_e_r_t_(_o_p_t_, _t_h_i_s_). · _s_e_t_(_a_r_g_, _v_a_l_) : Ceci permet de définir un des registres internes de la machine virtuelle. Les registres utilisables sont _f_l_a_g_s, _d_f, _w_i_n et _a_c_k. Pour le registre _f_l_a_g_s, l'argument doit être une combinaison valide de flags TCP comme pour les tests. Les arguments _d_f et _w_i_n peuvent avoir leur valeur définie relativement à leur valeur dans le paquet initial en utilisant la construction _t_h_i_s _+ _<_n_u_m_b_e_r_> vue précédemment. Cette construction est également valable pour le paramètre _a_c_k mais dans ce cas la valeur finale est relative au numéro de séquence initial (et non à son numéro d'acquitement). · _d_r_o_p, _a_c_c_e_p_t, et _r_e_p_l_y : Ces instructions provoquent l'arrêt du traitement du code en entrainant respectivement un abandon du paquet, une continuation de traitement au sein de la cible, et l'envoi d'une réponse construite à partir de l'état de la machine virtuelle. L'action par défaut en fin de programme est _a_c_c_e_p_t. Ce langage permet donc simplement de définir les comportements pour réordonnancer les options ainsi que pour générer des réponses sur mesure à des tests pathologiques pour tromper les outils de détection de systèmes d'exploitation. On peut faire les remarques suivantes : · Compte tenu que le code de la section _t_c_p___o_p_t_i_o_n n'agit que sur les options afin de le réordonner, seul le buffer d'option de l'état de la machine virtuelle est utilisé suite à l'exécution du code. En conséquence les tests _l_i_s_t_e_n et _a_c_k, et les instructions _i_n_s_e_r_t, _s_e_t, _d_r_o_p, _r_e_p_l_y, bien que valides, y ont peu d'intêret. · Les options supportées par les différents tests et conditions sont tirées des différentes RFC les détaillant ; en voivi les noms : _e_o_l, _n_o_p, _m_s_s, _w_s_c_a_l_e, _s_a_c_k_O_K, _s_a_c_k, _e_c_h_o, _e_c_h_o_r_e_p_l_y, _t_i_m_e_s_t_a_m_p, _p_o_c_O_K, _p_o_c_S_P, _C_C, _C_C_._N_E_W, _C_C_._E_C_H_O, _a_c_r_e_q, _a_c_d_a_t_a. · Les flags TCP supportés par les différents tests englobent la totalité des 12 bits utilisables et sont représentés par les noms suivants (du bit de poids faible au bit de poids fort) : _f_i_n, _s_y_n, _r_s_t, _p_u_s_h, _a_c_k, _u_r_g, _e_c_e, _c_w_r, _b_o_g_1, _b_o_g_2, _b_o_g_3, _b_o_g_4. 55.. EExxeemmppllee 55..11.. FFiicchhiieerr ddee ccoonnffiigguurraattiioonn Supposons que l'on souhaite réaliser un fichier de configuration pour émuler un AmigaOS. Pour cela, on dispose de la signature nmap suivante (se référer à la documentation de nmap pour plus de détails) : Fingerprint AmigaOS AmiTCP/IP 4.3 TSeq(Class=64K) T1(DF=N%W=1F0E%ACK=S++%Flags=AS%Ops=M) T2(Resp=N) T3(Resp=Y%DF=N%W=1F0E%ACK=O%Flags=A%Ops=) T4(DF=N%W=2000%ACK=O%Flags=R%Ops=) T5(DF=N%W=0%ACK=S++%Flags=AR%Ops=) T6(DF=N%W=0%ACK=O%Flags=R%Ops=) T7(DF=N%W=0%ACK=S%Flags=AR%Ops=) PU(DF=N%TOS=0%IPLEN=38%RIPTL=15C%RID=E%RIPCK=0%UCK=0%ULEN=134%DAT=E) Nous devons commencer la configuration par la définition d'un identifiant, comme suit : id "AmigaOS"; On souhaite réécrire les connexions TCP entrantes dans un premier temps, et ne pas agir sur les tailles de fenêtres (simplement tromper nmap), aussi utilise-t-on des valeurs génériques pour la section _t_c_p. tcp { incoming yes; outgoing no; max-window 32768; } La ligne _T_S_e_q de la signature nmap définit le type de générateur de numéro de séquence initial utiliser. Le paramètre important en est la classe du générateur. On peut rencontrer les classes suivantes : · _C_l_a_s_s_=_C : Générateur constant, correspondant à _f_i_x_e_d_-_i_n_c _0. · _C_l_a_s_s_=_T_D : Générateur dépendant du temps. On peut l'émuler avec un générateur à incrément fixe faible de manière à satisfaire les paramètres _g_c_d et _s_i. Il n'y a pas d'heuristiques particulière pour cela, il faut donc essayer plusieurs valeurs différentes. · _C_l_a_s_s_=_R_I : Générateur à incréments aléatoires. Ce générateur est émulé avec le mode _r_a_n_d_o_m_-_i_n_c. L'intervalle de recherche aléatoire est déterminé par la difficulté retournée par nmap (_g_c_d et _s_i). Même restrictions que précédemment. · _C_l_a_s_s_=_T_R : Générateur parfaitement aléatoire. émulé par _t_r_u_e_- _r_a_n_d_o_m. · _C_l_a_s_s_=_i_8_0_0, _C_l_a_s_s_=_6_4_K : Incrémentation fixes, respectivement de multiples de 800 et de 64000. Ici on utilisera donc : tcp_isn { type fixed-inc 64000; initial-value random; } Ensuite on trouve l'ensemble des tests TCP effectués par nmap au sein des lignes _T_x. La syntaxe de ces lignes est toujours la même et décrit l'éventuelle réponse reçue par nmap à son test. Tx(Resp=Y%DF=Y%W=XXXX%ACK=S++%Flags=AS%Ops=M) La signification des différents champs est la suivante : · _R_e_s_p : _Y si une réponse a été reçue, _N sinon. · _D_F : Indique si le bit "Don't Fragment" est positionné dans la réponse · _W : Indique la ou les tailles de fenêtres (séparées par des "|") attendues dans la réponse. · _A_C_K : Indique la valeur attendue pour l'acquittement dans la réponse. Peut valoir une valeur numérique ou _S pour indiquer le numéro de séquence du test, ou _S_+_+ pour indiquer le numéro de séquence du test plus un. · _F_l_a_g_s : Contient les flags TCP activés dans la réponse, sous la forme de leurs initiales repectives (_A pour _A_c_k, _S pour _S_y_n, ...). · _O_p_s : Contient la liste des options présentes suivant leur ordre au sein de la réponse, sous forme de leurs initiales repectives (_M pour _M_S_S, _N pour _N_O_P, ...) sauf pour _E qui signifie que l'option précédente est de la même valeur que dans le paquet de test. Si l'on souhaite émuler le système précisément, il faut déduire des différents résultats l'ordre des options à partir des réponses que nmap reçoit et des paquets initiaux auxquels elles correspondent. Ici, on n'a qu'une option donc la section correspondante est assez simple : tcp_options { keep-unknown yes; keep-unused no; isolated-packets yes; code { copy(mss); } } A ce stade le système ressemble un peu à celui émulé. En revanche sur les tests très précis, nos réponses ne statisferont pas nmap. Afin de le tromper complètement en local, on peut déduire des résultats aux tests TCP les réponses à lui retourner au sein du mode _d_e_c_o_y. Pour ce faire on utilise un "squelette" de code adapté aux tests de nmap que l'on complète afin de générer les réponses attendues : tcp_decoy { code { if (option(mss)) { if (listen) { if (flags(syn&ece)) { /* nmap test 1 */ } if (flags(null)) { /* nmap test 2 */ } if (flags(syn&fin&urg&push)) { /* nmap test 3 */ } if (ack(0) && flags(ack) && !flags(syn|push|urg|rst)) { /* nmap test 4 * } } else { if (flags(syn) && !flags(ack)) { /* nmap test 5 */ } if (ack(0) && flags(ack) && !flags(syn|push|urg|rst)) { /* nmap test 6 * } if (flags(fin&push&urg)) { /* nmap test 7 */ } } } } } Et il n'y a plus qu'à écrire le code pour chaque test, par exemple pour le premier set(df, 0); set(win, 0x1F0E); set(ack, this + 1); set(flags, ack|syn); insert(mss, this+1); reply; ou pour le second (pas de réponse) : drop; Enfin on peut également réagir localement (au sein de la section _u_d_p___d_e_c_o_y) au dernier test de nmap, le test UDP port-unreach (_P_U), qui a la syntaxe suivante : PU(DF=N%TOS=0%IPLEN=38%RIPTL=15C%RID=E%RIPCK=0%UCK=0%ULEN=134%DAT=E) La signification des différents champs est la suivante : · _R_e_s_p : Comme précédemment, correspond à l'option _r_e_p_l_y. · _D_F : Comme précédemment, correspond à l'option _d_f. · _T_O_S : Type Of Service, correspond à l'option _t_o_s. · _I_P_L_E_N : longueur du paquet ICMP. Peut-être définie via l'option _m_a_x_-_l_e_n. La réponse ICMP générée contient le début du paquet original (comportement recommandé par les RFC). Nmap essaie de déterminer si certaines portions en ont été changées au cours du traitement via les paramètres suivants, correspondants à ceux de la section _m_a_n_g_l_e_-_o_r_i_g_i_n_a_l. · _R_I_D_, _R_I_P_C_K_, _U_C_K_, _D_A_T : Ces paramètres définissent les modifications apportées à (respectivement) l'ID IP original, le checksum IP original, le checksum UDP original, les données originales. Chacun de ces paramètres peut avoir une des trois valeurs suivantes : _0 (mis à zéro), _F ("fucked", valeur changée), _E (égal). Ces paramètres correspondent aux options suivantes (même ordre) _i_p_-_i_d, _i_p_-_c_s_u_m, _u_d_p_-_c_s_u_m, _u_d_p_-_d_a_t_a qui peuvent prendre une des valeurs suivantes : _z_e_r_o, _m_a_n_g_l_e, _s_a_m_e. · _R_I_P_L_E_N_, _U_L_E_N : Ces paramètres décrivent les longeurs initiales des paquets IP et UDP, correspondant aux options _i_p_-_l_e_n et _u_d_p_-_l_e_n. Elles peuvent être définies à une valeur quelconque ou à _s_a_m_e pour conserver les valeurs originales. Ici, on pourrait donc utiliser ce qui suit : udp_unreach { reply yes; df no; max-len 56; tos 0; mangle-original { ip-len 348; ip-id same; ip-csum zero; udp-len 308; udp-csum zero; udp-data same; } } Il n'y a plus qu'à tester ! Un tel fichier peut par la suite être amélioré et optimisé afin d'être à la fois plus fiable (le réordonnancement d'option et le générateur de numéros de séquence initiaux ne sont pas simples à "deviner") et plus performant (regrouper les tests, etc.). 55..22.. RRéésseeaauu ddee tteesstt Afin de démontrer quelques unes des capacités du module IP Personality, plaçons nous dans le cadre de deux réseaux réduits à une machine, reliés via une machine routeur où tourne le module. La configuration esr la suivante : +---------+ +---------+ +---------+ | suskind |<--------->| dse2 |<--------->| dse1 | +---------+ +---------+ +---------+ Les systèmes d'exploitation utilisés pour les tests sont sur chacune des machines : · suskind : FreeBSD-2.2.8-RELEASE. · dse1 : Linux 2.2.14. · dse2 : Linux 2.3.99pre6 (ippersonality). On peut tout de suite vérifier que ces OS sont mutuellement détectables à l'aide de nmap par exemple, à partir de chacune des machines. (on a laissé les détails afin de voir en quoi ils changent par la suite). Si l'on effectue un nmap de suskind vers dse2 : TCP Sequence Prediction: Class=random positive increments Difficulty=2119945 (Good luck!) Sequence numbers: 59CEAA9C 5987D082 59CC67D4 59598903 5983CC3D 5971B98C Remote OS guesses: Linux 2.3.49 x86, Linux 2.3.99-pre2 x86 OS Fingerprint: TSeq(Class=RI%gcd=1%SI=205909) T1(Resp=Y%DF=Y%W=7C70%ACK=S++%Flags=AS%Ops=MNNTNW) T2(Resp=N) T3(Resp=Y%DF=Y%W=7C70%ACK=S++%Flags=AS%Ops=MNNTNW) T4(Resp=Y%DF=Y%W=0%ACK=O%Flags=R%Ops=) T5(Resp=Y%DF=Y%W=0%ACK=S++%Flags=AR%Ops=) T6(Resp=Y%DF=Y%W=0%ACK=O%Flags=R%Ops=) T7(Resp=Y%DF=Y%W=0%ACK=S++%Flags=AR%Ops=) PU(Resp=Y%DF=Y%TOS=C0%IPLEN=164%RIPTL=148%RID=E%RIPCK=E%UCK=E%ULEN=134%DAT=E) On observe le même résultat avec un nmap de dse1 vers dse2. Si l'on effectue un nmap de dse1 vers suskind : TCP Sequence Prediction: Class=random positive increments Difficulty=9819 (Worthy challenge) Sequence numbers: 3B1E1359 3B1F0409 3B1F9BAB 3B201E56 3B20B8D2 3B217357 Remote operating system guess: FreeBSD 2.2.1 - 3.2 OS Fingerprint: TSeq(Class=RI%gcd=1%SI=265B) T1(Resp=Y%DF=Y%W=403D%ACK=S++%Flags=AS%Ops=MNWNNT) T2(Resp=N) T3(Resp=Y%DF=Y%W=403D%ACK=S++%Flags=AS%Ops=MNWNNT) T4(Resp=Y%DF=N%W=4000%ACK=O%Flags=R%Ops=) T5(Resp=Y%DF=N%W=0%ACK=S++%Flags=AR%Ops=) T6(Resp=Y%DF=N%W=0%ACK=O%Flags=R%Ops=) T7(Resp=Y%DF=N%W=0%ACK=S%Flags=AR%Ops=) PU(Resp=Y%DF=N%TOS=0%IPLEN=38%RIPTL=148%RID=F%RIPCK=F%UCK=0%ULEN=134%DAT=E) On se donne maintenant 3 fichiers d'émulation de systèmes d'exploitation, soit _a_m_i_g_a_o_s_._c_o_n_f, _l_i_n_u_x_._c_o_n_f, et _w_i_n_9_x_._c_o_n_f. On décide de faire passer dse2 pour une machine windows auprès de suskind. Il suffit d'utiliser les deux ligne suivantes (sur dse2) : iptables -t mangle -A PREROUTING -s suskind -d dse2 -j PERS --tweak dst \ --local --conf win9x.conf iptables -t mangle -A OUTPUT -s dse2 -d suskind -j PERS --tweak src \ --local --conf win9x.conf On décide ensuite de faire passer dse2 pour une machine amiga auprès de dse1. Il suffit de rajouter les deux lignes suivantes : iptables -t mangle -A PREROUTING -s dse1 -d dse2 -j PERS --tweak dst \ --local --conf amigaos.conf iptables -t mangle -A OUTPUT -s dse2 -d dse1 -j PERS --tweak src \ --local --conf amigaos.conf Pour utiliser le rôle de routeur de la machine on veut également modifier la manière dont dse1 voit suskind, en faisant passer suskind pour une machine sous Linux. iptables -t mangle -A PREROUTING -s suskind -d dse1 -j PERS --tweak src \ --conf linux.conf iptables -t mangle -A PREROUTING -s dse1 -d suskind -j PERS --tweak dst \ --conf linux.conf Voyons maintenant ce que donnent les même tests que précédemment (avec nmap). Si l'on effectue un nmap de suskind vers dse2 : TCP Sequence Prediction: Class=trivial time dependency Difficulty=0 (Trivial joke) Sequence numbers: A97ECB1D A97ECB1F A97ECB21 A97ECB23 A97ECB25 A97ECB27 Remote operating system guess: Windows NT4 / Win95 / Win98 OS Fingerprint: TSeq(Class=TD%gcd=2%SI=0) T1(Resp=Y%DF=Y%W=2017%ACK=S++%Flags=AS%Ops=M) T2(Resp=Y%DF=N%W=0%ACK=S%Flags=AR%Ops=) T3(Resp=Y%DF=Y%W=2017%ACK=S++%Flags=AS%Ops=M) T4(Resp=Y%DF=N%W=0%ACK=S++%Flags=R%Ops=) T5(Resp=Y%DF=N%W=0%ACK=S++%Flags=AR%Ops=) T6(Resp=Y%DF=N%W=0%ACK=S++%Flags=R%Ops=) T7(Resp=Y%DF=N%W=0%ACK=S++%Flags=AR%Ops=) PU(Resp=N) Si l'on effectue un nmap de dse1 vers dse2 : TCP Sequence Prediction: Class=64K rule Difficulty=1 (Trivial joke) Sequence numbers: D997B378 D998AD78 D999A778 D99AA178 D99B9B78 D99C9578 Remote operating system guess: AmigaOS AmiTCP/IP 4.3 OS Fingerprint: TSeq(Class=64K) T1(Resp=Y%DF=N%W=1F0E%ACK=S++%Flags=AS%Ops=M) T2(Resp=N) T3(Resp=Y%DF=N%W=1F0E%ACK=O%Flags=A%Ops=) T4(Resp=Y%DF=N%W=2000%ACK=O%Flags=R%Ops=) T5(Resp=Y%DF=N%W=0%ACK=S++%Flags=AR%Ops=) T6(Resp=Y%DF=N%W=0%ACK=O%Flags=R%Ops=) T7(Resp=Y%DF=N%W=0%ACK=S%Flags=AR%Ops=) PU(Resp=Y%DF=N%TOS=0%IPLEN=38%RIPTL=15C%RID=E%RIPCK=0%UCK=0%ULEN=134%DAT=E) Si l'on effectue un nmap de dse1 vers suskind : TCP Sequence Prediction: Class=random positive increments Difficulty=188907 (Good luck!) Sequence numbers: 32BD32 393D33 3B87EE 3FE6A3 4AC5E7 4F9533 No OS matches for host (If you know what OS is running on it, see http://www.insecure.org/cgi-bin/nmap-submit.cgi). TCP/IP fingerprint: TSeq(Class=RI%gcd=1%SI=2EF4C) TSeq(Class=RI%gcd=1%SI=2EF18) TSeq(Class=RI%gcd=1%SI=2E1EB) T1(Resp=Y%DF=Y%W=403D%ACK=S++%Flags=AS%Ops=MNNTNW) T2(Resp=N) T3(Resp=Y%DF=Y%W=403D%ACK=S++%Flags=AS%Ops=MNNTNW) T4(Resp=Y%DF=N%W=4000%ACK=O%Flags=R%Ops=) T5(Resp=Y%DF=N%W=0%ACK=S++%Flags=AR%Ops=) T6(Resp=Y%DF=N%W=0%ACK=O%Flags=R%Ops=) T7(Resp=Y%DF=N%W=0%ACK=S%Flags=AR%Ops=) PU(Resp=Y%DF=N%TOS=0%IPLEN=38%RIPTL=148%RID=F%RIPCK=F%UCK=0%ULEN=134%DAT=E) On constate bien que dans le cas de la machine locale dse2 on peut complètement tromper nmap. En revanche en mode "routeur", les paramètres sur lesquels on joue le perturbent, mais ne suffisent pas à lui faire détecter un autre système. 66.. PPsseeuuddoo CCooddee 66..11.. GGéénnéérraalliittééss Nous avons implémenté une machine virtuelle simple au sein du noyau. Celle-ci opère sur un paquet TCP en entrée et gère un état interne. Son état est composé de : · Un pointeur d'instruction dans le code. · Un buffer de stockage d'options TCP. · Plusieurs "registres" : _f_l_a_g_s, _m_s_s, _w_s_c_a_l_e, _w_i_n, _a_c_k et _d_f correspondants aux champs TCP du même nom pour un éventuel paquet de réponse. Le code executé par la machine virtuelle est composé d'instructions sur 32 bits (en ordre de la machine) regroupant un mnémonique (sur 8 bits), une option (sur 4 bits) et un opérande (sur 20 bits), comme visible ci après. 0 7 8 11 12 31 +---------------+--------+---------------------------------------+ | Mnemonic | Option | Operand | +---------------+--------+---------------------------------------+ 66..22.. IInnssttrruuccttiioonnss 66..22..11.. TTEESSTT CCooddee :: 01 Effectue un test sur l'objet défini par l'option. Si le test est vrai, le pointeur d'instruction passe de l'instruction _i à l'instruction _i_+_2. Si le test est faux, l'exécution se continue à l'instruction _i_+_1. Les options des tests sont les suivantes : · _T_C_P _O_p_t_i_o_n (0) : Vrai si l'option TCP dont le code est passé en opérande est défini dans le paquet initial. · _A_n_y _T_C_P _F_l_a_g_s (1) : Vrai si un des flags TCP passés en opérande est activé dans les flags TCP du paquet initial. · _A_l_l _T_C_P _F_l_a_g_s (2) : Vrai si tous les flags TCP passés en opérande sont activés dans les flags TCP du paquet initial. · _A_c_k (3) : Vrai si la valeur de l'acquittement du paquet initial vaut l'opérande. · _L_i_s_t_e_n (4) : Vrai si le port destination du paquet initial est ouvert sur la machine locale. 66..22..22.. JJMMPP CCooddee :: 02 Continue l'exécution à l'instruction dont le numéro est l'opérande. 66..22..33.. PPUUTT CCooddee :: 03 Insère une option TCP dans le buffer d'options TCP. L'option TCP insérée est l'opérande, sa source est déterminée par l'option de l'instruction. Les options sont les suivantes : · _C_o_p_y (0) : L'option insérée est copiée à partir du paquet initial si elle y est définie. · _I_n_s_e_r_t (1) : L'option insérée est copiée à partir des registres de la machine virtuelle. Uniquement valable pour les options _m_s_s, _w_s_c_a_l_e and _t_i_m_e_s_t_a_m_p. 66..22..44.. SSEETT CCooddee :: 04 Définit la valeur d'un registre de la machine virtuelle. Le registre concerné et le type d'affectation sont déterminés par l'option. La valeur utilisée est l'opérande. Les options acceptées sont les suivantes : · _f_l_a_g_s (0) : Définit le registre _f_l_a_g_s à la valeur de l'opérande. · _a_c_k (1) : Définit le registre _a_c_k (acquittement) à la valeur de l'opérande. · _d_f (2) : Définit le registre _d_f (bit "Don't Fragment" de l'entête IP) à la valeur de l'opérande. · _w_i_n (3) : Définit le registre _w_i_n (taille de fenêtre) à la valeur de l'opérande. · _m_s_s (4) : Définit le registre _m_s_s (taille de segment TCP maximale) à la valeur de l'opérande. · _w_s_c_a_l_e (5) : Définit le registre _w_s_c_a_l_e (mise à l'échelle de la fenêtre) à la valeur de l'opérande. · _t_i_m_e_s_t_a_m_p (6) : Définit le registre _t_i_m_e_s_t_a_m_p (valeur locale du timestamp) à la valeur de l'opérande. · _r_e_l_a_t_i_v_e _a_c_k (9) : Définit le registre _a_c_k (acquittement) à la valeur de l'opérande ajoutée au numéro de séquence du paquet initial. · _r_e_l_a_t_i_v_e _d_f (10) : Définit le registre _d_f (bit "Don't Fragment" de l'entête IP) à la valeur de l'opérande ajoutée à celle de la valeur de ce champ dans le paquet initial. · _r_e_l_a_t_i_v_e _w_i_n (11) : Définit le registre _w_i_n (taille de fenêtre) à la valeur de l'opérande ajoutée à la taille de fenêtre du paquet initial. · _r_e_l_a_t_i_v_e _m_s_s (12) : Définit le registre _m_s_s (taille de segment TCP maximale) à la valeur de l'opérande ajoutée à la valeur mss du paquet initial (si définie). · _r_e_l_a_t_i_v_e _w_s_c_a_l_e (13) : Définit le registre wscale (mise à l'échelle de la fenêtre) à la valeur de l'opérande ajoutée à la valeur wscale du paquet initial (si définie). · _r_e_l_a_t_i_v_e _t_i_m_e_s_t_a_m_p (14) : Définit le registre _t_i_m_e_s_t_a_m_p (valeur locale du timestamp) à la valeur de l'opérande ajoutée à la valeur courante utilisable pour le timestamp. 66..22..55.. RREETT CCooddee :: 05 Termine l'exécution du programme en retournant l'opérande. Les opérandes acceptés sont les suivants : · _A_c_c_e_p_t (1) : Termine l'exécution et demande l'acceptation du paquet pour continuer son traitement. · _D_r_o_p (2) : Termine l'exécution et demande l'abandon du paquet. · _R_e_p_l_y (3) : Termine l'exécution et demande l'envoi d'une réponse basée sur l'état de la machine virtuelle. 66..33.. OOppttiioonnss TTCCPP Pour les différentes instructions acceptant des options TCP, les options suivantes sont reconnues : · _e_o_l (0) · _n_o_p (1) · _m_s_s (2) · _w_s_c_a_l_e (3) · _s_a_c_k_O_K (4) · _s_a_c_k (5) · _e_c_h_o (6) · _e_c_h_o_r_e_p_l_y (7) · _t_i_m_e_s_t_a_m_p (8) · _p_o_c_O_K (9) · _p_o_c_S_P (10) · _C_C (11) · _C_C_._N_E_W (12) · _C_C_._E_C_H_O (13) · _a_c_r_e_q (14) · _a_c_d_a_t_a (15) 77.. OOuuttiillss ddee ddéévveellooppppeemmeenntt 77..11.. DDeebbooggaaggee Afin de pouvoir suivre le fonctionnement du module, un certain nombre d'informations de debogage peuvent être imprimées dans le buffer de messages du noyau au fer et à mesure de l'analyse et de la modification des paquets. Par défaut les messages de debogage sont désactivés, mais peuvent être activés par un sysctl, via le fichier _/_p_r_o_c_/_s_y_s_/_n_e_t_/_i_p_v_4_/_i_p___p_e_r_s_o_n_a_l_i_t_y___d_e_b_u_g. Le niveau de debogage est réglé par la valeur numérique associée à ce paramètre : Chaque bit correspond à un des sous modules ce qui permet de sélectionner finement les messages à afficher en combinant les bits voulus comme ci après : · _1 : Moteur central de la cible · _2 : Modifications de numéros de séquence · _4 : Modifications des options · _8 : Modifications des fenêtres · _1_6 : Leurres TCP locaux · _3_2 : Machine virtuelle · _6_4 : Leurres UDP locaux · _1_2_8 : Modifications des identifiants IP Exemple : echo 35 > /proc/sys/net/ipv4/ip_personality_debug 77..22.. OOssddeett Osdet est un outil de test tentant de découvrir le système d'exploitation utilisé. Il a été développé à partir des sources de nmap. Il effectue les mêmes tests que nmap, mais ce de manière séquentielle en affichant pour chaque test le paquet envoyé et la réponse éventuelle reçue (code adapté de tcpdump). Ceci permet d'analyser finemement le comportement de la pile IP et de constater le bon fonctionnement ou non du code noyau produit. Exemple d'utilisation : dse1:~# osdet -h usage: osdet [-t n[-N],...] [-p port] [-P port] [-S ip] [-h] host -p port Sets openport (defaults to 23 (telnet)). -P port Sets closedport (defaults to a random high port). -S ip Sets source Ip for scans if multihomed. -t ... Selects a subset of tests to perform. dse1:~# osdet -p 23 -P 234 dse2 OSDET v0.3 [using nmap backend version 2.53] Trying to detect remote os of dse2 [172.20.30.2]. (assuming port 23 is open and port 234 is closed) Using pcap filter: (icmp and dst host 172.20.30.1) or (tcp and src host 172.20.30.2 and dst host 172.20.30.1) * Test 1 (TCP to open port, SYN and BOGUS) Sending packet... ok: 172.20.30.1.50925 > 172.20.30.2.23: S 26F7D60A:26F7D60A(0) win 3072 (ttl 54, id 36252) Waiting for answer... ok: 172.20.30.2.23 > 172.20.30.1.50925: S 6ECE0057:6ECE0057(0) ack 26F7D60B win 7950 (ttl 255, id 59900) * Test 2 (TCP to open port, NULL) Sending packet... ok: 172.20.30.1.50926 > 172.20.30.2.23: . win 3072 (ttl 54, id 27188) Waiting for answer... no reply. * Test 3 (TCP to open port, SYN, FIN, URG and PUSH) Sending packet... ok: 172.20.30.1.50927 > 172.20.30.2.23: SFP 26F7D60A:26F7D60A(0) win 3072 urg 0 (ttl 54, id 28956) Waiting for answer... ok: 172.20.30.2.23 > 172.20.30.1.50927: . ack 2 win 7950 (ttl 255, id 60156) * Test 4 (TCP to open port, ACK 0) Sending packet... ok: 172.20.30.1.50928 > 172.20.30.2.23: . ack 0 win 3072 (ttl 54, id 7360) Waiting for answer... ok: 172.20.30.2.23 > 172.20.30.1.50928: R 0:0(0) win 8192 (ttl 255, id 60412) * Test 5 (TCP to closed port, SYN) Sending packet... ok: 172.20.30.1.50929 > 172.20.30.2.234: S 26F7D60A:26F7D60A(0) win 3072 (ttl 54, id 49268) Waiting for answer... ok: 172.20.30.2.234 > 172.20.30.1.50929: R 0:0(0) ack 26F7D60B win 0 (ttl 255, id 60668) * Test 6 (TCP to closed port, ACK 0) Sending packet... ok: 172.20.30.1.50930 > 172.20.30.2.234: . ack 0 win 3072 (ttl 54, id 53356) Waiting for answer... ok: 172.20.30.2.234 > 172.20.30.1.50930: R 0:0(0) win 0 (ttl 255, id 60924) * Test 7 (TCP to closed port, FIN, PUSH and URG) Sending packet... ok: 172.20.30.1.50931 > 172.20.30.2.234: FP 26F7D60A:26F7D60A(0) win 3072 urg 0 (ttl 54, id 60119) Waiting for answer... ok: 172.20.30.2.234 > 172.20.30.1.50931: R 0:0(0) ack 26F7D60A win 0 (ttl 255, id 61180) * Test 8 (UDP to closed port, expecting ICMP unreach) Sending packet... ok: 172.20.30.1.50932 > 172.20.30.2.234: udp 300 (ttl 60, id 36334) Waiting for answer... ok: 172.20.30.2 > 172.20.30.1: icmp: 172.20.30.2 udp port 234 unreachable (ttl 255, id 61436) * Test 9 (Initial Sequence Number) Sending paquets... 26F7D60B 26F7D60C 26F7D60D 26F7D60E 26F7D60F 26F7D610; last is: 172.20.30.1.50939 > 172.20.30.2.23: S 26F7D610:26F7D610(0) win 3072 (ttl 54, id 777) Waiting for answers... 9D128940[1] 9D138340[2] 9D147D40[3] 9D157740[4] 9D167140[5] 9D176B40[6]; last is: 172.20.30.2.23 > 172.20.30.1.50939: S 9D176B40:9D176B40(0) ack 26F7D611 win 32120 (DF) (ttl 64, id 0) * Nmap OS Fingerprint: TSeq(Class=64K) T1(Resp=Y%DF=N%W=1F0E%ACK=S++%Flags=AS%Ops=M) T2(Resp=N) T3(Resp=Y%DF=N%W=1F0E%ACK=O%Flags=A%Ops=) T4(Resp=Y%DF=N%W=2000%ACK=O%Flags=R%Ops=) T5(Resp=Y%DF=N%W=0%ACK=S++%Flags=AR%Ops=) T6(Resp=Y%DF=N%W=0%ACK=O%Flags=R%Ops=) T7(Resp=Y%DF=N%W=0%ACK=S%Flags=AR%Ops=) PU(Resp=Y%DF=N%TOS=0%IPLEN=38%RIPTL=15C%RID=E%RIPCK=0%UCK=0%ULEN=134%DAT=E) TCP Sequence Prediction: Class=64K rule Difficulty=1 (Trivial joke) * Remote OS Guess: AmigaOS AmiTCP/IP 4.3 77..33.. PPeerrsscccc La bibliothèque dynamique qui s'ajoute à iptables réalise l'analyse du fichier de configuration et la compilation du code en pseudo-code pour la machine virtuelle. Afin d'être sûr que l'interprétation de la configuration du code, et que le code généré était correct, nous avons réalisé un interprêteur de fichier de configuration et un compilateur/désassembleur de code. Cet outil peut également être utilisé pour simplement vérifier la bonne syntaxe d'un fichier de configuration ou tester la longueur d'un code compilé. Exemple d'utilisation : dse2:~# percc example.conf === config === id: Example isn initialized: yes, value=877764155 isn type: true-random rewrite way: ingoing outgoing keep unknown options: yes keep unused options: yes max window: 0 change options for isolated packets: yes udp-unreach: reply: yes df: yes max-len: 500 tos: 0 ip-len: 0 ip-id: same ip-csum: same udp-len: 0 udp-csum: same udp-data: mangle === interpreted code #0 === if (flags(syn)) { if (option(sackOK)) { copy(sackOK); } else { copy(nop); copy(nop); } copy(timestamp); copy(mss); } else { if (option(sack)) { copy(sack); } else { copy(nop); copy(nop); } copy(timestamp); } code: 15 instructions. === compiled code #0 === 0000: [01100002] TEST tcp_flags, syn 0001: [0200000B] JMP 000B 0002: [01000004] TEST tcp_option, sackOK 0003: [02000006] JMP 0006 0004: [03000004] PUT sackOK (copy) 0005: [02000008] JMP 0008 0006: [03000001] PUT nop (copy) 0007: [03000001] PUT nop (copy) 0008: [03000008] PUT timestamp (copy) 0009: [03000002] PUT mss (copy) 000A: [02000012] JMP 0012 000B: [01000005] TEST tcp_option, sack 000C: [0200000F] JMP 000F 000D: [03000005] PUT sack (copy) 000E: [02000011] JMP 0011 000F: [03000001] PUT nop (copy) 0010: [03000001] PUT nop (copy) 0011: [03000008] PUT timestamp (copy) asm: 18 instructions. === interpreted code #1 === if (option(mss)) { set(df, 0); if (listen) { if (flags(syn&ece)) { set(win, 7950); set(ack, this + 1); set(flags, syn|ack); insert(mss, this + 1); reply; } if (flags(null)) { drop; } if (flags(fin&syn&urg&push)) { set(win, 7950); set(ack, 2); set(flags, ack); reply; } if ((ack(0) && flags(ack)) && !flags(syn|urg|push|rst)) { set(win, 8192); set(ack, 2); set(flags, rst); reply; } } else { set(win, 0); if (flags(syn) && !flags(ack)) { set(ack, this + 1); set(flags, ack|rst); reply; } if ((ack(0) && flags(ack)) && !flags(syn|urg|push|rst)) { set(ack, 2); set(flags, rst); reply; } if (flags(fin&urg&push)) { set(ack, this + 0); set(flags, ack|rst); reply; } } } code: 53 instructions. === compiled code #1 === 0000: [01000002] TEST tcp_option, mss 0001: [0200003A] JMP 003A 0002: [04200000] SET df, 0 0003: [01400000] TEST listen 0004: [02000022] JMP 0022 0005: [01200042] TEST tcp_flags, syn&ece 0006: [0200000D] JMP 000D 0007: [04301F0E] SET win, 7950 0008: [04900001] SET ack, this + 1 0009: [04000012] SET flags, syn|ack 000A: [04C00001] SET mss, this + 1 000B: [03100002] PUT mss (insert) 000C: [05000003] RET reply 000D: [01100000] TEST tcp_flags, null 000E: [02000010] JMP 0010 000F: [05000002] RET drop 0010: [0120002B] TEST tcp_flags, fin&syn&urg&push 0011: [02000016] JMP 0016 0012: [04301F0E] SET win, 7950 0013: [04100002] SET ack, 2 0014: [04000010] SET flags, ack 0015: [05000003] RET reply 0016: [01300000] TEST ack, 0 0017: [0200003A] JMP 003A 0018: [01100010] TEST tcp_flags, ack 0019: [0200003A] JMP 003A 001A: [0110002E] TEST tcp_flags, syn|urg|push|rst 001B: [0200001D] JMP 001D 001C: [0200003A] JMP 003A 001D: [04302000] SET win, 8192 001E: [04100002] SET ack, 2 001F: [04000004] SET flags, rst 0020: [05000003] RET reply 0021: [0200003A] JMP 003A 0022: [04300000] SET win, 0 0023: [01100002] TEST tcp_flags, syn 0024: [0200002B] JMP 002B 0025: [01100010] TEST tcp_flags, ack 0026: [02000028] JMP 0028 0027: [0200002B] JMP 002B 0028: [04900001] SET ack, this + 1 0029: [04000014] SET flags, ack|rst 002A: [05000003] RET reply 002B: [01300000] TEST ack, 0 002C: [02000035] JMP 0035 002D: [01100010] TEST tcp_flags, ack 002E: [02000035] JMP 0035 002F: [0110002E] TEST tcp_flags, syn|urg|push|rst 0030: [02000032] JMP 0032 0031: [02000035] JMP 0035 0032: [04100002] SET ack, 2 0033: [04000004] SET flags, rst 0034: [05000003] RET reply 0035: [01200029] TEST tcp_flags, fin&urg&push 0036: [0200003A] JMP 003A 0037: [04900000] SET ack, this + 0 0038: [04000014] SET flags, ack|rst 0039: [05000003] RET reply asm: 58 instructions. 88.. BBiibblliiooggrraapphhiiee · J. Postel. _I_n_t_e_r_n_e_t _P_r_o_t_o_c_o_l, Request for Comments 791. Network Working Group, 09/1981. · J. Postel. _I_n_t_e_r_n_e_t _C_o_n_t_r_o_l _M_e_s_s_a_g_e _P_r_o_t_o_c_o_l, Request for Comments 792. Network Working Group, 09/1981. · J. Postel. _T_r_a_n_s_m_i_s_s_i_o_n _C_o_n_t_r_o_l _P_r_o_t_o_c_o_l, Request for Comments 793. Network Working Group, 09/1981. · V. Jacobson, R. Braden. _T_C_P _E_x_t_e_n_s_i_o_n_s _f_o_r _L_o_n_g_-_D_e_l_a_y _P_a_t_h_s, Request for Comments 1072. Network Working Group, 10/1988. · J. Zweig, C. Partridge. _T_C_P _A_l_t_e_r_n_a_t_e _C_h_e_c_k_s_u_m _O_p_t_i_o_n_s, Request for Comments 1146. Network Working Group, 04/1990. · V. Jacobson, R. Braden, D. Borman. _T_C_P _E_x_t_e_n_s_i_o_n_s _f_o_r _H_i_g_h _P_e_r_f_o_r_m_a_n_c_e, Request for Comments 1323. Network Working Group, 05/1992. · T. Connolly, P. Amer, P. Conrad. _A_n _E_x_t_e_n_s_i_o_n _t_o _T_C_P _: _P_a_r_t_i_a_l _O_r_d_e_r _S_e_r_v_i_c_e, Request for Comments 1693. Network Working Group, 11/1994. · "Fyodor". _R_e_m_o_t_e _O_S _d_e_t_e_c_t_i_o_n _v_i_a _T_C_P_/_I_P _S_t_a_c_k _F_i_n_g_e_r_P_r_i_n_t_i_n_g, Phrack Magazine Volume 8, Issue 54, 12/1998. · R. Russel. _L_i_n_u_x _n_e_t_f_i_l_t_e_r _H_a_c_k_i_n_g _H_O_W_T_O, Linux Documentation Project, 05/2000.