Created
August 13, 2025 18:00
-
-
Save ArkaprabhaChakraborty/20d382e7b7d2089f688e15ae065f3b24 to your computer and use it in GitHub Desktop.
FTP enumeration
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import ftplib | |
| from datetime import datetime | |
| import argparse | |
| import os | |
| def anonymous_ftp_enum(hostname, port=21, timeout=10): | |
| """ | |
| Log into FTP server with anonymous credentials and enumerate accessible content. | |
| Args: | |
| hostname (str): FTP server hostname or IP address | |
| port (int): FTP server port (default: 21) | |
| timeout (int): Connection timeout in seconds (default: 10) | |
| """ | |
| print(f"\n{'='*50}") | |
| print(f"Scanning target: {hostname}:{port}") | |
| print(f"{'='*50}") | |
| try: | |
| # Create FTP connection | |
| print(f"[*] Connecting to {hostname}:{port}") | |
| ftp = ftplib.FTP(timeout=timeout) | |
| ftp.connect(hostname, port) | |
| # Attempt anonymous login | |
| print("[*] Attempting anonymous login...") | |
| ftp.login('anonymous', '[email protected]') | |
| print("[+] Anonymous login successful!") | |
| # Get welcome message | |
| welcome_msg = ftp.getwelcome() | |
| print(f"\n[+] Server Welcome Message:\n{welcome_msg}\n") | |
| # Get system type | |
| syst_type = ftp.sendcmd('SYST') | |
| print(f"[+] System Type: {syst_type}") | |
| # Get current directory | |
| current_dir = ftp.pwd() | |
| print(f"[+] Current Directory: {current_dir}") | |
| # List files and directories recursively | |
| print("\n[+] Directory Listing (recursive):") | |
| list_ftp_contents(ftp) | |
| # Check for interesting files | |
| check_interesting_files(ftp) | |
| # Check write permissions | |
| check_write_permissions(ftp) | |
| except ftplib.all_errors as e: | |
| print(f"[-] FTP Error: {e}") | |
| finally: | |
| try: | |
| ftp.quit() | |
| print("\n[*] Connection closed") | |
| except: | |
| pass | |
| def list_ftp_contents(ftp, path='.'): | |
| """ | |
| Recursively list FTP contents with details. | |
| """ | |
| try: | |
| files = [] | |
| ftp.dir(path, files.append) | |
| for file in files: | |
| # Parse the line (format typically like: "drwxr-xr-x 2 owner group ... name") | |
| parts = file.split() | |
| if len(parts) < 9: | |
| continue | |
| permissions = parts[0] | |
| name = ' '.join(parts[8:]) | |
| full_path = f"{path}/{name}" if path != '.' else name | |
| # Skip current and parent directory markers | |
| if name in ('.', '..'): | |
| continue | |
| # Print file/directory info | |
| print(f" {permissions} {full_path}") | |
| # If directory, recurse into it | |
| if permissions.startswith('d'): | |
| try: | |
| list_ftp_contents(ftp, full_path) | |
| except ftplib.error_perm: | |
| print(f" [!] Cannot access directory: {full_path}") | |
| except ftplib.error_perm as e: | |
| print(f" [!] Cannot list directory {path}: {e}") | |
| def check_interesting_files(ftp): | |
| """ | |
| Check for common interesting files. | |
| """ | |
| interesting_files = [ | |
| 'readme.txt', 'README', 'NOTES', 'TODO', 'CHANGES', | |
| 'backup', 'backups', 'database', 'db', 'config', | |
| 'configuration', 'credentials', 'password', 'passwords', | |
| 'users', 'accounts', 'logs', 'access.log', 'error.log' | |
| ] | |
| print("\n[+] Checking for interesting files:") | |
| found_files = [] | |
| def check_directory(path): | |
| try: | |
| files = [] | |
| ftp.dir(path, files.append) | |
| for file in files: | |
| parts = file.split() | |
| if len(parts) < 9: | |
| continue | |
| name = ' '.join(parts[8:]) | |
| full_path = f"{path}/{name}" if path != '.' else name | |
| # Check if file is interesting | |
| lower_name = name.lower() | |
| for interesting in interesting_files: | |
| if interesting in lower_name: | |
| found_files.append(full_path) | |
| print(f" [!] Found interesting file: {full_path}") | |
| # If directory, recurse | |
| if parts[0].startswith('d') and name not in ('.', '..'): | |
| check_directory(full_path) | |
| except ftplib.error_perm: | |
| pass | |
| check_directory('.') | |
| if not found_files: | |
| print(" [-] No interesting files found") | |
| def check_write_permissions(ftp): | |
| """ | |
| Check if anonymous user has write permissions. | |
| """ | |
| print("\n[+] Checking write permissions:") | |
| test_file = f"test_{datetime.now().strftime('%Y%m%d%H%M%S')}.txt" | |
| test_content = b"FTP write permission test file - safe to delete" | |
| try: | |
| # Try to upload a file | |
| print(f" [*] Attempting to upload test file: {test_file}") | |
| ftp.storbinary(f"STOR {test_file}", test_content) | |
| print(" [+] Write permissions available!") | |
| # Clean up - delete the test file | |
| try: | |
| ftp.delete(test_file) | |
| print(" [*] Test file deleted") | |
| except: | |
| print(" [!] Could not delete test file") | |
| except ftplib.error_perm as e: | |
| print(f" [-] No write permissions: {e}") | |
| def read_targets_from_file(file_path): | |
| """ | |
| Read targets from a file, one per line. | |
| Returns list of targets with port if specified (target:port format) | |
| """ | |
| targets = [] | |
| try: | |
| with open(file_path, 'r') as f: | |
| for line in f: | |
| line = line.strip() | |
| if line and not line.startswith('#'): | |
| if ':' in line: | |
| target, port = line.split(':', 1) | |
| targets.append((target, int(port))) | |
| else: | |
| targets.append((line, 21)) # Default port | |
| except Exception as e: | |
| print(f"[-] Error reading target file: {e}") | |
| return None | |
| return targets | |
| if __name__ == "__main__": | |
| parser = argparse.ArgumentParser(description="FTP Anonymous Login and Enumeration Tool") | |
| parser.add_argument("target", nargs='?', help="Single target (hostname/IP) or file containing targets") | |
| parser.add_argument("-p", "--port", type=int, default=21, help="FTP server port (default: 21)") | |
| parser.add_argument("-t", "--timeout", type=int, default=10, help="Connection timeout in seconds (default: 10)") | |
| parser.add_argument("-f", "--file", help="File containing list of targets (one per line)") | |
| args = parser.parse_args() | |
| if not args.target and not args.file: | |
| parser.print_help() | |
| exit(1) | |
| targets = [] | |
| # Handle file input | |
| if args.file: | |
| if not os.path.isfile(args.file): | |
| print(f"[-] File not found: {args.file}") | |
| exit(1) | |
| file_targets = read_targets_from_file(args.file) | |
| if file_targets: | |
| targets.extend(file_targets) | |
| # Handle single target input | |
| if args.target: | |
| targets.append((args.target, args.port)) | |
| if not targets: | |
| print("[-] No valid targets specified") | |
| exit(1) | |
| print(f"\nFTP Anonymous Enumeration Tool - {len(targets)} target(s) to scan\n") | |
| for target, port in targets: | |
| anonymous_ftp_enum(target, port, args.timeout) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment