Depuis plus de deux ans, le projet Honeynet propose régulièrement d'étudier des outils, des tactiques utilisés par des pirates lors d'intrusion pour démontrer la réalité de ces attaques, pour apprendre comment un pirate agit pour s'en protéger et plus généralement pour améliorer les technologies et méthodes de collecte d'information. Chaque mois, le Scan of the Month challenge propose l'analyse de trames réseaux suspectes. Le mois de mai a vu un autre challenge: le Reverse Challenge. Il s'agissait d'analyser un binaire retrouvé sur une machine piratée du projet Honeynet. Trente-cinq personnes ou équipes ont soumis leurs analyses au jury du projet et je vais vous faire part de cette expérience ayant moi même participé à ce concours.

Identifier le programme

Système d'exploitation

La commande file permet d'identifier le binaire nommé the-binary. Le nom du binaire ne nous aide pas beaucoup.

$ file the-binary
the-binary: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, stripped

Ce programme fonctionne sur un Unix SYSV pour plate-forme compatible Intel. C'est à dire sous Linux (Consulter /usr/include/elf.h pour plus d'info sur les entêtes ELF). Malheureusement pour nous, l'analyse est compliquée par le fait que les symboles ont été supprimés et le programme a été compilé en statique: La commande strip retire les informations insérées par le compilateur pour faciliter le debugage. L'option --static du compilateur a été utilisé pour inclure les fonctions qui se trouveraient dans des librairies sinon. Le programme devient indépendant du type de Linux (RedHat, Debian...) sur lequel il est installé.

Librairie et compilateur

