mirror of
https://github.com/0xMarcio/PentestPilot.git
synced 2026-02-13 05:22:54 +00:00
Initial commit of PentestPilot — AI‑assisted pentest recon and orchestration toolkit.\n\nHighlights:\n- Resumeable pipelines (full_pipeline) with manifest state and elapsed timings\n- Rich dashboard (colors, severity bars, durations, compact/json modes)\n- Web helpers: httpx→nuclei auto, tech routing + quick scanners\n- Agents: multi‑task orchestrator (web/full/ad/notes/post) with resume\n- AD/SMB, password utils, shells, transfer, privesc, tunnels\n- QoL scripts: proxy toggle, cleanup, tmux init, URL extractor\n- Docs: README (Quick Start + Docs Index), HOWTO (deep guide), TOOLKIT (catalog with examples)\n\nStructure:\n- bin/automation: pipelines, dashboard, manifest, resume, tech_actions\n- bin/web: routing, scanners, helpers\n- bin/ai: orchestrators + robust AI utils\n- bin/ad, bin/passwords, bin/shells, bin/transfer, bin/privesc, bin/misc, bin/dns, bin/scan, bin/windows, bin/hashes\n- HOWTO.md and TOOLKIT.md cross‑linked with examples\n\nUse:\n- settarget <target>; agent full <domain|hosts.txt>; dashboard --compact\n- See HOWTO.md for setup, semantics, and examples.
135 lines
5.3 KiB
Python
Executable File
135 lines
5.3 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
import sys, os, json, subprocess, tempfile
|
|
|
|
HELP = """Usage: httpx_tech_route.py <host|file> [--tech list] [--severity sevlist] [--wpscan] [--wpscan-limit N] [--extra] [--extra-limit N] [--dry-run]
|
|
Runs httpx -json -tech-detect, groups URLs by technologies, and runs nuclei per tech presets.
|
|
Tech presets map:
|
|
wordpress -> nuclei tags: wordpress (+ optional wpscan_quick)
|
|
drupal, joomla, laravel, aspnet, spring, tomcat, iis, exchange, sharepoint, grafana, kibana, gitlab, confluence, jupyter -> nuclei tag same as tech
|
|
"""
|
|
|
|
if len(sys.argv) < 2:
|
|
print(HELP, file=sys.stderr); sys.exit(1)
|
|
|
|
arg = sys.argv[1]
|
|
dry = '--dry-run' in sys.argv
|
|
tech_filter = None
|
|
severity = 'medium,high,critical'
|
|
wpscan = '--wpscan' in sys.argv
|
|
wpscan_limit = 5
|
|
extra = '--extra' in sys.argv
|
|
extra_limit = 5
|
|
if '--tech' in sys.argv:
|
|
i = sys.argv.index('--tech')
|
|
if i+1 < len(sys.argv): tech_filter = set(sys.argv[i+1].lower().split(','))
|
|
if '--severity' in sys.argv:
|
|
i = sys.argv.index('--severity')
|
|
if i+1 < len(sys.argv): severity = sys.argv[i+1]
|
|
if '--wpscan-limit' in sys.argv:
|
|
i = sys.argv.index('--wpscan-limit')
|
|
if i+1 < len(sys.argv): wpscan_limit = int(sys.argv[i+1])
|
|
if '--extra-limit' in sys.argv:
|
|
i = sys.argv.index('--extra-limit')
|
|
if i+1 < len(sys.argv): extra_limit = int(sys.argv[i+1])
|
|
|
|
def run(cmd):
|
|
try:
|
|
return subprocess.check_output(cmd, stderr=subprocess.STDOUT).decode()
|
|
except Exception:
|
|
return ''
|
|
|
|
if not shutil := __import__('shutil') or True:
|
|
pass
|
|
if not shutil.which('httpx'):
|
|
print('[!] httpx not found', file=sys.stderr); sys.exit(2)
|
|
if not shutil.which('nuclei') and not dry:
|
|
print('[!] nuclei not found', file=sys.stderr); sys.exit(2)
|
|
|
|
json_lines = ''
|
|
if os.path.isfile(arg):
|
|
json_lines = run(['httpx','-silent','-l',arg,'-ports','80,81,88,443,3000,5000,7001,7002,8000,8008,8080,8081,8088,8443,8888,9000','-tech-detect','-json'])
|
|
else:
|
|
json_lines = run(['bash','-lc',f'printf %s\\n {arg} | httpx -silent -ports 80,81,88,443,3000,5000,7001,7002,8000,8008,8080,8081,8088,8443,8888,9000 -tech-detect -json'])
|
|
|
|
by_tech = {}
|
|
for line in json_lines.splitlines():
|
|
try:
|
|
o = json.loads(line)
|
|
url = o.get('url')
|
|
techs = [t.lower() for t in o.get('technologies', [])]
|
|
for t in techs:
|
|
if tech_filter and t not in tech_filter: continue
|
|
by_tech.setdefault(t, set()).add(url)
|
|
except Exception:
|
|
continue
|
|
|
|
presets = {
|
|
'wordpress': {'tags': 'wordpress', 'wpscan': True},
|
|
'drupal': {'tags': 'drupal'},
|
|
'joomla': {'tags': 'joomla'},
|
|
'laravel': {'tags': 'laravel'},
|
|
'aspnet': {'tags': 'aspnet'},
|
|
'spring': {'tags': 'spring'},
|
|
'tomcat': {'tags': 'tomcat'},
|
|
'iis': {'tags': 'iis'},
|
|
'exchange': {'tags': 'exchange'},
|
|
'sharepoint': {'tags': 'sharepoint'},
|
|
'grafana': {'tags': 'grafana'},
|
|
'kibana': {'tags': 'kibana'},
|
|
'gitlab': {'tags': 'gitlab'},
|
|
'confluence': {'tags': 'confluence'},
|
|
'jupyter': {'tags': 'jupyter'},
|
|
'jenkins': {'tags': 'jenkins'},
|
|
'magento': {'tags': 'magento'},
|
|
'sonarqube': {'tags': 'sonarqube'},
|
|
}
|
|
|
|
for t, urls in sorted(by_tech.items(), key=lambda x: (-len(x[1]), x[0])):
|
|
if not urls: continue
|
|
print(f"[+] Tech: {t} ({len(urls)} urls)")
|
|
tf = tempfile.NamedTemporaryFile(delete=False, mode='w')
|
|
for u in sorted(urls): tf.write(u+'\n')
|
|
tf.close()
|
|
tag = presets.get(t, {'tags': t}).get('tags', t)
|
|
if dry:
|
|
print(f"nuclei -l {tf.name} -tags {tag} -severity {severity} -o scans/nuclei_{t}.txt -silent")
|
|
else:
|
|
outdir = os.environ.get('OUTDIR','scans')
|
|
os.makedirs(outdir, exist_ok=True)
|
|
out = os.path.join(outdir, f'nuclei_{t}.txt')
|
|
subprocess.call(['nuclei','-l',tf.name,'-tags',tag,'-severity',severity,'-o',out,'-silent'])
|
|
print(f" nuclei -> {out}")
|
|
# Optional WordPress scan
|
|
if t == 'wordpress' and wpscan and shutil.which('wpscan') and not dry:
|
|
limit = 0
|
|
with open(tf.name,'r') as f:
|
|
for line in f:
|
|
u = line.strip()
|
|
if not u: continue
|
|
subprocess.call(['bin/web/wpscan_quick.sh', u])
|
|
limit += 1
|
|
if limit >= wpscan_limit: break
|
|
# Optional extra tech-specific quick wrappers
|
|
if extra and not dry:
|
|
limit = 0
|
|
with open(tf.name,'r') as f:
|
|
for line in f:
|
|
u=line.strip();
|
|
if not u: continue
|
|
if t == 'drupal' and shutil.which('droopescan'):
|
|
subprocess.call(['bin/web/droopescan_quick.sh', u])
|
|
elif t == 'joomla' and shutil.which('joomscan'):
|
|
subprocess.call(['bin/web/joomscan_quick.sh', u])
|
|
elif t == 'jenkins':
|
|
subprocess.call(['bin/web/jenkins_quick.sh', u])
|
|
elif t == 'sonarqube':
|
|
subprocess.call(['bin/web/sonarqube_quick.sh', u])
|
|
elif t == 'magento':
|
|
subprocess.call(['bin/web/magento_quick.sh', u])
|
|
elif t == 'jira':
|
|
subprocess.call(['bin/web/jira_quick.sh', u])
|
|
elif t == 'confluence':
|
|
subprocess.call(['bin/web/confluence_quick.sh', u])
|
|
limit += 1
|
|
if limit >= extra_limit: break
|