From 7129810b7ce1ee9b957066020dcd16a4a6bef924 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 5 Jan 2026 18:14:36 +0000 Subject: [PATCH] Add SQLMapCLI - Beautiful CLI wrapper with Rich UI Co-authored-by: GilbertKrantz <90319182+GilbertKrantz@users.noreply.github.com> --- README.md | 58 +++++++ sqlmapcli.py | 442 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 500 insertions(+) create mode 100755 sqlmapcli.py diff --git a/README.md b/README.md index e85b3a043..172c4df25 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,64 @@ sqlmap works out of the box with [Python](https://www.python.org/download/) vers Usage ---- +### SQLMap CLI - Beautiful Automated Testing 🎨 + +**NEW**: We now have a beautiful CLI wrapper that automates comprehensive SQL injection testing in a single command! + +#### Quick Start + +Install dependencies: +```bash +pip install -r requirements.txt +``` + +#### Examples + +**Quick scan** (default settings): +```bash +python sqlmapcli.py -u "http://example.com/page?id=1" +``` + +**Comprehensive scan** (tests all risk and level combinations): +```bash +python sqlmapcli.py -u "http://example.com/page?id=1" --comprehensive +``` + +**Custom level and risk**: +```bash +python sqlmapcli.py -u "http://example.com/page?id=1" --level 3 --risk 2 +``` + +**Interactive mode**: +```bash +python sqlmapcli.py --interactive +``` + +#### Features + +✨ **Beautiful output** with Rich library - panels, tables, progress bars +⚡ **One-line comprehensive testing** - test all risk/level combinations automatically +📊 **Clear result summaries** - vulnerability tables with color-coded findings +🎯 **Interactive mode** - guided prompts for easy testing +⏱️ **Progress tracking** - see exactly what's being tested in real-time + +#### CLI Options + +``` +-u, --url Target URL +--comprehensive Run all risk/level combinations (1-3 risk, 1-5 levels) +--level {1-5} Test level (default: 1) +--risk {1-3} Test risk (default: 1) +--max-level {1-5} Maximum level for comprehensive scan +--max-risk {1-3} Maximum risk for comprehensive scan +--technique SQL injection techniques (default: BEUSTQ) +-i, --interactive Interactive mode +``` + +--- + +### Original SQLMap Usage + To get a list of basic options and switches use: python sqlmap.py -h diff --git a/sqlmapcli.py b/sqlmapcli.py new file mode 100755 index 000000000..54b447663 --- /dev/null +++ b/sqlmapcli.py @@ -0,0 +1,442 @@ +#!/usr/bin/env python3 +""" +SQLMap CLI - A beautiful CLI wrapper for sqlmap +Automates comprehensive SQL injection testing with a single command +""" + +import subprocess +import sys +import argparse +import time +import re +from pathlib import Path +from typing import List, Dict, Tuple +from datetime import datetime + +try: + from rich.console import Console + from rich.panel import Panel + from rich.table import Table + from rich.progress import Progress, SpinnerColumn, TextColumn, BarColumn, TimeElapsedColumn + from rich.live import Live + from rich.layout import Layout + from rich.text import Text + from rich.prompt import Prompt, Confirm + from rich import box + from rich.style import Style +except ImportError: + print("Error: 'rich' library is required. Install it with: pip install rich") + sys.exit(1) + +console = Console() + +SQLMAP_PATH = Path(__file__).parent / "sqlmap.py" + +# SQL injection techniques +TECHNIQUES = { + 'B': 'Boolean-based blind', + 'E': 'Error-based', + 'U': 'Union query-based', + 'S': 'Stacked queries', + 'T': 'Time-based blind', + 'Q': 'Inline queries' +} + +class SQLMapCLI: + def __init__(self): + self.console = Console() + self.results = { + 'total_tests': 0, + 'vulnerabilities': [], + 'start_time': None, + 'end_time': None, + 'target': None + } + + def print_banner(self): + """Display a beautiful banner""" + banner = """ +╔═══════════════════════════════════════════════════════════════╗ +║ ║ +║ ███████╗ ██████╗ ██╗ ███╗ ███╗ █████╗ ██████╗ ║ +║ ██╔════╝██╔═══██╗██║ ████╗ ████║██╔══██╗██╔══██╗ ║ +║ ███████╗██║ ██║██║ ██╔████╔██║███████║██████╔╝ ║ +║ ╚════██║██║▄▄ ██║██║ ██║╚██╔╝██║██╔══██║██╔═══╝ ║ +║ ███████║╚██████╔╝███████╗██║ ╚═╝ ██║██║ ██║██║ ║ +║ ╚══════╝ ╚══▀▀═╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ║ +║ ║ +║ CLI - Automated SQL Injection Testing ║ +║ ║ +╚═══════════════════════════════════════════════════════════════╝ + """ + self.console.print(banner, style="bold cyan") + self.console.print( + Panel( + "[yellow]⚠️ Legal Disclaimer: Only use on targets you have permission to test[/yellow]", + border_style="yellow", + box=box.ROUNDED + ) + ) + self.console.print() + + def run_sqlmap_test(self, url: str, level: int, risk: int, technique: str = "BEUSTQ", + batch: bool = True, extra_args: List[str] = None) -> Tuple[bool, str]: + """Run sqlmap with specified parameters""" + cmd = [ + sys.executable, + str(SQLMAP_PATH), + "-u", url, + f"--level={level}", + f"--risk={risk}", + f"--technique={technique}", + "-v", "1" + ] + + if batch: + cmd.append("--batch") + + if extra_args: + cmd.extend(extra_args) + + try: + result = subprocess.run( + cmd, + capture_output=True, + text=True, + timeout=600 # 10 minute timeout per test + ) + return result.returncode == 0, result.stdout + result.stderr + except subprocess.TimeoutExpired: + return False, "Test timed out after 10 minutes" + except Exception as e: + return False, str(e) + + def parse_results(self, output: str) -> Dict: + """Parse sqlmap output for vulnerabilities""" + vulns = [] + + # Look for vulnerability indicators + if "sqlmap identified the following injection point" in output: + # Extract injection details + lines = output.split('\n') + for i, line in enumerate(lines): + if "Parameter:" in line: + param = line.split("Parameter:")[1].strip() + if "Type:" in line: + vuln_type = line.split("Type:")[1].strip() + if i + 1 < len(lines) and "Title:" in lines[i + 1]: + title = lines[i + 1].split("Title:")[1].strip() + vulns.append({ + 'parameter': param if 'param' in locals() else 'Unknown', + 'type': vuln_type, + 'title': title + }) + + # Check for backend DBMS detection + backend_dbms = None + if "back-end DBMS:" in output.lower(): + for line in output.split('\n'): + if "back-end DBMS:" in line.lower(): + backend_dbms = line.split(":", 1)[1].strip() + break + + return { + 'vulnerabilities': vulns, + 'backend_dbms': backend_dbms, + 'is_vulnerable': len(vulns) > 0 or "vulnerable" in output.lower() + } + + def comprehensive_scan(self, url: str, max_level: int = 5, max_risk: int = 3, + techniques: str = "BEUSTQ"): + """Run comprehensive scan with all levels and risks""" + self.results['target'] = url + self.results['start_time'] = datetime.now() + + # Create results table + results_table = Table(title="Scan Results", box=box.ROUNDED) + results_table.add_column("Level", style="cyan", justify="center") + results_table.add_column("Risk", style="yellow", justify="center") + results_table.add_column("Status", justify="center") + results_table.add_column("Findings", style="magenta") + + total_tests = max_level * max_risk + test_count = 0 + + with Progress( + SpinnerColumn(), + TextColumn("[progress.description]{task.description}"), + BarColumn(), + TextColumn("[progress.percentage]{task.percentage:>3.0f}%"), + TimeElapsedColumn(), + console=self.console + ) as progress: + + overall_task = progress.add_task( + f"[cyan]Scanning {url}...", + total=total_tests + ) + + for level in range(1, max_level + 1): + for risk in range(1, max_risk + 1): + test_count += 1 + + progress.update( + overall_task, + description=f"[cyan]Testing Level {level}, Risk {risk}..." + ) + + success, output = self.run_sqlmap_test(url, level, risk, techniques) + parsed = self.parse_results(output) + + status = "✓" if success else "✗" + status_style = "green" if success else "red" + + findings = "No vulnerabilities" if not parsed['is_vulnerable'] else f"{len(parsed['vulnerabilities'])} found!" + findings_style = "green" if not parsed['is_vulnerable'] else "bold red" + + if parsed['is_vulnerable']: + self.results['vulnerabilities'].extend(parsed['vulnerabilities']) + + results_table.add_row( + str(level), + str(risk), + f"[{status_style}]{status}[/{status_style}]", + f"[{findings_style}]{findings}[/{findings_style}]" + ) + + progress.update(overall_task, advance=1) + self.results['total_tests'] += 1 + + self.results['end_time'] = datetime.now() + + # Display results + self.console.print() + self.console.print(results_table) + self.display_summary() + + def quick_scan(self, url: str, level: int = 1, risk: int = 1): + """Run a quick scan with default settings""" + self.results['target'] = url + self.results['start_time'] = datetime.now() + + self.console.print( + Panel( + f"[cyan]Running quick scan on:[/cyan]\n[yellow]{url}[/yellow]\n[dim]Level: {level}, Risk: {risk}[/dim]", + border_style="cyan", + box=box.ROUNDED + ) + ) + + with Progress( + SpinnerColumn(), + TextColumn("[progress.description]{task.description}"), + TimeElapsedColumn(), + console=self.console + ) as progress: + + task = progress.add_task("[cyan]Scanning for vulnerabilities...", total=None) + success, output = self.run_sqlmap_test(url, level, risk) + progress.update(task, completed=True) + + parsed = self.parse_results(output) + self.results['vulnerabilities'] = parsed['vulnerabilities'] + self.results['total_tests'] = 1 + self.results['end_time'] = datetime.now() + + self.display_summary() + + def display_summary(self): + """Display a comprehensive summary of results""" + self.console.print() + + # Calculate duration + duration = (self.results['end_time'] - self.results['start_time']).total_seconds() + + # Create summary panel + summary_text = f""" +[cyan]Target:[/cyan] {self.results['target']} +[cyan]Total Tests:[/cyan] {self.results['total_tests']} +[cyan]Duration:[/cyan] {duration:.2f} seconds +[cyan]Vulnerabilities Found:[/cyan] {len(self.results['vulnerabilities'])} + """ + + self.console.print( + Panel( + summary_text.strip(), + title="[bold]Scan Summary[/bold]", + border_style="green" if len(self.results['vulnerabilities']) == 0 else "red", + box=box.DOUBLE + ) + ) + + # Display vulnerabilities if found + if self.results['vulnerabilities']: + self.console.print() + vuln_table = Table(title="⚠️ Vulnerabilities Detected", box=box.HEAVY) + vuln_table.add_column("Parameter", style="cyan") + vuln_table.add_column("Type", style="yellow") + vuln_table.add_column("Title", style="red") + + for vuln in self.results['vulnerabilities']: + vuln_table.add_row( + vuln.get('parameter', 'N/A'), + vuln.get('type', 'N/A'), + vuln.get('title', 'N/A') + ) + + self.console.print(vuln_table) + self.console.print() + self.console.print( + "[bold red]⚠️ SQL injection vulnerabilities detected! Take immediate action.[/bold red]" + ) + else: + self.console.print() + self.console.print( + "[bold green]✓ No SQL injection vulnerabilities detected.[/bold green]" + ) + + self.console.print() + + def interactive_mode(self): + """Interactive mode for user input""" + self.console.print() + self.console.print( + Panel( + "[cyan]Interactive Mode[/cyan]\n[dim]Enter target details for SQL injection testing[/dim]", + border_style="cyan" + ) + ) + + url = Prompt.ask("\n[cyan]Enter target URL[/cyan]") + + scan_type = Prompt.ask( + "[cyan]Select scan type[/cyan]", + choices=["quick", "comprehensive"], + default="quick" + ) + + if scan_type == "quick": + level = int(Prompt.ask("[cyan]Test level (1-5)[/cyan]", default="1")) + risk = int(Prompt.ask("[cyan]Test risk (1-3)[/cyan]", default="1")) + self.quick_scan(url, level, risk) + else: + max_level = int(Prompt.ask("[cyan]Maximum test level (1-5)[/cyan]", default="5")) + max_risk = int(Prompt.ask("[cyan]Maximum test risk (1-3)[/cyan]", default="3")) + self.comprehensive_scan(url, max_level, max_risk) + + +def main(): + parser = argparse.ArgumentParser( + description="SQLMap CLI - Beautiful automated SQL injection testing", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +Examples: + # Quick scan with default settings + python sqlmapcli.py -u "http://example.com/page?id=1" + + # Comprehensive scan (all risk and level combinations) + python sqlmapcli.py -u "http://example.com/page?id=1" --comprehensive + + # Custom level and risk + python sqlmapcli.py -u "http://example.com/page?id=1" --level 3 --risk 2 + + # Interactive mode + python sqlmapcli.py --interactive + """ + ) + + parser.add_argument( + '-u', '--url', + help='Target URL (e.g., "http://example.com/page?id=1")' + ) + + parser.add_argument( + '--comprehensive', + action='store_true', + help='Run comprehensive scan with all risk/level combinations' + ) + + parser.add_argument( + '--level', + type=int, + default=1, + choices=[1, 2, 3, 4, 5], + help='Level of tests to perform (1-5, default: 1)' + ) + + parser.add_argument( + '--risk', + type=int, + default=1, + choices=[1, 2, 3], + help='Risk of tests to perform (1-3, default: 1)' + ) + + parser.add_argument( + '--max-level', + type=int, + default=5, + choices=[1, 2, 3, 4, 5], + help='Maximum level for comprehensive scan (default: 5)' + ) + + parser.add_argument( + '--max-risk', + type=int, + default=3, + choices=[1, 2, 3], + help='Maximum risk for comprehensive scan (default: 3)' + ) + + parser.add_argument( + '--technique', + type=str, + default='BEUSTQ', + help='SQL injection techniques to use (default: BEUSTQ)' + ) + + parser.add_argument( + '-i', '--interactive', + action='store_true', + help='Run in interactive mode' + ) + + args = parser.parse_args() + + cli = SQLMapCLI() + cli.print_banner() + + # Check if sqlmap exists + if not SQLMAP_PATH.exists(): + console.print( + f"[bold red]Error: sqlmap.py not found at {SQLMAP_PATH}[/bold red]", + style="bold red" + ) + console.print("[yellow]Make sure you're running this script from the sqlmap directory[/yellow]") + sys.exit(1) + + # Interactive mode + if args.interactive: + cli.interactive_mode() + return + + # Check if URL is provided + if not args.url: + console.print("[bold red]Error: URL is required (use -u or --interactive)[/bold red]") + parser.print_help() + sys.exit(1) + + # Run appropriate scan + if args.comprehensive: + cli.comprehensive_scan( + args.url, + max_level=args.max_level, + max_risk=args.max_risk, + techniques=args.technique + ) + else: + cli.quick_scan(args.url, level=args.level, risk=args.risk) + + +if __name__ == "__main__": + main()