Création d'un outil réseau simplifié similaire à Netcat en Python

Introduction à Netcat

Netcat, souvent appelé « nc », est un utilitaire réseau polyvalent permettant de gérer des connexions TCP/IP ou UDP entre machines. Il facilite la communication sur des ports spécifiques, l'échange de données, l'analyse de ports et la mise en place de serveurs bsaiques.

Conception d'une implémentation Python

Fonctionnalités planifiées

Le programme utilisera la bibliothèque argparse pour gérer les arguments en ligne de comande, permettant ainsi de contrôler différents modes d'opération.

if __name__ == "__main__":
    analyseur = argparse.ArgumentParser(
        description="Outil réseau de type shell inversé",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog=textwrap.dedent(
            """
                python reseau_tool.py -h <IP> -p 8888 -s -r   # shell de commande
                python reseau_tool.py -h <IP> -p 8888         # connexion au serveur
                telecharger <fichier>                         # récupérer un fichier
                envoyer <fichier>                             # transmettre un fichier
            """
        ),
    )

    analyseur.add_argument("-r", "--cmd", action="store_true", help="activer le shell de commande")
    analyseur.add_argument("-s", "--ecouter", action="store_true", help="démarrer en mode écoute")
    analyseur.add_argument("-p", "--port", type=int, default=8888, help="numéro de port")
    analyseur.add_argument("-h", "--hote", default="127.0.0.1", help="adresse IP cible")

    parametres = analyseur.parse_args()

    instance = ReseauOutil(parametres)
    instance.lancer()

Définition de la classe principale

class ReseauOutil:
    def __init__(self, param):
        self.param = param
        self.connexion = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.connexion.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

    def lancer(self):
        if self.param.ecouter:
            self.ecouter_connexions()
        else:
            print('Entrez la première commande >', end='')
            self.transmettre_donnees()

Fonctionnalité d'écoute

Le mode écoute lie le socket à l'adresse et au port spécifiés, puis attend les connexions entrantes dans une boucle. Chaque client est géré dans un thread séparé.

    def ecouter_connexions(self):
        self.connexion.bind((self.param.hote, self.param.port))
        self.connexion.listen(5)

        while True:
            client_sock, _ = self.connexion.accept()
            thread_client = threading.Thread(target=self.gerer_client, args=(client_sock,))
            thread_client.start()

Traitement des commandes reçues

La méthode gerer_client traite les commandes reçues, exécutant des opérations locales ou transférant des fichiers selon le besoin.

    def gerer_client(self, socket_client):
        if self.param.cmd:
            while True:
                try:
                    tampon = b''
                    while b'\n' not in tampon:
                        tampon += socket_client.recv(64)
                    commande = tampon.strip().decode()
                    if commande.startswith("telecharger"):
                        envoyer_fichier(commande, socket_client)
                    elif commande.startswith("envoyer"):
                        recevoir_fichier(commande, socket_client)
                    else:
                        resultat = executer_commande(commande)
                        if resultat:
                            socket_client.send(resultat.encode())
                except Exception as erreur:
                    print(f'Erreur serveur : {erreur}')
                    self.connexion.close()
                    sys.exit()

Exécution des commandes locales

La fonction executer_commande interprète et exécute les commandes, incluant le changement de répertoire.

def executer_commande(cmd):
    if cmd.startswith('cd '):
        os.chdir(cmd[3:].strip())
        return os.getcwd() + '>'
    else:
        cmd = cmd.strip()
        if not cmd:
            return
        sortie = subprocess.check_output(shlex.split(cmd), stderr=subprocess.STDOUT)
        return sortie.decode() + os.getcwd() + '>'

Fonctionnalité de transmission

Le mode transmission établit une connexion avec l'hôte cible et permet l'interaction utilisateur, y compris le transfert de fichiers.

    def transmettre_donnees(self):
        self.connexion.connect((self.param.hote, self.param.port))
        try:
            while True:
                entree_utilisateur = input() + '\n'
                self.connexion.send(entree_utilisateur.encode())
                if entree_utilisateur.startswith("telecharger"):
                    recevoir_fichier(entree_utilisateur, self.connexion)
                elif entree_utilisateur.startswith("envoyer"):
                    envoyer_fichier(entree_utilisateur, self.connexion)
                else:
                    reponse = ''
                    while True:
                        donnees = self.connexion.recv(4096)
                        if not donnees:
                            break
                        reponse += donnees.decode('utf-8', errors='ignore')
                    if reponse:
                        print(reponse, end='')
        except KeyboardInterrupt:
            print("Arrêt par l'utilisateur.")
            self.connexion.close()
            sys.exit()

Transfert de fichiers

Les fonctions de transfert gèrent l'envoi et la réception de fichiers, avec validation d'existence et suivi de progression.

def envoyer_fichier(commande, socket_dest):
    nom_fichier = commande.split()[1]
    if os.path.isfile(nom_fichier):
        taille = os.path.getsize(nom_fichier)
        socket_dest.send(str(taille).encode() + b'\n')
        with open(nom_fichier, "rb") as fichier:
            socket_dest.send(fichier.read())
    else:
        socket_dest.send("Fichier introuvable".encode())

def recevoir_fichier(buffer, socket_src):
    _, nom_fichier = buffer.split(" ")
    nom_fichier = nom_fichier.replace("\n", "")
    taille_fichier = int(socket_src.recv(64).decode().strip('\n'))
    print("Taille du fichier : %s octets" % (taille_fichier))

    with open(nom_fichier, "wb") as fichier:
        recu = 0
        while recu < taille_fichier:
            bloc = min(4096, taille_fichier - recu)
            donnees = socket_src.recv(bloc)
            fichier.write(donnees)
            recu += len(donnees)
            print("Progression : {:.2f}%".format(recu / taille_fichier * 100))
    print("Fichier '{}' téléchargé avec succès.".format(nom_fichier))
    print('Entrez une commande >', end="")

Étiquettes: Python netcat Socket threading Subprocess

Publié le 11 juillet à 16h39