Skip to content

Instantly share code, notes, and snippets.

@ArkaprabhaChakraborty
Created August 13, 2025 18:00
Show Gist options
  • Save ArkaprabhaChakraborty/20d382e7b7d2089f688e15ae065f3b24 to your computer and use it in GitHub Desktop.
Save ArkaprabhaChakraborty/20d382e7b7d2089f688e15ae065f3b24 to your computer and use it in GitHub Desktop.
FTP enumeration
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