← Accueil

PCTAMALOU

Semaine 5 : La Structuration du Savoir – Python Intermédiaire

Par Platon-Y – pctamalou.fr & e-of-h.fr

🎯 Objectif de la semaine

Maîtriser les structures de données avancées de Python pour organiser et manipuler efficacement des informations complexes en cybersécurité. Transformer des scripts simples en outils professionnels modulaires et réutilisables.

Transition critique : Passer du scripting basique à l'ingénierie d'outils. Un bon hacker sait écrire du code, un excellent hacker sait structurer son code.

flowchart TD A[📊 Données Brutes] --> B{Structure de Données} B --> C[📝 Listes
Ordonnées, modifiables
index: 0, 1, 2...] B --> D[🗂️ Dictionnaires
Clé → Valeur
rapide accès] B --> E[🔗 Tuples
Immuables
périodes, coordonnées] B --> F[📦 Sets
Uniques, non-ordonnés
IPs uniques] C --> G[🔍 Analyse Séquentielle
Logs, scans, captures] D --> H[⚡ Recherche Rapide
Inventaire, mapping] E --> I[🔒 Données Constantes
Configurations, seuils] F --> J[🎯 Élimination Doublons
IPs uniques, ports] G --> K[🎨 JSON/CSV
Export/Import structuré] H --> K I --> K J --> K K --> L[🚀 Outils Professionnels
Modulaires, réutilisables] style C fill:#0a0e17,stroke:#00d4ff,stroke-width:2px style D fill:#11151f,stroke:#9d4edd,stroke-width:2px style L fill:#00f5a0,stroke:#000,stroke-width:2px

📚 Sujet – Théorie (1 heure)

Les structures de données : l'architecture invisible des outils

🏗️ Pourquoi la structure compte :

En cybersécurité, vous manipulez des données complexes :

  • Centaines d'IPs avec différents statuts
  • Milliers de logs avec timestamps et événements
  • Dizaines de services avec versions et vulnérabilités
  • Réseaux entiers avec topologies et relations

Choisir la bonne structure de données, c'est choisir le bon conteneur pour vos informations.

Les quatre piliers de Python en sécurité

Structure Syntaxe Caractéristiques Cas d'usage sécurité Complexité accès
Liste [] Ordonnée, mutable, doublons autorisés Résultats de scan, logs séquentiels, commandes O(n) recherche
O(1) index
Dictionnaire {} Clé → Valeur, non ordonné* Inventaire IP→services, configs, mappings O(1) recherche
O(1) insertion
Tuple () Ordonné, immuable, fixe Coordonnées réseau, versions, constantes O(1) index
Immuable
Set set() Non ordonné, unique, mutable IPs uniques, ports ouverts uniques, tags O(1) recherche
O(1) insertion

* Les dictionnaires Python 3.7+ conservent l'ordre d'insertion.

Fonctions : l'art de la modularité

⚠️ Les 3 principes des fonctions efficaces :

  1. Une fonction = une responsabilité (Single Responsibility Principle)
  2. Nom explicite : scan_port() pas do_stuff()
  3. Documentation complète avec docstring (triple guillemets)
def analyze_port_scan(results, threshold=100):
    """
    Analyse les résultats d'un scan de ports pour détecter des scans agressifs.
    
    Args:
        results (list): Liste de tuples (ip, port, status)
        threshold (int): Seuil de ports scannés pour alerte
    
    Returns:
        dict: Résultats d'analyse avec IPs suspectes
    """
    ip_counts = {}
    for ip, port, status in results:
        ip_counts[ip] = ip_counts.get(ip, 0) + 1
    
    suspicious = {ip: count for ip, count in ip_counts.items() 
                  if count > threshold}
    
    return {
        'total_scans': len(results),
        'unique_ips': len(ip_counts),
        'suspicious_ips': suspicious
    }

JSON : le langage universel des données structurées

