mirror of
https://github.com/sqlmapproject/sqlmap.git
synced 2026-01-14 04:19:03 +03:00
Add SQLMapCLI - Beautiful CLI wrapper with Rich UI
Co-authored-by: GilbertKrantz <90319182+GilbertKrantz@users.noreply.github.com>
This commit is contained in:
parent
84211142a8
commit
7129810b7c
58
README.md
58
README.md
|
|
@ -25,6 +25,64 @@ sqlmap works out of the box with [Python](https://www.python.org/download/) vers
|
||||||
Usage
|
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:
|
To get a list of basic options and switches use:
|
||||||
|
|
||||||
python sqlmap.py -h
|
python sqlmap.py -h
|
||||||
|
|
|
||||||
442
sqlmapcli.py
Executable file
442
sqlmapcli.py
Executable file
|
|
@ -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()
|
||||||
Loading…
Reference in New Issue
Block a user