Utiliser auditd sur Mac OS X et FreeBSD – 2

Cet article est la suite de Utiliser auditd sur Mac OS X et FreeBSD - 1

Configuration : quid des utilisateurs comme www ?

J'expliquais précédemment que le système d'audit ne peut suivre un utilisateur que si ce dernier se connecte à la machine (par ssh par exemple), et que quelques soient les ruses utilisées (su, sudo...) c'est toujours l'UID réelle de l'utilisateur qui est suivie.

À cause de cela, il est totalement impossible de traquer des évènements appartenant à des utilisateurs qui ne peuvent pas se connecter au système. Ainsi, une ligne comme celle-ci dans audit_user ne permettra de traquer aucun évènement :

# pour freebsd remplacer _www par www
_www:fc,fd,ex:

Il serait pourtant très intéressant de savoir ce que fait Apache dans votre dos, parfois sous la direction de méchants pirates.
De nos jours, La plupart des serveurs utilisent des protocoles comme l'HTTP qui permettent aux utilisateurs d'accéder aux ressources sans être authentifiés au niveau du système. Les utilisateurs en question n'existent d'ailleurs pas au niveau du système. Néanmoins, les applications web permettent de faire énormément de choses via les langages comme le PHP, y compris d'interagir avec le système, en lançant des commandes, en créant des fichiers, etc.

Si on souhaite qu'auditd puisse contrôler les actions de www, il faut recourir à un artifice. Attention tout de même, ce qui suit est plus proche d'un hack que d'une méthode vraiment propre, et je ne peux pas garantir son fonctionnement correct dans un environnement de production.

En premier lieu il faut installer un programme spécifique, qui va permettre de forcer auditd à suivre les actions pour une UID donnée, sans que l'utilisateur correspondant ait besoin de se connecter à la machine.

curl -LO http://www.freebsd.org/~csjp/setaudit.c
cc -o setaudit -lbsm setaudit.c 

Cela vous donne, si tout se passe bien, le binaire setaudit, que l'on va utiliser sous cette forme :

setaudit -a UID_WWW -m FLAGS /programme/de/lancement/d/apache

Pour suivre les évènements d'exécution de commande sous respectivement FreeBSD et Mac OS X, cela donnera :

setaudit -a www -m ex /usr/local/etc/rc.d/apache22 restart
setaudit -a _www -m ex /usr/sbin/apachectl restart

