blog

RSS

Blog

Berkeley is famous for LSD and BSD UNIX. I don't think that it is a coincidence.

Mise à jour : quelques jours après la publication de ce billet, on apprend que des personnalités du spectacle ont vu leurs comptes cloud ainsi piratés et que leurs photos intimes circulent sur le Web (voir par exemple la nouvelle sur /. et les commentaires et recommandations d'Apple) — 2014/09/05.

Attaques par force brute (ou brute-force), tout le monde connaît : essayer de se connecter à un service, une application, whatever en énumérant tous les mots de passe possibles. Pour s'en prémunir, il existe pas mal d'outils dont fail2ban, DenyHosts et sshguard mais ce ne sont que les plus connus.

Lors d'une tentative de connexion qui échoue (au hasard mot de passe invalide ou utilisateur inexistant), une trace est laissée dans un journal mentionnant le plus souvent l'identifiant et l'adresse IP source (a minima).

Ces outils surveillent les journaux, cherchent ces messages et ajoutent des règles (on y reviendra) pour empêcher de nouvelles connexions… De nouvelles connexions, oui mais depuis cette adresse IP. Seuls les scripts kiddies sont impactés par ces mesures, les « vrais » badguys disposant eux de botnets et donc d'adresses IP sources nombreuses. Bref, ces outils ne protègent donc pas de grand-chose…

Tant pis, il est de bon ton de filtrer tout de même, filtrons. Pour finir sur leur inutilité relative, de tous les outils que je connais, tous ont un défaut rédhibitoire :

  • soit ils manipulent iptables ou directement Netfilter ie. nécessitent Linux,
  • soit ils compilent en dur les messages à chercher (sic !)
  • au pire ils proposent de se rabattre sur TCP Wrapper mais seul les daemons utilisant cette bibliothèque libwrap seront « protégés » ; typiquement inutile pour un serveur (et donc une application) Web…

En me replongeant dans la documentation de mon daemon syslog(3) favori — je veux dire syslog-ng — il s'avère que j'ai déjà bien plus qu'il ne m'en faut sous la main, pas besoin de lancer un n-ième daemon :

  • il propose le filtrage par expressions régulières,
  • il sait lire un fichier texte (ie. un autre fichier journal d'une application n'envoyant pas ses messages à syslog(3) mais directement dans ce fichier),
  • il sait écrire dans un tube nommé,
  • il sait envoyer des messages à un programme…

Commençons par un exemple reprenant les deux premiers points :

syslog-ng.conf
...
source s_jabber { file ("/var/log/ejabberd/ejabberd.log" default-facility(local7) default-priority(info) follow_freq(1) program_override(ejabberd) flags(no-parse)); };
source s_local {
   unix-dgram("/var/run/log" flags(syslog-protocol));
   file("/dev/klog" program_override("/netbsd"));
   internal();
};
...
filter f_auth { facility(authpriv,auth) and level(info..emerg); };
destination d_authlog { file("/var/log/authlog" perm(600) owner(root) group(wheel)); };
log { source(s_local); filter(f_auth); destination(d_authlog); };
 
filter f_ssh_bruteforce {
   filter(f_auth) and message(".*Failed password for invalid user [^\ ]* from ")
      or filter(f_auth) and message("Invalid user [^\ ]* from ")
      or filter(f_auth) and message("Too many authentication failures for ")
      or filter(f_auth) and message("User [^\ ]* from [0-9a-f\.:]* not allowed because not listed in AllowUsers");
};
destination d_bruteforce { file("/var/log/suspicious.log" owner(root) group(wheel) perm(0640)); };
log { source(s_local); filter(f_ssh_bruteforce); destination(d_bruteforce); };
filter f_ejabberd_auth { message ("Failed authentication for [^\ ]@kabs.homeunix.org from IP "); };
log { source(s_jabber); filter(f_ejabberd_auth); destination(d_bruteforce); };
filter f_owncloud { program(ownCloud) and message("{core} Login failed: user '[\^ ]*' , wrong password, IP:"); };
log { source(s_local); filter(f_owncloud); destination(d_bruteforce); };

À ce stade, j'ai un journal /var/log/suspicious.log agrégeant les erreurs d'OpenSSH et d'ejabberd. Sur ce modèle, je peux maintenant ajouter des daemons, services, applications… Il reste à parser ce fichier pour ajouter une ACL au mécanisme de protection de mon choix et donc adresser le dernier point évoqué ci-dessus.

Ce mécanisme de mon choix n'est autre qu'une table dans pf(4) mais cela importe peu, je veux juste lancer une commande avec un argument (au moins) : l'adresse à bloquer. On pourrait raffiner et donner d'autres arguments, par exemple pour choisir la table dans pf(4) (SSH ou Nginx ?) ou pour générer un message à syslog(3) signalant que telle adresse a été ajoutée… Notre imagination est notre seule limite !

Pour une machine IPv4-only et avec un tube nommé, un fifo, ça ressemble donc à :

syslogng2pf.sh
#!/bin/ksh
#
umask=077
fifo=${1:-/var/spool/sockets/syslog2pf}
 
# XXX trap "rm -f ${fifo}" INT TERM QUIT
if [ ! -f ${fifo} ]; then
   mkfifo ${fifo}
fi
 
while [ -e ${fifo} ]; do
   while read suspicious <${fifo}; do
      src_addr=$( echo ${suspicious} |\
         sed -e "s/.*Failed password for invalid user [^\ ]* from \([0-9]*\.[0-9]*\.[0-9]*\.[0-9]*\).*/\1/" \
            -e "s/.*User [^\ ]* from \([0-9]*\.[0-9]*\.[0-9]*\.[0-9]*\) not allowed because not listed in AllowUsers/\1/" \
            -e "s/.*Failed authentication for [^\ ]@kabs.homeunix.org from IP \([0-9]*\.[0-9]*\.[0-9]*\.[0-9]*\).*/\1/" \
            -e "s/.*{core} Login failed: user '[^\ ]' , wrong password, IP:\([0-9]*\.[0-9]*\.[0-9]*\.[0-9]*\).*/\1/" \
            -e "/^[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*$/!d" \
            -e "/^192\.0\.2/d" )
      pfctl -t bruteforce -T add ${src_addr}
      pfctl -t bruteforce -T show | grep -q {src_addr}
      if [[ $? -eq 0 ]]; then
         logger -i -p authpriv.info "$0: ${src_addr} added to bruteforce table"
         echo "   ${src_addr}" >> /etc/bruteforce.pf
      else
         logger -i -p authpriv.crit "$0: error while adding ${src_addr} to <bruteforce> pf(4) table"
      fi
   done
done

Ce script attend en argument le chemin vers le fichier et lit depuis ce dernier, non sans l'avoir créé ; il fait le ménage dans les messages pour ne garder que l'adresse IPv4 source (IPv6 est laissé en exercice au lecteur ; hint outre [0-9], une adresse IPv6 contient [a-f] et :) ; à la fin, il supprime tout ce qui n'est pas une adresse IPv4 valide ; enfin, il supprime les adresses de mon réseau (ici le /24 d'exemple, cf. 5737). Il ajoute l'adresse à la table avec pfctl(8) et vérifie qu'elle est bien présente dans la table ; enfin, il journalise.

