Lucene search
+L

FTP Gather User Information

🗓️ 01 Dec 2025 12:51:31Reported by KittySploitType 
kittysploit
 kittysploit
🔗 kittysploit.com

KittySploit FTP Gather User Information module enumerates FTP users and anonymous access.

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

from kittysploit import *
from lib.protocols.ftp.ftp_client import FTPClientMixin
from ftplib import FTP, error_perm
import socket
import time

class Module(Post, FTPClientMixin):
    """FTP User Enumeration Module"""
    
    __info__ = {
        "name": "FTP Gather User Information",
        "description": "Enumerates FTP users, checks for anonymous access, and gathers connection information",
        "author": "KittySploit Team",
        "session_type": SessionType.FTP,
    'agent': {
        'risk': 'intrusive',
        'effects': ['active_exploitation'],
        'expected_requests': 2,
        'reversible': False,
        'approval_required': True,
        'produces': ['risk_signals'],
        'cost': 1.5,
        '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': [],
         'consumes_capabilities': [],
         'option_bindings': {},
         'suggested_followups': []},
    },
    }
    
    check_anonymous = OptBool(True, "Check for anonymous FTP access", False)
    test_common_users = OptBool(True, "Test common usernames", False)
    common_users = OptString("admin,root,ftp,user,test,guest,anonymous", "Common usernames to test (comma-separated)", False)
    
    def check(self):
        """Check if the module can run"""
        session_id_value = str(self.session_id)
        if not session_id_value:
            print_error("Session ID not set")
            return False
        
        if not self.framework or not hasattr(self.framework, 'session_manager'):
            print_error("Framework or session manager not available")
            return False
        
        session = self.framework.session_manager.get_session(session_id_value)
        if not session:
            print_error(f"Session {session_id_value} not found")
            return False
        
        # Try to get FTP connection to verify it works
        try:
            self.open_ftp()
            return True
        except Exception as e:
            print_error(f"FTP connection error: {e}")
            return False
    
    def _test_anonymous_access(self, host: str, port: int) -> dict:
        """Test anonymous FTP access"""
        result = {
            'enabled': False,
            'writable': False,
            'readable': False,
            'message': ''
        }
        
        try:
            ftp = FTP()
            ftp.connect(host, port, timeout=5)
            ftp.login('anonymous', 'anonymous@')
            
            result['enabled'] = True
            result['message'] = ftp.getwelcome()
            
            # Check if we can read
            try:
                ftp.retrlines('LIST')
                result['readable'] = True
            except:
                pass
            
            # Check if we can write
            try:
                test_file = '.test_write_access'
                ftp.storbinary(f'STOR {test_file}', b'test')
                ftp.delete(test_file)
                result['writable'] = True
            except:
                pass
            
            ftp.quit()
            
        except error_perm:
            result['enabled'] = False
            result['message'] = "Anonymous access denied"
        except Exception as e:
            result['message'] = f"Error: {e}"
        
        return result
    
    def _test_user_login(self, host: str, port: int, username: str, password: str = "") -> bool:
        """Test if a username/password combination works"""
        try:
            ftp = FTP()
            ftp.connect(host, port, timeout=5)
            ftp.login(username, password)
            ftp.quit()
            return True
        except:
            return False
    
    def _get_ftp_banner(self, host: str, port: int) -> str:
        """Get FTP server banner"""
        try:
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.settimeout(5)
            sock.connect((host, port))
            banner = sock.recv(1024).decode('utf-8', errors='ignore').strip()
            sock.close()
            return banner
        except:
            return "Could not retrieve banner"
    
    def _get_current_user_info(self) -> dict:
        """Get information about current FTP user"""
        info = {}
        
        try:
            connection = self.open_ftp()
            if connection:
                # Get current directory
                try:
                    info['current_directory'] = connection.pwd()
                except:
                    info['current_directory'] = "Unknown"
                
                # Try to get system info
                try:
                    info['system'] = connection.sendcmd('SYST')
                except:
                    info['system'] = "Unknown"
                
                # Try to get features
                try:
                    features = connection.sendcmd('FEAT')
                    info['features'] = features.split('\n')[1:] if '\n' in features else [features]
                except:
                    info['features'] = []
                
                # Check permissions by trying common operations
                info['can_list'] = False
                info['can_read'] = False
                info['can_write'] = False
                info['can_delete'] = False
                
                try:
                    connection.retrlines('LIST')
                    info['can_list'] = True
                except:
                    pass
                
                try:
                    # Try to read a file (test with pwd or similar)
                    connection.pwd()
                    info['can_read'] = True
                except:
                    pass
                
                try:
                    # Try to create a test file
                    test_file = '.test_write_' + str(int(time.time()))
                    connection.storbinary(f'STOR {test_file}', b'test')
                    connection.delete(test_file)
                    info['can_write'] = True
                    info['can_delete'] = True
                except:
                    pass
        
        except Exception as e:
            info['error'] = str(e)
        
        return info
    
    def run(self):
        """Run the user enumeration"""
        try:
            print_status("FTP User Enumeration")

            # Get connection info
            conn_info = self.get_ftp_connection_info()
            host = conn_info.get('host', 'localhost')
            port = conn_info.get('port', 21)
            current_user = conn_info.get('username', 'unknown')
            
            print_status(f"FTP Server: {host}:{port}")
            print_status(f"Current User: {current_user}")
            print_info()
            
            # Get FTP banner
            print_status("FTP Server Information:")
            banner = self._get_ftp_banner(host, port)
            print_status(f"Banner: {banner}")
            print_info()
            
            # Get current user information
            print_status("Current User Information:")
            user_info = self._get_current_user_info()
            
            if 'current_directory' in user_info:
                print_status(f"Current Directory: {user_info['current_directory']}")
            
            if 'system' in user_info:
                print_status(f"System Type: {user_info['system']}")

            if 'features' in user_info and user_info['features']:
                print_status("Server Features:")
                for feature in user_info['features'][:10]:  # Limit to first 10
                    if feature.strip():
                        print_status(f"  - {feature.strip()}")
            
            print_status("Permissions:")
            if 'can_list' in user_info:
                status = "✓" if user_info['can_list'] else "✗"
                print_status(f"  {status} List directories")
            if 'can_read' in user_info:
                status = "✓" if user_info['can_read'] else "✗"
                print_status(f"  {status} Read files")
            if 'can_write' in user_info:
                status = "✓" if user_info['can_write'] else "✗"
                print_status(f"  {status} Write files")
            if 'can_delete' in user_info:
                status = "✓" if user_info['can_delete'] else "✗"
                print_status(f"  {status} Delete files")
            
            print_info()
            
            # Test anonymous access
            if self.check_anonymous:
                print_status("Anonymous Access Test:")
                anon_result = self._test_anonymous_access(host, port)
                
                if anon_result['enabled']:
                    print_success("Anonymous FTP access is ENABLED!")
                    print_status(f"Welcome message: {anon_result['message']}")
                    if anon_result['readable']:
                        print_status("  - Anonymous users can READ files")
                    if anon_result['writable']:
                        print_success("  - Anonymous users can WRITE files (CRITICAL!)")
                else:
                    print_error("Anonymous FTP access is disabled")
                    print_status(f"Response: {anon_result['message']}")
                
                print_info()
            
            # Test common usernames
            if self.test_common_users:
                print_status("Common Username Testing:")
                print_status("Testing common usernames (passwordless)...")
                
                common_users_list = [u.strip() for u in self.common_users.split(',')]
                found_users = []
                
                for username in common_users_list:
                    if username.lower() == current_user.lower():
                        continue  # Skip current user
                    
                    if self._test_user_login(host, port, username, ""):
                        found_users.append(username)
                        print_success(f"{username} - Passwordless login works!")
                    else:
                        print_info(f"{username}")
                
                if found_users:
                    print_success(f"Found {len(found_users)} users with passwordless access!")
                else:
                    print_error("No passwordless logins found for common usernames")
                
                print_info()
            
            print_status("Enumeration complete!")
            
            return True
            
        except ProcedureError:
            raise
        except Exception as e:
            raise ProcedureError(FailureType.Unknown, f"Error enumerating users: {e}")

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

10 Jul 2026 11:31Current
5.8Medium risk
Vulners AI Score5.8
0