Un examen des chaînes contenus dans le binaire, strings -a the-binary | less, révèle la version de la librairie @(#) The Linux C library 5.3.12 ainsi que celle du compilateur GCC: (GNU) 2.7.2.l.2. Attention, il est important d'utiliser l'option -a de strings pour parcourir l'intégralité du fichier. En l'oubliant, on rate la version de GCC qui se trouve dans la section .comment. On obtient une première indication sur le niveau technique du pirate: il sait supprimer les symboles mais n'a pas penser à supprimer les sections .note et .comment. Il semble ignorer que le programme peut être compressé (UPX) ou protégé (Burneye).

L'étape suivante est d'identifier le système d'exploitation d'origine. La libc 5.3.12 a été utilisée sur la Slackware 3.1 et les RedHat 4.x. Un des participants a automatisé la comparaison entre la librairie incluse dans le binaire et les librairies disponibles pour ces plate-formes. Méthode et outils sur /results/sol/sol-06/analysis.html#s1_2_1 On obtient les résultats suivants
Distribution Concordances Fonctions non résolues
Slackware 3.1 169 5
RedHat, 5.3.12-8 149 9
RedHat, 5.3.12-17 105 10
RedHat, 5.3.12-18.2 27 0
RedHat, 5.3.12-18.5 27 0
La librairie viendrait d'une Slackware 3.1 avec 97% de chance. Une recherche avec Google révèle la présence de nombreux binaires comportant la version 2.7.2.l.2 se trouvent dans la Slackware 3.1.

Table des symboles

Ayant identifié la version de la librairie, il sera possible d'identifier facilement les 169 fonctions trouvées précédement. Certains participants ont utilisé des scripts perl pour remplacer le nom dans le fichier source obtenu lors du desassemblage objdump -d the-binary. L'outil dress du projet Fenris, http://razor.bindview.com/tools/fenris/, permet de reconstruire une table des symboles. Cela va permettre d'avoir les noms des fonctions.

[kmaster@vectra fenris]$ ./dress -F fnprints.dat -F support/fn-libc5.dat -F support/fn-2.0.7.dat the-binary the-binary-dressed
dress - stripped static binary recovery tool by <lcamtuf@coredump.cx>
[+] Loaded 125589 fingerprints...
[*] Code section at 0x08048090 - 0x080675cc, offset 144 in the file.
[*] For your initial breakpoint, use *0x8048090
[+] Locating CALLs... 371 found.
[+] Matching fingerprints...
[*] Writing new ELF file:
[+] Cloning general ELF data...
[+] Setting up sections: .init .text .fini .rodata .data .ctors .dtors .bss .note .comment 
[+] Preparing new symbol tables...
[+] Copying all sections: .init .text .fini .rodata .data .ctors .dtors .bss .note .comment 
[+] All set. Detected fingerprints for 214 of 371 functions.

Désassembler

Le programme peut maintenant être désassemblé avec objdump -d the-binary ou tracé strace -ff si on est sur une machine isolée.

Les résultats

Le binaire est un agent dans un système de DDoS. Il intègre aussi des fonctions de backdoor, il permet des accès distants en tant qu'utilisateur root. Il se commande via douze fonctions qui permet de l'interroger, de lui fournir des adresses IP au sein desquels se trouvent celle du client (donc du pirate), des fonctions pour lancer des attaques de type SYN flood, des attaques par fragmentation UDP ou ICMP (Jolt 2), des attaques DNS par retour (On interroge de multiples DNS avec comme adresse source celle de la machine à ddoser). Les communications réseaux entre le client et l'agent sont cryptés de façon sommaire. Pour cette communication, le protocole 11 est utilisé. Il s'agit d'un protocole affecté à NVP, Network Voice Protocol, protocole obsolète de voix sur IP. Cette technique oblige l'agent à fonctionner en tant que root pour ouvrir une socket RAW. Si cela vous intéresse, un équivalent à été réécrit en C, il est disponible sur reverse/results/sol/sol-14/the-binary.c

Comment détecter ce programme ?

En local

Avec beaucoup de chance, on peut remarquer le programme [mingetty] qui est en fait notre programme the-binary.

ps axuw|grep mingetty
root       980  0.0  0.2  1388  356 tty1     S    Jul15   0:00 /sbin/mingetty tty1
root       981  0.0  0.2  1388  356 tty2     S    Jul15   0:00 /sbin/mingetty tty2
root       982  0.0  0.2  1388  356 tty3     S    Jul15   0:00 /sbin/mingetty tty3
root       983  0.0  0.2  1388  356 tty4     S    Jul15   0:00 /sbin/mingetty tty4
root       984  0.0  0.2  1388  356 tty5     S    Jul15   0:00 /sbin/mingetty tty5
root       985  0.0  0.2  1388  356 tty6     S    Jul15   0:00 /sbin/mingetty tty6
root     14306  0.0  0.0   240   44 ?        S    09:35   0:00 [mingetty]              
root     14323  0.0  0.4  1788  628 pts/4    R    09:38   0:00 grep mingetty
ps axuc
...
root     14306  0.0  0.0   240   44 ?        S    09:35   0:00 the-binary

Pour communiquer le programme utilise une socket en mode RAW, netstat permet de le détecter.

# netstat -lap|grep ^raw
raw        0      0 *:nvp                   *:*                     7           14306/[mingetty] 

On peut aussi bien utiliser lsof.

lsof -n|grep raw
the-binar 14306    root    0u   raw                         22807 00000000:000B->00000000:0000 st=07

Il reste encore la possibilité de surveiller les exécutables présents sur la machine.

De manière passive

Ce programme utilise un protocole IP non standard. Il peut être bloqué par le firewall, ce qui est recommandé d'ailleurs. Tout ce qui n'est pas explicitement autorisé doit être interdit. L'analyse des logs du firewall permet d'observer des tentatives de communication. L'IP spoofing peut aussi faire l'objet de détection.

Si vous disposez de la sonde de détection d'intrusion snort, http://www.snort.org, il est possible d'activer dans le fichier bad-traffic.rules une règle pour détecter les protocoles IP non standard, soit généralement tous protocoles autre que icmp(1), igmp(2), tcp(6), udp(17), gre(47), ipv6-crypt(50), ipv6-auth(51), ospf(89).
alert ip $EXTERNAL_NET any -> $HOME_NET any (msg:"BAD TRAFFIC Non-Standard IP protocol"; ip_proto:!1; ip_proto:!2; ip_proto:!6; ip_proto:!17; ip_proto:!47; ip_proto:!50; ip_proto:!51; ip_proto:!89; classtype:non-standard-protocol; sid:1620; rev:2;)

Une analyse statistique avec des outils comme RRDTool ou Netflow peut être nécessaire si le programme communique via un protocole légitime. Cet aspect est abordé sur http://www.securite.org/presentations/secip/

De manière active

On peut aussi scanner son réseau à la recherche de machine utilisant le protocole 11.

# nmap -sO -n -p 11 127.0.0.1

Starting nmap V. 2.54BETA37 ( www.insecure.org/nmap/ )
Interesting protocols on  (127.0.0.1):
Protocol   State       Name
11         open        nvp-ii                  

Nmap run completed -- 1 IP address (1 host up) scanned in 1 second

Quels sont les protections du programme contre l'analyse

Au niveau de la compilation, le programme ne contient pas d'information de debugage. Plus d'information aurait été supprimées avec sstrip http://www.muppetlabs.com/~breadbox/software/elfkickers.html. Il semble que le programme strip n'a été utilisé que pour réduire la taille du binaire. Le programme a été compilé en statique, il intègre les fonctions des librairies qu'il utilise. Ainsi il devient "indépendant" du système sur lequel le programme est utilisé. Le mot de passe utilisé pour le shell distant est protégé par un mot de passe (SeNif), celui-ci est sommairement encodé pour éviter d'apparaître dans les chaînes de textes (strings). Pour rendre l'analyse du binaire difficile, le code est volontairement confus en particulier les fonctions de codage et de décodage mais la difficulté principale vient de la programmation inefficace/laborieuse du programmeur. Une optimisation poussée peut rendre plus difficile la compréhension du binaire mais il semble que le pirate s'est contenté d'un gcc -O.

En local, il est difficile de remarquer ce programme car il change de nom: il apparaît comme [mingetty]

D'un point de vue réseau, l'utilisation d'un protocole inhabituel passe souvent inaperçue. C'est une spécificité de ce programme. La majorité des programmes de ce type communique via UDP ou ICMP. Le protocole de communication permet de demander à l'agent de renvoyer les réponses sur plusieurs adresses IP ainsi un observateur éventuel aurait des difficultés pour identifier l'adresse du pirate parmi les autres. Le pirate peut commander l'agent en spoofant son adresse IP source. D'autre part, les communications sont codées via un algorithme assez simple et, la taille des paquets est aléatoire.

Quel est le niveau du pirate?

Le pirate a des bases de programmation en C qui lui ont permis de faire des copier/coller de programmes existants pour créer son propre outil. Il ne maîtrise pas les pointeurs et comble cette lacune avec de multiples copies de données en mémoire. On observe le passage d'une adresse IP comme 4 entiers à une fonction suivie d'un entier décrivant le programme doit utiliser l'IP correspond au nom qui suit.

int send_dns_SOA(char source_ip1,char source_ip2,char source_ip3,char source_ip4,int count,int
    source_port1,int source_port2,int use_hostname,char *hostname)

Le paramètre use_hostname est inutile, il aurait suffit de vérifier si hostname était NULL ou une chaîne vide. En fait, la résolution de nom n'a aucune raison de se faire sur l'agent, elle peut être effectuée directement sur le client.

L'analyse du code révèle des constructions assez amusantes comme celle-ci ipHeader->ip_id = htons(random());. La fonction htons permet d'adapter l'ordre des données de la machine à celui du réseau, ce qui n'a aucune raison d'être pour des données aléatoires...

Le programme calcule le checksum de l'entête IP alors que c'est géré directement par le kernel. Lors des attaques par fragmentation, les entêtes ICMP ou UDP sont initialisés or ces données ne correspondent réellement à un entête qui si l'offset est 0, c'est à dire jamais. Les interrogations DNS pour flooder le serveur dont il usurpe l'adresse IP utilise des requêtes DNS prédéfinis mais 4/9 de ces requêtes ne fonctionnent pas car la taille des données est mal calculée. Pour éviter que les paquets réseaux aient une taille fixe, le programme augmente la taille des paquets par une valeur aléatoire mais des données en clairs peuvent ainsi être envoyées.

Quelles améliorations sont à craindre dans l'avenir ?

L'ajout d'un système de mot de passe éviterait la prise de contrôle de l'agent par un tiers. Le simple codage des données peut être remplacé par un véritable algorithme de cryptage. L'utilisation d'un protocole non standard est une bonne idée mais l'information peut être passer via un canal caché. L'outil pourrait inclure des fonctions de worm le rendant redoutable. Enfin, il serait intéressant de prévoir un système de mise à jour. Le binaire peut être protégé par de la compression (UPX) ou du cryptage (Burneye) et inclure des mesures pour éviter d'être tracé (utilisation de signaux par exemple).