Ainsi, toutes les commandes lancées par Apache (donc sous l'UID www) sont enregistrées par auditd.
Comme le masque des évènements (les flags de l'argument -m) décrit ce que l'on doit suivre, il est même inutile de renseigner le fichier audit_user.

Pour résumer :

  • Si vous souhaitez que l'audit suive les utilisateurs qui se connectent à la machine, vous pouvez régler les classes d'évènements qui seront soumises à l'audit pour l'ensemble des utilisateurs dans le fichier audit_control, et gérer les cas particuliers dans audit_user.
  • Si vous souhaitez que l'audit suive des UID "interne", pour traquer tout signe de compromission sur un serveur web, un serveur de mail, etc. il faudra recourir à setaudit pour activer l'audit au moment du lancement du service correspondant.
  • Faites attention à la liste de flags que vous choisissez. Le volume des logs d'audit peut grossir très rapidement.

Troisième partie de l'article ->

Utiliser auditd sur Mac OS X et FreeBSD – 1

FreeBSD et les versions récentes de Mac OS X sont livrées avec un système d'audit intégré, dérivé du Basic Security Module de SUN, et disponible en logiciel libre sous le nom d'OpenBSM. Ce système d'audit se décompose en deux parties : un ensemble d'appels systèmes dédiés et de librairies d'un côté, et des logiciels "utilisateur" de l'autre. Sauf précision contraire, les explications présentées ici sont valables à la fois pour FreeBSD 7 et 8, et Mac OS X 10.6. Dans cette série d'articles je ne vais aborder que l'aspect "utilisateur" du système d'audit : comment l'activer, comment le configurer, comment exploiter les résultats.
Le système d'audit fourni par OpenBSM a pour but la surveillante des actions des utilisateurs. Un certain nombre d'évènements peuvent être mis sous surveillance, pour tout ou partie des utilisateurs. Quand le système d'exploitation détecte qu'un utilisateur sous surveillance génère un des évènements à surveiller, il averti le démon auditd qui se charge de reporter cet évènement dans un fichier de log. Continue reading

Que nous apprend l’affaire « Gawker »

Comme je l'évoquais brièvement dans un précédent article, les serveurs de l'entreprise de média en ligne Gawker ont été piratés récemment, avec pour conséquence immédiate la diffusion au public des logins, emails, et mots de passe chiffrés de plus d'un million d'utilisateurs. Pour plusieurs centaines de milliers d'entre eux, les mots de passe avaient même été déchiffrés avant leur diffusion.
Pour quelqu'un comme moi les dessous de l'affaire sont croustillants, et les détails techniques sont même passionnants. Mais, plus important : tout le monde a une leçon à tirer de ce fiasco monumental.
Cette leçon, et non des moindres, est la suivante : dès l'instant où vous confiez un mot de passe à quelqu'un, ce mot de passe est vulnérable. Ça paraît tout bête comme ça, on se dit "mais oui, c'est sûr qu'en donnant mon mot de passe à mon voisin, alors il n'est plus secret". Non. Relisez bien la leçon, elle est à prendre au premier degré. Oubliez votre voisin, votre copine. Vous créez un compte chez Hotmail, vous choisissez un mot de passe pour pouvoir accéder plus tard à votre boîte mail, vous fournissez ce mot de passe à Hotmail. Ça y est, ce mot de passe n'est plus sûr. Un jour, c'est inévitable, c'est déjà arrivé, et ça arrivera à nouveau, des serveurs Hotmail seront piratés et votre mot de passe pourrait tomber dans les mains de quelqu'un.
L'affaire Gawker est bourrée d'exemples illustrants parfaitement ce principe. Les employés de chez Gawker s'échangeaient des mots de passe par chat, dont les archives des conversations ont été piratées. Le piratage des bases de données des sites de Gawker a montré que les mots de passe des utilisateurs n'étaient pas sécurisés : le chiffrement utilisé était archaïque et connu depuis des années pour être déficient, etc. etc.
Continue reading

De la sécurité de vos mots de passe

En terme de mot de passe, on nous rebat souvent les oreilles avec des recommandations sécuritaires qui tombent sous le sens. Par contre, les arguments qui sous-tendent ces recommandations sont parfois largement infondés.
Tout d'abord, on nous dit de ne pas choisir un mot de passe facile à deviner, c'est bien sûr du bon sens. On nous dit que le mot de passe doit être long, car les ordinateurs actuels permettent de tester des milliers de mots de passe par seconde. Plus le mot de passe est long, plus il sera long à trouver. Certes. C'est mathématiquement exact. Mais à quoi arrive-t-on si on force les gens à utiliser un mot de passe à la fois long et compliqué ? Ils vont utiliser le même mot de passe partout, ils vont le noter quelque part, etc.
Imaginons un instant qu'un pirate tente de deviner votre mot de passe de messagerie. Il a une très bonne connexion internet, et son ping (le délai qui s'écoule entre le moment où il envoie une requête au serveur, et le moment où le serveur dit qu'il a bien reçu cette requête) est de 10 millisecondes. Il faut ajouter à cela le temps de traitement sur le serveur, et l'envoi de la réponse. Mettons pour simplifier que la totalité de la transaction dure 20 ms. Si votre mot de passe de messagerie fait 8 caractères différents et qu'il utilise des lettres minuscules (26 possibilités), des lettres majuscules (26 possibilités), et des chiffres (10 possibilités), c'est probablement assez correct. Ce mot de passe utilise donc 8 caractères différents pris dans une série de 62. Soit un total de 136325,9 milliards de mots de passe possibles. À raison d'un mot de passe testé toutes les 20 ms, cela donne plus de 864 siècles, pour tester tous les mots de passe.
Maintenant, ajoutez des caractères parmi @#&"'(§!)-_$*/:;.,?+ (20 possibilités), et il faudra presque 9114 siècles pour tester tous les mots de passe.
Moralité, la puissance des ordinateurs permet effectivement de tester des millions de mots de passe par heure, mais la latence des connexions internet rend ce mode d'attaque quasiment impossible, car elle ralentit terriblement le processus.
Continue reading

Le fonctionnement des snapshots

Si les tractations entre Apple et Sun s’étaient passées correctement, les utilisateurs de Mac OS X auraient pu profiter d’ici un an ou deux d’un fabuleux système de fichiers : ZFS. Mais comme souvent, l’intérêt des clients passe après les intérêts de l’entreprise, et il semble finalement qu’Apple ait décidé de créer son propre nouveau système de fichiers.

Un aspect sympathique de ZFS (parmi tant d’autres !), est sa capacité à générer et gérer des snapshots. Un snapshot, c’est une sorte de photographie à un instant donné d’un système de fichiers. Cette photographie peut alors être montée comme n’importe quel autre système de fichiers, elle peut servir de base à une sauvegarde, elle peut servir à rechercher un fichier effacé après la création du snapshot…
Plus que l’utilité évidente en matière de sauvegarde de ces snapshots, j’aimerai revenir sur leur fonctionnement interne, car c’est là que réside toute la magie. En effet, au premier abord l’idée d’une photographie à un instant T de son disque dur semble séduisante, mais très rapidement des voix s’élèvent qui s’interrogent sur le temps que cela prend, la place que cela occupe… Imaginez votre disque dur de 250 Go, 500 Go, ou même 1 To, bien rempli. Vous concluez naturellement que pour faire un snapshot de votre volume, il vous faudra un disque dur externe, de grande capacité, et que cela prend un temps fou. Fort heureusement, c’est faux.

ZFS crée ses snapshots selon le mode copy on write, tout comme le système de fichiers par défaut de FreeBSD : UFS. C’est ce dernier que je vais utiliser pour présenter le fonctionnement des snapshots. Il est probable que l’implémentation des snapshots dans ZFS diffère légèrement, mais la logique reste la même, et de nombreux autres systèmes la partage.
Continue reading

Spam de blog : les user agents

Depuis que j'ai codé mon propre antispam pour protéger ce blog des attaques quotidiennes de malfaisants spambots, je collecte précieusement tout ce que ces derniers ont à me dire dans une base de données.
Ainsi, j'ai pu découvrir que, mis à part en de rares occasions, les user agents des spambots sont très variés. Le graphique ci-dessous montre pour la période de mai 2008 à octobre 2009 comment sont répartis le nombre de user agents distincts et le nombre de tentative de spam.

Nombre de user agents différents en fonction du temps

En mai 2008, on note le pic (il dépasse 130 POST/jour) qui correspond à très peu de user agents différents (1 à 2, suivant le jour). Alors que dans le reste de la période la courbe du nombre de POST est très proche de la courbe du nombre d'UA différents.
À noter que, plutôt que de me limiter au seul User Agent, j'ai fait ma petite analyse sur la concaténation des variables HTTP_USER_AGENT, HTTP_ACCEPT, HTTP_ACCEPT_LANGUAGE, et HTTP_ACCEPT_ENCODING. On obtient ainsi une meilleure discrimination entre les différents attaquants.

Voici un exemple abrégé de capture :

REQUEST_TIME : 2008-12-09 19:51:33
HTTP_HOST : www.patpro.net
REMOTE_ADDR : 88.198.53.43
HTTP_USER_AGENT : Mozilla/5.0 (Windows; U; Windows NT 5.0; ru; rv:1.8.1.6) 
                  Gecko/20070725 Firefox/2.0.0.6
HTTP_ACCEPT : text/xml, application/xml, application/xhtml+xml,
              text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
HTTP_ACCEPT_LANGUAGE : ru
HTTP_ACCEPT_ENCODING : deflate

Si on s'intéresse particulièrement aux variables HTTP_ACCEPT* on voit qu'elles sont utilisées de manières très inégales en fonction du robot :

header_spamweb

On peut supposer que ces caractéristiques permettent de différencier les différents botnets, et versions des scripts. Les spammers poussent donc le raffinement assez loin, dans le but, bien sûr, de passer au travers des protections antispam. Dans certaines tentatives que j'ai enregistrées, le client fourni même un cookie de session.

Le spam en France

Le spam en France, c'est un vaste sujet. On pourrait gloser à loisir pendant des jours sur tel ou tel aspect du problème. Alors je vais juste donner un nombre : 96,1%. Ce nombre, c'est le taux de spam de la France pour le mois de juin. C'est à dire que dans notre beau pays, plus de 96% du trafic d'email sont du spam. C'est le record mondial (pour le mois de juin). Pour la même période, les États Unis d'Amérique sont à 78,4% et le Japon atteint un très enviable 67,1% de pollution. Et puis, si c'est MessageLab qui nous le dit, c'est forcément vrai (j'ironise, mais vu les logs de mes serveurs de messagerie, ils sont sûrement dans le vrai).

Je rappelle à toutes fins utiles que, si le cœur vous en dit, vous pouvez télécharger le "plug-in" signal-spam pour Mail, qui permet de déclarer votre spam à l'organisme paresseux nommé Signal Spam (c'est pas bien de se moquer, méchant patpro).

jot et seq, créer des séquences en ligne de commande

Quand on écrit des scripts shell, il arrive fréquemment d'avoir besoin de générer des séquences de chiffres ou de lettres (nourrir une boucle, créer des noms de fichiers aléatoires ou non...).
Il existe pour cela deux outils, seq et jot. seq est la commande GNU, elle est donc très répandue (Linux). jot est une commande originaire du monde BSD (FreeBSD, Mac OS X, ...). Elle est moins répandue, et surtout moins connue, ce qui est bien dommage. En effet, jot est nettement plus puissante et versatile que seq.

Voyons quelques cas d'utilisation concrets de ces deux commandes, avec pour commencer une boucle for classique. Note : toutes les commandes présentées ici ont été testées dans un shell bash. La syntaxe des différentes boucles peut varier dans d'autres shells.
Imaginons que j'ai besoin d'une boucle qui tourne sur une liste de chiffres de 1 à 10, je peux très simplement faire ceci, et obtenir à chaque fois le même résultat exactement :

# syntaxe basique
for i in 1 2 3 4 5 6 7 8 9 10; do
	# mon travail
	echo $i
done

# syntaxe avancée
for ((a=1; a<=10 ; a++)); do
	# mon travail
	echo $a
done

# autre syntaxe avancée
for a in {1..10}; do
	# mon travail
	echo $a
done

# syntaxe avec seq
for b in $(seq 1 10); do
	# mon travail
	echo $b
done

# syntaxe avec jot
for c in $(jot 10); do
	# mon travail
	echo $c
done

Pour ces cas simples, l'intérêt des commandes seq et jot n'est pas immédiat, d'autant que si la syntaxe basique est hors jeux pour les listes longues (1 à 1000 par exemple), les syntaxes avancées sauront en général s'en tirer.
Voyons comment faire maintenant si je souhaite faire une liste de 01 à 10 :

La syntaxe basique fonctionne toujours, mais je dois taper à la main 01, 02, ... C'est fastidieux. Les syntaxes avancées ne savent pas faire directement. Je peux par contre invoquer printf pour faire une réécriture de mes valeurs :

for a in {1..20}; do
	a=$(printf "%02d" $a)
	# mon travail
	echo $a
done

Pour faire ce type d'itération sans étapes intermédiaires il reste seq et jot.
L'ajout de l'option w à la commande seq permet de forcer le formatage de sorte que tous les nombres affichés aient la même largeur. En cas de besoin, un ou plusieurs 0 sont ajoutés devant le nombre pour compléter :

seq -w 1 10
# résultat :
01
02
...
09
10

Pour jot, le padding n'est pas complètement automatique, il faut en régler la largeur via le formatage de type printf. On obtient le même résultat qu'avec seq :

jot -w '%02d' 10

C'est plus délicat, mais cela nous permet de mettre le doigt sur une première limitation de seq : ce dernier ne sait pas utiliser d'autres options de printf que %e, %f et %g.
Ainsi, pour produire une liste de 00001 à 00010, je peux utiliser jot avec un formatage printf ou seq avec un formatage automatique (a priori, c'est une nouveauté apparue depuis la rédaction de cet article), mais pas seq :

jot -w '%05d' 10
# résultat :
00001
00002
...
00009
00010
seq -w 00010
# résultat :
00001
00002
...
00009
00010

Les deux commandes sont bien sûr capables de gérer une borne inférieure et une borne supérieure, mais leur gestion des incréments est une de leurs différences fondamentales. Vous pouvez indiquer à chacune de travailler de 1 à 1000 mais seq ne sait travailler que par incrément fixe :

# seq sait aller de 1 à *maximum* 1000 par saut de 250 :
seq 1 250 1000
# résultat :
1
251
501
751

Alors que jot travaille par défaut sur le nombre d'étapes :

# jot sait aller de 1 à 1000 strictement, en 5 étapes :
jot 5 1 1000
# résultat :
1
251
500
750
1000

jot fonctionne en fait en calculant l'incrément optimal. Ce dernier comporte parfois des décimales. Comme le format par défaut pour jot est l'entier, le résultat ci-dessus n'est qu'un arrondi des valeurs réelles de travail de jot. Si on force l'affichage des décimales, on obtient bien une série mathématiquement satisfaisante :

jot -w '%g' 5 1 1000
# résultat :
1
250.75
500.5
750.25
1000

Il sait aussi travailler sur un incrément explicite, comme seq :

# et jot donne alors le même résultat que seq :
jot 5 1 1000 250
# résultat :
1
251
501
751

Nous laissons définitivement seq derrière nous, car il ne sait travailler qu'avec des chiffres, alors que jot peut utiliser tout l'ASCII, générer des listes aléatoires, écrire une chaîne donnée x fois. Et si vous le jumelez avec son ami rs, il sait générer des matrices, des séries de mots de passe...
Voici quelques exemples tirés du man et d'ailleurs :

# les 128 caractères ASCII :
jot -c 128 0

# une ligne de 40 points : 
jot -s "" -b . 40

# l'alphabet en majuscule :
jot -c 26 A

# créer 5 noms de fichiers partiellement aléatoires :
jot -r -w '/tmp/fichier-%05d' 5 0 10000

# 10 mots de passe de 12 caractères ASCII aléatoires entre ! et ~ :
jot -r -c 120 33 126 | rs -g0 -w12

# une matrice 10x10, de 100 nombres tirés au hasard entre 1 et 100
jot -r 100 | rs 10 10 

Un peu d'histoire pour finir. Le nom de la commande jot ne cesse de faire râler les linuxiens qui doivent un jour ou l'autre travailler sur un système BSD. Il est indéniable que seq est un nom qui tombe sous le sens, de sequence à seq, il n'y a qu'un pas. Pour jot, ce n'est pas aussi immédiat.
Le nom de jot signifie en anglais "très petite quantité", et vient du latin iota. John A. Kunze, qui a développé jot, mais aussi rs et lam, m'a expliqué qu'il a écrit ces trois commandes il y'a très longtemps, en s'inspirant des opérateurs iota, reshape, et laminate du langage APL. Il a alors décidé de donner à ses commandes des noms typiquement "unixiens". Ainsi sont nées jot, rs et lam.

edit 1 : ajout du résultat pour certains exemples, correction d'une typo dans une commande.
edit 2 : correction d'un exemple, suppression de certaines boucles for inutiles, ajout de précision sur les incréments décimaux.
edit 3 : ajout des séquences du type {1..10} gérées par bash.
edit 4 : mise à jour au suejt de la capacité de seq à gérer le padding automatique.