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="")