📋 Pourquoi JSON est essentiel :

  • Interopérabilité : Lu par tous les langages et outils
  • Lisibilité : Format texte facile à déboguer
  • Hiérarchique : Supporte structures complexes imbriquées
  • Standard : API REST, configurations, exports

En sécurité, vous utiliserez JSON pour :

  • Exporter des résultats d'analyse
  • Stocker des configurations d'outils
  • Communiquer avec des APIs (Shodan, VirusTotal)
  • Sauvegarder des inventaires réseau

Les compréhensions : la puissance Pythonique

Python offre une syntaxe concise pour transformer des données :

# Liste classique
results = []
for ip in ip_list:
    if is_suspicious(ip):
        results.append(ip)

# Liste en compréhension (plus Pythonique)
results = [ip for ip in ip_list if is_suspicious(ip)]

# Dictionnaire en compréhension
service_ports = {'ssh': 22, 'http': 80, 'https': 443}
inverted_ports = {port: service for service, port in service_ports.items()}
# Résultat: {22: 'ssh', 80: 'http', 443: 'https'}

💡 Philosophie du code structuré :

"Écrivez du code comme si la personne qui devra le maintenir était un psychopathe qui connaît votre adresse."

En sécurité, votre code doit être :

  1. Lisible : Les autres doivent pouvoir le comprendre rapidement
  2. Maintenable : Facile à modifier et étendre
  3. Robuste : Gère les cas d'erreur et données inattendues
  4. Documenté : Explique le "pourquoi" pas juste le "comment"

🔬 Mission pratique – Laboratoire (2 heures)

Scénario : Création d'un système d'inventaire réseau intelligent

Vous devez développer un outil pour cartographier un réseau, stocker les découvertes dans une structure organisée, et exporter les résultats pour analyse.

📝 Organisation professionnelle :

~/mon_lab/semaine5_structures/
├── scripts/           # Code source
├── data/              # Données brutes
├── reports/           # Exports générés
├── tests/             # Tests unitaires
└── README.md          # Documentation

