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.
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é.
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 |
2.7.2.l.2
se trouvent dans la
Slackware 3.1.
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.
Le programme peut maintenant être désassemblé avec objdump -d the-binary
ou tracé strace -ff
si on est sur une machine isolée.
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
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.
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 fichierbad-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/
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
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.
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.
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).