diff --git a/Changelog.md b/Changelog.md
@@ -3,6 +3,7 @@
- feat(scan): saving wappalyzer results to file ~ @djnnvx
- feat(scan): adding cvemap module ~ @djnnvx
- fix(scan): adding --no-dirbusting ~ @djnnvx
+- feat(meta): adding run-web-recon script ~ @djnnvx
## v0.0.3
diff --git a/quick-run.sh b/inventory/quick-run.sh
diff --git a/inventory/run-web-recon b/inventory/run-web-recon
@@ -0,0 +1,213 @@
+#!/bin/env python
+
+"""
+ This script runs a various set of commands in a "smart" way to run recon.
+ It has a big set of dependencies, but should work against a standard kali
+ install.
+"""
+
+import argparse
+import os
+import subprocess
+import shutil
+import shlex
+from typing import Optional, Dict, Any
+
+class ShellCommandRunner:
+ """
+ A class for safely running shell commands and checking for program existence.
+
+ It uses the modern subprocess.run() API for execution and provides structured
+ results for easy parsing and decision-making in Python.
+ """
+
+ def __init__(self):
+ """
+ Initializes the ShellCommandRunner.
+ """
+
+ def _get_program_name(self, command: str) -> str:
+ """
+ Internal helper to reliably extract the program name from a command string.
+ Uses shlex.split to handle quoted arguments correctly.
+ """
+ try:
+ parts = shlex.split(command)
+ return parts[0]
+ except ValueError:
+ return command.split()[0] if command.strip() else ""
+ except IndexError:
+ return ""
+
+ def is_callable(self, command: str) -> bool:
+ """
+ Checks if the main program specified in the command string is executable
+ and can be found in the system's PATH environment variable.
+
+ Args:
+ command: The full command string (e.g., 'ls -la /tmp').
+
+ Returns:
+ True if the program is found and executable, False otherwise.
+ """
+ program_name = self._get_program_name(command)
+ if not program_name:
+ return False
+
+ return shutil.which(program_name) is not None
+
+ def execute_command(self, command: str, timeout: int = 15) -> Dict[str, Any]:
+ """
+ Safely executes a shell command and captures its output and status.
+
+ Execution is performed without using shell=True for security.
+
+ Args:
+ command: The full command string (e.g., 'grep "error" logfile.txt').
+ timeout: Maximum time (in seconds) to wait for the command to finish.
+
+ Returns:
+ A dictionary containing:
+ - 'success': bool (True if execution succeeded and exit code was 0)
+ - 'exit_code': int (The return code of the command)
+ - 'stdout': str (Standard output)
+ - 'stderr': str (Standard error)
+ - 'error': Optional[str] (Python exception description if one occurred)
+ """
+ # Default failure result
+ result = {
+ 'success': False,
+ 'exit_code': -1,
+ 'stdout': '',
+ 'stderr': '',
+ 'error': None
+ }
+
+ program_name = self._get_program_name(command)
+
+ if not self.is_callable(command):
+ result['error'] = f"Program '{program_name}' not found or is not callable on the system PATH."
+ return result
+
+ try:
+ args = shlex.split(command)
+
+ proc = subprocess.run(
+ args,
+ capture_output=True,
+ text=True,
+ check=False,
+ timeout=timeout
+ )
+
+ result['exit_code'] = proc.returncode
+ result['stdout'] = proc.stdout.strip()
+ result['stderr'] = proc.stderr.strip()
+ result['success'] = proc.returncode == 0
+
+ except subprocess.TimeoutExpired:
+ result['error'] = f"Command timed out after {timeout} seconds."
+ except OSError as e:
+ result['error'] = f"OS Error during execution: {e}"
+ except Exception as e:
+ result['error'] = f"An unexpected error occurred: {e}"
+
+ return result
+
+def shexec(command):
+
+ runner = ShellCommandRunner()
+ res = runner.execute_command(command)
+
+ if not res['success']:
+ print(res['stderr'])
+ os.exit(1)
+
+ print(f'>>> {command} <<<')
+ print(res['stdout'] + '\n--------------------------------------------------------------------------------------------\n\n')
+ return res['stdout']
+
+
+if __name__ == '__main__':
+
+ # --- 1. Load arg-parser
+ parser = argparse.ArgumentParser(description="run reconaissance quickly & integrate mutliple tools (nmap, yellow, ...).")
+ parser.add_argument(
+ '--domain',
+ type=str,
+ required=True,
+ help='The primary domain to process (e.g., example.com)'
+ )
+
+ parser.add_argument(
+ '--wordlist',
+ type=str,
+ required=False,
+ help='main wordlist to use'
+ )
+
+ parser.add_argument(
+ '--proxy',
+ type=str,
+ required=False,
+ help='proxy to use if any'
+ )
+
+ parser.add_argument(
+ '--rate-limit',
+ dest='rate_limit',
+ type=int,
+ default=1000,
+ help='The maximum number of requests per unit of time (integer).'
+ )
+
+ parser.add_argument(
+ '--force-insecure',
+ action='store_true',
+ help='Force HTTP instead of HTTPS'
+ )
+
+ parser.add_argument(
+ '--verbose',
+ action='store_true',
+ help='Enable verbose output for debugging and detailed logging.'
+ )
+
+ args = parser.parse_args()
+ runner = ShellCommandRunner()
+
+ # --- 2. Check dependencies
+ deps = ['yellow', 'nmap'] # TODO: , 'nxc', 'ffuf', 'nikto', 'sqlmap']
+ for d in deps:
+ if not runner.is_callable(d):
+ print(f'[!] missing dependency: {d}')
+ os.exit(1)
+
+
+ ratelimit_opt = ''
+ if args.rate_limit:
+ ratelimit_opt = f'--rate-limit {ratelimit}'
+
+ insecure_opt = ''
+ if args.force_insecure:
+ insecure_opt = f'--insecure'
+
+ ratelimit_opt = ''
+ if args.proxy:
+ proxy_opt = f'--proxy {proxy}'
+
+ # --- 3. run yellow
+ if args.verbose:
+ print('[+] running yellow (osint & scan)')
+
+ shexec(f'yellow -d {parser.domain}')
+ shexec(f'yellow osint -d {parser.domain} {proxy_opt} {ratelimit_opt} ')
+ shexec(f'yellow scan -d {parser.domain} {proxy_opt} {ratelimit_opt} {insecure_opt} --file {parser.domain}/scans/domains.txt')
+
+ os.chdir(parser.domain)
+
+ # --- 3. nmap all IPs quickly
+ nmap_scan = shexec('nmap -sS -T4 -iL scans/domains.txt -oN scans/global-nmap')
+
+
+
| | |