Étapes détaillées : Construction d'un outil complet

  1. Configuration de l'environnement
    # Structure du projet
    cd ~/mon_lab
    mkdir -p semaine5_structures/{scripts,data,reports,tests}
    cd semaine5_structures
    
    # Fichier de données d'exemple
    cat > data/scan_results.txt << 'EOF'
    192.168.1.1:22:open:ssh:OpenSSH_7.9
    192.168.1.1:80:open:http:Apache_2.4.41
    192.168.1.10:22:closed:ssh:Unknown
    192.168.1.10:443:open:https:nginx_1.18
    192.168.1.15:22:open:ssh:OpenSSH_8.2
    192.168.1.15:21:open:ftp:vsftpd_3.0
    192.168.1.20:3389:open:rdp:Windows_RDP
    192.168.1.25:22:open:ssh:OpenSSH_7.9
    192.168.1.25:80:open:http:Apache_2.4.38
    192.168.1.25:443:open:https:Apache_2.4.38
    192.168.1.30:22:filtered:ssh:Unknown
    192.168.1.100:445:open:smb:Samba_4.9
    EOF
  2. Création du module d'analyse principal
    #!/usr/bin/env python3
    """
    PCTAMALOU - Semaine 5 : Système d'inventaire réseau
    Module principal pour l'analyse et la structuration des données réseau
    """
    
    import json
    import csv
    from collections import defaultdict, Counter
    from datetime import datetime
    import ipaddress
    
    # ============================================================================
    # CLASSES DE DONNÉES
    # ============================================================================
    class NetworkHost:
        """Représente un hôte réseau avec ses services"""
        
        def __init__(self, ip_address):
            self.ip = ip_address
            self.services = []  # Liste de services
            self.os_guess = None
            self.first_seen = datetime.now()
            self.last_seen = datetime.now()
            self.tags = set()  # Tags pour classification
        
        def add_service(self, port, status, service, version):
            """Ajoute un service à l'hôte"""
            service_data = {
                'port': port,
                'status': status,
                'service': service,
                'version': version,
                'discovered': datetime.now()
            }
            self.services.append(service_data)
            self.last_seen = datetime.now()
        
        def to_dict(self):
            """Convertit l'hôte en dictionnaire pour export"""
            return {
                'ip': self.ip,
                'services': self.services,
                'os_guess': self.os_guess,
                'first_seen': self.first_seen.isoformat(),
                'last_seen': self.last_seen.isoformat(),
                'tags': list(self.tags)
            }
    
    # ============================================================================
    # FONCTIONS UTILITAIRES
    # ============================================================================
    def parse_scan_file(filepath):
        """
        Parse un fichier de résultats de scan
        
        Args:
            filepath (str): Chemin vers le fichier
        
        Returns:
            list: Liste d'objets NetworkHost
        """
        hosts = {}  # Dictionnaire IP -> NetworkHost
        
        try:
            with open(filepath, 'r') as f:
                for line_num, line in enumerate(f, 1):
                    line = line.strip()
                    if not line or line.startswith('#'):
                        continue
                    
                    try:
                        # Format: IP:PORT:STATUS:SERVICE:VERSION
                        ip, port, status, service, version = line.split(':')
                        port = int(port)
                        
                        # Création ou récupération de l'hôte
                        if ip not in hosts:
                            hosts[ip] = NetworkHost(ip)
                        
                        # Ajout du service
                        hosts[ip].add_service(port, status, service, version)
                        
                    except ValueError as e:
                        print(f"[WARN] Ligne {line_num} mal formatée: {line}")
                        continue
            
            return list(hosts.values())
            
        except FileNotFoundError:
            print(f"[ERREUR] Fichier {filepath} non trouvé")
            return []
        except Exception as e:
            print(f"[ERREUR] Échec parsing: {e}")
            return []
    
    def analyze_network(hosts):
        """
        Analyse une liste d'hôtes pour en extraire des insights
        
        Args:
            hosts (list): Liste d'objets NetworkHost
        
        Returns:
            dict: Statistiques et insights
        """
        if not hosts:
            return {}
        
        stats = {
            'total_hosts': len(hosts),
            'total_services': 0,
            'services_by_type': Counter(),
            'ports_by_status': defaultdict(int),
            'common_versions': Counter(),
            'ip_ranges': set()
        }
        
        for host in hosts:
            stats['total_services'] += len(host.services)
            
            # Analyse des services
            for service in host.services:
                stats['services_by_type'][service['service']] += 1
                stats['ports_by_status'][service['status']] += 1
                
                if service['version'] != 'Unknown':
                    stats['common_versions'][service['version']] += 1
            
            # Classification automatique
            classify_host(host)
            
            # Extraction du réseau
            try:
                network = ipaddress.ip_network(f"{host.ip}/24", strict=False)
                stats['ip_ranges'].add(str(network))
            except:
                pass
        
        return stats
    
    def classify_host(host):
        """Classe un hôte en fonction de ses services"""
        services = {s['service'] for s in host.services}
        
        if 'ssh' in services:
            host.tags.add('linux_server')
        if 'rdp' in services or 'smb' in services:
            host.tags.add('windows_host')
        if 'http' in services or 'https' in services:
            host.tags.add('web_server')
        if 'ftp' in services:
            host.tags.add('file_server')
        
        # Hôte avec nombreux services = probable serveur
        if len(host.services) > 3:
            host.tags.add('multi_service')
    
    def export_to_json(hosts, output_path):
        """Exporte les hôtes au format JSON"""
        data = {
            'generated': datetime.now().isoformat(),
            'hosts': [host.to_dict() for host in hosts],
            'metadata': {
                'total_hosts': len(hosts),
                'total_services': sum(len(h.services) for h in hosts)
            }
        }
        
        try:
            with open(output_path, 'w') as f:
                json.dump(data, f, indent=2, default=str)
            return True
        except Exception as e:
            print(f"[ERREUR] Export JSON échoué: {e}")
            return False
    
    def export_to_csv(hosts, output_path):
        """Exporte les services au format CSV"""
        try:
            with open(output_path, 'w', newline='') as f:
                writer = csv.writer(f)
                writer.writerow(['IP', 'Port', 'Status', 'Service', 'Version', 'Tags'])
                
                for host in hosts:
                    for service in host.services:
                        writer.writerow([
                            host.ip,
                            service['port'],
                            service['status'],
                            service['service'],
                            service['version'],
                            ','.join(host.tags)
                        ])
            return True
        except Exception as e:
            print(f"[ERREUR] Export CSV échoué: {e}")
            return False
    
    # ============================================================================
    # FONCTION PRINCIPALE
    # ============================================================================
    def main():
        print("[*] PCTAMALOU - Système d'inventaire réseau")
        print("[*] Analyse des données...")
        
        # 1. Parsing des données
        hosts = parse_scan_file('data/scan_results.txt')
        if not hosts:
            print("[!] Aucune donnée à analyser")
            return
        
        print(f"[+] {len(hosts)} hôtes chargés")
        
        # 2. Analyse
        stats = analyze_network(hosts)
        print(f"[+] {stats['total_services']} services découverts")
        
        # 3. Affichage des insights
        print("\n📊 INSIGHTS:")
        print(f"  • Services par type: {dict(stats['services_by_type'])}")
        print(f"  • Ports par statut: {dict(stats['ports_by_status'])}")
        
        if stats['common_versions']:
            print(f"  • Versions courantes: {stats['common_versions'].most_common(3)}")
        
        # 4. Export
        print("\n💾 EXPORT DES DONNÉES:")
        if export_to_json(hosts, 'reports/inventory.json'):
            print("  ✅ JSON exporté: reports/inventory.json")
        
        if export_to_csv(hosts, 'reports/services.csv'):
            print("  ✅ CSV exporté: reports/services.csv")
        
        # 5. Recherche avancée (exemple)
        print("\n🔍 RECHERCHES AVANCÉES:")
        
        # Hôtes Linux avec SSH
        linux_hosts = [h for h in hosts if 'linux_server' in h.tags]
        print(f"  • Hôtes Linux détectés: {len(linux_hosts)}")
        
        # Services potentiellement vulnérables
        vulnerable_services = []
        for host in hosts:
            for service in host.services:
                if 'OpenSSH_7.' in service['version']:  # Exemple de détection
                    vulnerable_services.append(f"{host.ip}:{service['port']}")
        
        if vulnerable_services:
            print(f"  ⚠️  Services potentiellement vulnérables: {vulnerable_services}")
        
        print("\n[+] Analyse terminée avec succès")
    
    # ============================================================================
    # POINT D'ENTRÉE
    # ============================================================================
    if __name__ == "__main__":
        main()
  3. Exécution et analyse des résultats
    # Exécution
    cd ~/mon_lab/semaine5_structures
    python3 scripts/network_inventory.py
    
    # Inspection des exports
    cat reports/inventory.json | head -50
    cat reports/services.csv
    
    # Analyse avec outils Python interactifs
    python3 -c "
    import json
    with open('reports/inventory.json') as f:
        data = json.load(f)
    print(f'Hôtes: {len(data[\"hosts\"])}')
    print(f'Services totaux: {data[\"metadata\"][\"total_services\"]}')"
  4. Exercices pratiques avancés

    Exercice 1 : Ajoutez une fonction de recherche :

    def find_hosts_by_service(hosts, service_name):
        """Trouve tous les hôtes exécutant un service spécifique"""
        return [host for host in hosts 
                if any(s['service'] == service_name for s in host.services)]
    
    # Utilisation
    web_servers = find_hosts_by_service(hosts, 'http')
    print(f"Serveurs web: {[h.ip for h in web_servers]}")

    Exercice 2 : Implémentez un système de scoring de risque :

    def calculate_risk_score(host):
        """Calcule un score de risque basé sur les services"""
        score = 0
        risk_services = {'ftp': 2, 'telnet': 3, 'rdp': 2, 'smb': 2}
        
        for service in host.services:
            if service['status'] == 'open':
                score += risk_services.get(service['service'], 1)
        
        if 'Unknown' in [s['version'] for s in host.services]:
            score += 1
        
        return score
    
    # Classement par risque
    hosts_sorted = sorted(hosts, key=calculate_risk_score, reverse=True)
    print("Hôtes les plus risqués:", [h.ip for h in hosts_sorted[:3]])