En adaptant le morceau de fichier de configuration ci-dessus, ça tombe en marche.

Utiliser un fifo est surtout utile si la lecture peut être plus lente que l'écriture… est-ce vraiment utile ?

Remarque 1 : la simplicité d'Unix frappe encore : la lecture fonctionne que le fichier soit un fichier simple ou un fifo.

Remarque 2 : le lecteur avisé notera que Syslog-NG peut lancer une commande, il suffit alors de modifier le script pour lire stdin.

Remarque 3 : pour tester, remplacer le tube par /tmp/plop, lancer le script dans un terminal et envoyer des messages (echo “blabla 1.2.3.4” » /tmp/plop) depuis un autre. On voit bien apparaître les lignes sur le premier terminal :

pfctl -t bruteforce -T add 1.2.3.4
pfctl -t bruteforce -T add 1.2.3.5
pfctl -t bruteforce -T add 1.2.3.6

Remarque 4 : j'ai volontairement ajouté en commentaire la commande « stupide » trap : quand le processus meurt, le fichier est supprimé… son contenu est alors perdu, c'est donc tout sauf une bonne idée.

Voilà, il ne reste plus qu'à « empiler » : faire transiter les messages de nouvelles applications, daemons, programmes, whatever par syslog(3), filtrer dans syslog-ng.conf puis dans le script.

NB : l'avantage des programmes que je fustigeais au début réside dans la mémorisation de la date d'ajout pour pouvoir programmer une date de fin. Une tâche en crontab(5) fait presque aussi bien.

