sqlmap/sqlmapcli.py
Wilbert Chandra 2270c8981b Add UI and utility functions for SQL injection testing
- Implemented a new UI module (sql_cli/ui.py) for displaying banners and scan results using the Rich library.
- Created utility functions in sql_cli/utils.py for generating log filenames and saving logs.
- Refactored sqlmapcli.py to utilize the new UI and utility functions, enhancing the interactive mode and scan processes.
- Added support for custom headers and POST data in the interactive mode.
- Introduced a test endpoints JSON file (test_endpoints.json) for batch testing.
2026-01-07 12:49:14 +00:00

193 lines
6.8 KiB
Python
Executable File

#!/usr/bin/env python3
"""
SQLMap CLI - A beautiful CLI wrapper for sqlmap
Automates comprehensive SQL injection testing with a single command
"""
import sys
import argparse
import json
from pathlib import Path
# Add the current directory to path so we can import from sql_cli
sys.path.append(str(Path(__file__).parent))
try:
from rich.console import Console
from rich.panel import Panel
from rich.prompt import Prompt, Confirm
except ImportError:
print("Error: 'rich' library is required. Install it with: pip install rich")
sys.exit(1)
from sql_cli.scanner import SQLMapScanner
from sql_cli.utils import SQLMAP_PATH
from sql_cli.ui import print_banner
console = Console()
def interactive_mode(scanner: SQLMapScanner):
"""Interactive mode for user input"""
console.print()
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]")
# Ask if this is a POST request
has_data = Confirm.ask(
"[cyan]Does this request require POST data/body?[/cyan]", default=False
)
data = None
if has_data:
console.print("\n[dim]Examples:[/dim]")
console.print(
'[dim] JSON: {"email":"test@example.com","password":"pass123"}[/dim]'
)
console.print("[dim] Form: username=admin&password=secret[/dim]")
data = Prompt.ask("\n[cyan]Enter POST data/body[/cyan]")
# Ask for custom headers
has_headers = Confirm.ask(
"[cyan]Do you need to add custom headers (Auth, etc.)?[/cyan]", default=False
)
headers = None
if has_headers:
console.print("\n[dim]Example:[/dim]")
console.print(
'[dim] "Authorization: Bearer token; Cookie: PHPSESSID=..."[/dim]'
)
headers = Prompt.ask("\n[cyan]Enter headers[/cyan]")
scan_type = Prompt.ask(
"\n[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"))
scanner.quick_scan(url, level, risk, data=data, headers=headers)
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")
)
scanner.comprehensive_scan(url, max_level, max_risk, data=data, headers=headers)
def main():
parser = argparse.ArgumentParser(
description="SQLMap CLI - Beautiful automated SQL injection testing",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
# Quick scan with default settings (GET parameter)
python sqlmapcli.py -u "https://demo.owasp-juice.shop/rest/products/search?q=test"
# Test with POST data (JSON)
python sqlmapcli.py -u "https://demo.owasp-juice.shop/rest/user/login" --data='{"email":"test@example.com","password":"pass123"}'
# Comprehensive scan (all risk and level combinations)
python sqlmapcli.py -u "https://demo.owasp-juice.shop/rest/products/search?q=test" --comprehensive
# Batch mode - test multiple endpoints from JSON file
python sqlmapcli.py -b endpoints.json --level 2 --risk 2 --concurrency 10
# 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")
parser.add_argument("--level", type=int, default=1, choices=[1, 2, 3, 4, 5], help="Level (1-5, default: 1)")
parser.add_argument("--risk", type=int, default=1, choices=[1, 2, 3], help="Risk (1-3, default: 1)")
parser.add_argument("--max-level", type=int, default=5, choices=[1, 2, 3, 4, 5], help="Max level for comprehensive")
parser.add_argument("--max-risk", type=int, default=3, choices=[1, 2, 3], help="Max risk for comprehensive")
parser.add_argument("--technique", type=str, default="BEUSTQ", help="SQL techniques (default: BEUSTQ)")
parser.add_argument("--data", type=str, help='POST data')
parser.add_argument("--headers", type=str, help='Extra headers')
parser.add_argument("--raw", action="store_true", help="Show raw sqlmap output")
parser.add_argument("--verbose", type=int, choices=[0, 1, 2, 3, 4, 5, 6], help="Verbosity (0-6)")
parser.add_argument("-i", "--interactive", action="store_true", help="Interactive mode")
parser.add_argument('-b', '--batch-file', type=str, help='Path to batch JSON')
parser.add_argument('-c', '--concurrency', type=int, default=5, help='Concurrency (default: 5)')
parser.add_argument('--no-logs', action='store_true', help='Disable logs')
args = parser.parse_args()
scanner = SQLMapScanner(enable_logging=not args.no_logs)
print_banner()
if not SQLMAP_PATH.exists():
console.print(f"[bold red]Error: sqlmap.py not found at {SQLMAP_PATH}[/bold red]")
sys.exit(1)
if args.interactive:
interactive_mode(scanner)
return
if args.batch_file:
try:
with open(args.batch_file, 'r') as f:
endpoints = json.load(f)
if not isinstance(endpoints, list):
console.print("[bold red]Error: Batch file must contain a JSON array[/bold red]")
sys.exit(1)
verbose_level = args.verbose if args.verbose is not None else 1
scanner.batch_scan(
endpoints,
level=args.level,
risk=args.risk,
concurrency=args.concurrency,
verbose=verbose_level
)
return
except Exception as e:
console.print(f"[bold red]Error loading batch file: {e}[/bold red]")
sys.exit(1)
if not args.url:
console.print("[bold red]Error: URL is required[/bold red]")
parser.print_help()
sys.exit(1)
verbose_level = args.verbose if args.verbose is not None else 1
if args.comprehensive:
scanner.comprehensive_scan(
args.url,
max_level=args.max_level,
max_risk=args.max_risk,
techniques=args.technique,
data=args.data,
headers=args.headers,
verbose=verbose_level,
)
else:
scanner.quick_scan(
args.url,
level=args.level,
risk=args.risk,
data=args.data,
headers=args.headers,
raw=args.raw,
verbose=verbose_level,
)
if __name__ == "__main__":
main()