🎯 Défi de structuration avancé

Créez un système complet qui :

  1. Lit plusieurs fichiers de scan de formats différents (Nmap, Masscan)
  2. Normalise les données dans une structure unifiée
  3. Détecte les changements entre deux scans (nouveaux services, hôtes)
  4. Génère un rapport de différences avec priorités
  5. Exporte en HTML pour visualisation web

Indices : Utilisez des set() pour les comparaisons, difflib pour les différences textuelles, et un template HTML simple.

📋 Objectif de la semaine – Checklist de validation

✅ Compétences techniques acquises

  • Maîtriser les opérations sur listes, dicts, sets, tuples
  • Écrire des fonctions modulaires avec docstrings
  • Exporter/importer des données en JSON/CSV
  • Utiliser des compréhensions pour transformer des données
  • Structurer un projet avec classes et modules
  • Analyser des données réseau complexes

🧠 Changement mental accompli

  • Penser en termes de structures de données, pas de variables isolées
  • Comprendre les compromis performance/lisibilité/maintenabilité
  • Voir le code comme un système, pas une collection de lignes
  • Appliquer des principes d'architecture logicielle même dans des scripts

🏗️ Bonne pratique d'architecture :

Commencez toujours par modéliser vos données avant d'écrire du code. Posez-vous :

  1. Quelles sont mes entités principales ? (Hôtes, Services, Scans)
  2. Quelles sont leurs relations ? (Un hôte a plusieurs services)
  3. Quelles opérations vais-je faire souvent ? (Recherche, tri, filtre)
  4. Comment vais-je persister ces données ? (JSON, base de données)