2014/08/20 01:04 · pc

Ma BiduleBox permet entre autres choses :

  • le maintient d'enregistrement(s) DNS dynamique(s) DynDNS, No-IP, …
  • le port-forwarding (pour contourner le problème du routage des adresses RFC 1918)

ainsi le port 80 de votre machine interne 10.0.2.1 peut-elle être accessible depuis Internet en se connectant à http://ma-machine-a-moi-que-j-ai.noip.com.

Lorsque je suis chez moi, le nom kabs.homeunix.org pointe bien vers la bonne adresse (publique) mais dès que je me connecte, c'est la box qui est vue. Dès lors, il faut que les requêtes DNS depuis l'intérieur pointent vers l'adresse interne ie. RFC 1918 :

  • de l'extérieur, kabs.homeunix.org a pour enregistrement A l'adresse publique (l'adresse de la LiveBox), enregistrement maintenu via le DNS dynamique ;
  • de l'intérieur, kabs.homeunix.org a pour enregistrement A l'adresse du serveur (RFC 1918).

Il suffit donc d'ajouter une zone pour la machine (ici avec BIND) :

  • dans named.conf ajouter :
    zone "kabs.homeunix.org" {
            type master;
            file "db.kabs.homeunix.org";
    };
  • créer le fichier de zone db.kabs.homeunix.org :
    kabs.homeunix.org. IN SOA kabs.homeunix.org. root.localhost. (
                    2             ; Serial
                    604800        ; Refresh
                    86400         ; Retry
                    2419200       ; Expire
                    604800 )      ; Negative Cache TTL
    ;
    kabs.homeunix.org. IN NS localhost.
    kabs.homeunix.org. IN A  10.0.2.1

Et voilà, le nom kabs.homeunix.org. ne pointe pas vers la même adresse suivant le serveur interrogé ie. suivant la position des clients mais et ces derniers se connectent tous au même hôte où qu'ils soient.

2014/01/08 23:21 · pc

Lorsqu'on a un agent SSH chargé et que l'on cherche à se connecter à une machine sur laquelle on n'a pas de clef SSH, on obtient lamentablement :

[host_a]$ ssh luser@host_b
Received disconnect from 192.0.2.18: 2: Too many authentication failures for luser

Première solution :

[host_a]$ unset SSH_AUTH_SOCK
[host_a]$ ssh luser@host_b
luser@host_b's password:
...

En effet, ssh(1) a conscience qu'un agent est chargé et l'interroge si la variable d'environnement SSH_AUTH_SOCK existe et pointe sur une socket Unix ouverte par ssh-agent(1) (ici sur host_a).

Seconde solution :

[host_a]$ ssh -o "PreferredAuthentications password" luser@host_b
luser@host_b's password:
...

Cette dernière solution permet, si on fait du « renvoi d'agent » (option ForwardAgent yes dans $HOME/.ssh/config), d'utiliser une clef sur la machine host_b, quand bien même luser s'est authentifié par mot de passe sur host_b. Lors de la connexion, sshd(8) sur host_b lancera pour luser un autre agent et :

[host_b]$ ssh-add -l
4096 37:59:ff:99:c0:24:9a:ee:cf:24:dd:aa:7c:f6:cc:5c /Users/bofh/.ssh/foo (RSA)
4096 25:ff:d1:9c:87:cc:33:8a:bb:78:6d:60:aa:64:08:ee /var/users/bofh/.ssh/bar (RSA)

Ici, luser sur host_b peut utiliser deux clefs SSH, manifestement chargées depuis des machines différentes (par « renvoi d'agent » donc).

2013/05/04 11:15 · pc

La commande mp3tag (installé avec mp3blaster) utilise des séquences d'échappement ANSI pour l'affichage des données sur le terminal. Pour les supprimer avec sed(1) il faut ruser un peu avec le caractère ESCAPE, 033 en octal, 27 en décimal, :

for f in $(find . -name "*.mp3"); do mp3tag "$f" 2>&1 ; done |\
   sed -e "s/$(printf '\033')\\[[01];3[0-9]m//g; s/$(printf '\033')\\[0m//g"

En lieu et place de printf '\033' on peut aussi utiliser echo '\0033\c'.

2013/03/31 14:54 · pc
  • blog.txt
  • Dernière modification : 2011/07/16 19:03
  • de pc