Lucene search
+L

FTP Server Enumeration Scanner

🗓️ 21 Jan 2026 10:30:13Reported by KittySploitType 
kittysploit
 kittysploit
🔗 kittysploit.com👁 4 Views

KittySploit FTP Server Enumeration Scanner connects to FTP servers to enumerate data.

Refs
Code
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from kittysploit import *
from lib.protocols.ftp.ftp_client import FTPOptions, FTPClientMixin

class Module(Auxiliary, FTPClientMixin):
    """
    FTP Server Enumeration Scanner (Mode Direct - Sans Session)
    
    Ce module démontre comment FTPClientMixin fonctionne en mode DIRECT :
    - Se connecte directement à un serveur FTP (pas de session existante)
    - Utilise FTPOptions pour la configuration (rhost, rport, ftp_user, ftp_password)
    - Le mixin détecte automatiquement qu'il n'y a pas de session et crée une nouvelle connexion
    
    DIFFÉRENCE avec un module Post :
    - Module Post : utilise self.session (connexion déjà établie)
    - Module Auxiliary : utilise rhost/rport (nouvelle connexion)
    """
    
    __info__ = {
        "name": "FTP Server Enumeration Scanner",
        "description": "Connect to FTP server and enumerate files, directories and server information (Direct mode - no session required)",
        "author": "KittySploit Team",
        "tags": ["ftp", "scanner", "enumeration"],
    'agent': {
        'risk': 'active',
        'effects': ['network_probe'],
        'expected_requests': 2,
        'reversible': True,
        'approval_required': False,
        'produces': ['tech_hints', 'risk_signals', 'endpoints', 'params'],
        'cost': 1.0,
        'noise': 0.5,
        'value': 1.0,
        'requires':         {'min_endpoints': 0,
         'min_params': 0,
         'tech_hints_any': [],
         'tech_hints_all': [],
         'specializations_any': [],
         'risk_signals_any': [],
         'auth_session': False,
         'capabilities_any': [],
         'capabilities_all': [],
         'confidence_min': {},
         'confidence_min_any': {},
         'endpoint_pattern_any': [],
         'param_any': [],
         'api_surface_ready': False},
        'chain':         {'produces_capabilities': [{'capability': 'service_identified', 'from_detail': ''}],
         'consumes_capabilities': [],
         'option_bindings': {},
         'suggested_followups': []},
    },
    }
    
    # Configuration FTP (Mode Direct)
    rhost = OptString("", "Target FTP server IP or hostname", True)
    rport = OptPort(21, "Target FTP port", True)
    ftp_user = OptString("anonymous", "FTP username", True)
    ftp_password = OptString("[email protected]", "FTP password", True)
    timeout = OptInteger(10, "Connection timeout in seconds", True)
    
    # Options de scan
    max_depth = OptInteger(3, "Maximum directory depth to recurse", False)
    show_hidden = OptBool(True, "Show hidden files (starting with .)", False)
    remote_path = OptString("/", "Starting directory path", False)
    
    def run(self):
        """Run FTP enumeration scan"""
        
        print_info("=" * 70)
        print_info("FTP SCANNER - MODE DIRECT (AUXILIARY)")
        print_info("=" * 70)
        print_info()
        
        # Afficher la configuration
        print_status("Configuration:")
        print_info(f"  Target:   {self.rhost}:{self.rport}")
        print_info(f"  Username: {self.ftp_user}")
        print_info(f"  Password: {'*' * len(str(self.ftp_password))}")
        print_info(f"  Timeout:  {self.timeout}s")
        print_info()
        
        try:
            # ===================================================================
            # ÉTAPE 1 : Connexion FTP
            # ===================================================================
            # Le mixin FTPClientMixin détecte automatiquement qu'on est en mode
            # "Direct" car il n'y a pas de self.session, donc il utilise rhost
            print_status("Step 1: Connecting to FTP server...")
            
            ftp = self.get_ftp_connection()
            print_success(f"✓ Connected to {self.rhost}")
            print_info()
            
            # ===================================================================
            # ÉTAPE 2 : Récupérer les informations du serveur
            # ===================================================================
            print_status("Step 2: Gathering server information...")
            
            try:
                welcome_msg = ftp.getwelcome()
                print_info(f"  Welcome Message: {welcome_msg}")
            except:
                print_warning("  No welcome message available")
            
            try:
                current_dir = ftp.pwd()
                print_info(f"  Current Directory: {current_dir}")
            except:
                print_warning("  Could not get current directory")
            
            # Tester la commande SYST
            try:
                system_type = ftp.sendcmd('SYST')
                print_info(f"  System Type: {system_type}")
            except Exception as e:
                print_warning(f"  SYST command failed: {e}")
            
            print_info()
            
            # ===================================================================
            # ÉTAPE 3 : Énumérer les fichiers et dossiers
            # ===================================================================
            print_status("Step 3: Enumerating files and directories...")
            print_info()
            
            # Utiliser la méthode list_files() du mixin
            # Cette méthode fonctionne aussi bien en mode Direct qu'en mode Session!
            files = self.list_files(self.remote_path)
            
            if not files:
                print_warning("No files found or access denied")
                return True
            
            # Séparer les dossiers et les fichiers
            directories = []
            regular_files = []
            
            for file_info in files:
                name = file_info.get('name', '')
                file_type = file_info.get('type', 'unknown')
                
                # Filtrer les fichiers cachés si demandé
                if not self.show_hidden and name.startswith('.'):
                    continue
                
                if file_type == 'directory':
                    directories.append(file_info)
                else:
                    regular_files.append(file_info)
            
            # Afficher les statistiques
            print_success(f"Found {len(directories)} directories and {len(regular_files)} files")
            print_info()
            
            # ===================================================================
            # ÉTAPE 4 : Afficher les résultats
            # ===================================================================
            
            # Afficher les dossiers
            if directories:
                print_status("Directories:")
                dir_table = []
                for d in directories:
                    dir_table.append([
                        "📁 " + d.get('name', ''),
                        d.get('date', ''),
                        d.get('size', '')
                    ])
                print_table(['Name', 'Date', 'Size'], dir_table)
                print_info()
            
            # Afficher les fichiers
            if regular_files:
                print_status("Files:")
                file_table = []
                for f in regular_files:
                    file_table.append([
                        "📄 " + f.get('name', ''),
                        f.get('date', ''),
                        f.get('size', '') + " bytes"
                    ])
                print_table(['Name', 'Date', 'Size'], file_table)
                print_info()
            
            # ===================================================================
            # ÉTAPE 5 : Tester d'autres commandes FTP
            # ===================================================================
            print_status("Step 4: Testing FTP capabilities...")
            
            # Test FEAT (FTP Features)
            try:
                features = ftp.sendcmd('FEAT')
                print_info("  Supported Features:")
                for line in features.split('\n')[1:-1]:  # Skip first and last line
                    print_info(f"    {line.strip()}")
            except Exception as e:
                print_warning(f"  FEAT command not supported: {e}")
            
            print_info()
            
            # ===================================================================
            # RÉSUMÉ : Comment ça fonctionne
            # ===================================================================
            print_info("=" * 70)
            print_success("SCAN COMPLETED")
            print_info("=" * 70)
            print_info()
            print_info("💡 HOW IT WORKS (Mode Direct):")
            print_info()
            print_info("1. Ce module hérite de 'Auxiliary' et 'FTPClientMixin'")
            print_info("2. Il définit des options FTP : rhost, rport, ftp_user, ftp_password")
            print_info("3. Quand on appelle get_ftp_connection():")
            print_info("   → Le mixin détecte qu'il n'y a PAS de self.session")
            print_info("   → Il utilise donc rhost pour créer une NOUVELLE connexion")
            print_info("   → Il appelle _get_direct_client() automatiquement")
            print_info()
            print_info("4. Toutes les méthodes du mixin fonctionnent ensuite:")
            print_info("   → list_files(), download_file(), change_directory(), etc.")
            print_info()
            print_info("=" * 70)
            print_info()
            print_info("🔄 COMPARAISON avec un module Post (Session):")
            print_info()
            print_info("Mode Post (avec session FTP existante):")
            print_info("  ✓ Hérite de 'Post' (pas 'Auxiliary')")
            print_info("  ✓ Utilise self.session (connexion déjà établie)")
            print_info("  ✓ get_ftp_connection() détecte self.session")
            print_info("  ✓ Utilise _get_session_client() automatiquement")
            print_info("  ✓ Pas besoin de rhost/rport (déjà connecté!)")
            print_info()
            print_info("Mode Auxiliary (sans session - comme ce module):")
            print_info("  ✓ Hérite de 'Auxiliary' (pas 'Post')")
            print_info("  ✓ Définit rhost, rport, ftp_user, ftp_password")
            print_info("  ✓ get_ftp_connection() détecte l'absence de session")
            print_info("  ✓ Crée une nouvelle connexion avec ces paramètres")
            print_info("  ✓ Utilise _get_direct_client() automatiquement")
            print_info()
            print_info("=" * 70)
            print_info()
            print_success("✓ Une seule bibliothèque FTPClientMixin pour les deux modes!")
            print_info()
            
            # Fermer la connexion proprement
            try:
                ftp.quit()
                print_status("Connection closed cleanly")
            except:
                ftp.close()
                print_status("Connection closed")
            
            return True
            
        except Exception as e:
            print_error(f"FTP scan failed: {e}")
            import traceback
            print_error(traceback.format_exc())
            return False

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

16 Jul 2026 12:39Current
5.8Medium risk
Vulners AI Score5.8
4