📚 Ressources gratuites recommandées

Pour approfondir :

⚠️ Anti-patterns à éviter

  • Listes gigantesques : Pour 100k+ éléments, utilisez des générateurs
  • Dictionnaires trop complexes : Au-delà de 3 niveaux, pensez classes
  • Fonctions trop longues : Une fonction > 50 lignes doit être découpée
  • Variables globales : Préférez le passage de paramètres
  • JSON non validé : Toujours valider les données entrantes

🔮 Frère d'armes,

Tu viens de franchir une étape majeure dans ton apprentissage.

Avant cette semaine, tu écrivais du code. Maintenant, tu architectures des solutions.

La différence entre un script et un outil professionnel ne réside pas dans le nombre de lignes, mais dans :

  • La structure des données
  • La modularité du code
  • La maintenabilité dans le temps
  • La scalabilité face à plus de données

Ton système d'inventaire réseau n'est qu'un début. Les mêmes principes s'appliqueront à :

• L'analyse de logs de sécurité
• La gestion de vulnérabilités
• L'orchestration de scans
• La corrélation d'événements
• La création de dashboards

Tu as appris à penser comme un ingénieur, pas juste comme un scripteur.

Dans la semaine 6, nous quitterons le monde des scripts pour entrer dans celui des réseaux réels. Nous verrons comment les machines communiquent, comment capturer ce trafic, et comment l'analyser pour y détecter des anomalies.

Mais désormais, tu as une compétence fondamentale : tu sais organiser l'information. Et en cybersécurité, l'information organisée, c'est du pouvoir.

Pratique ces concepts. Restructure tes anciens scripts. Améliore leur architecture. Chaque refactoring te rendra meilleur.

Tu n'es plus un débutant. Tu es devenu un architecte.

— Platon-Y