astrotools-cli 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
astrotools/cli.py ADDED
@@ -0,0 +1,1345 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ ===============================================================================
4
+ SUPER TOOLKIT - ULTIMATE EDITION
5
+ Comprehensive Network Reconnaissance, Security & Cryptography
6
+ ===============================================================================
7
+ """
8
+ import hashlib
9
+ import ipaddress
10
+ import json
11
+ import os
12
+ import random
13
+ import re
14
+ import socket
15
+ import string
16
+ import struct
17
+ import sys
18
+ import time
19
+ import ssl
20
+ import subprocess
21
+ from urllib.parse import urlparse
22
+ from concurrent.futures import ThreadPoolExecutor, as_completed
23
+ import requests
24
+ import questionary
25
+ from rich.console import Console
26
+ from rich.panel import Panel
27
+ from rich.table import Table
28
+ from rich.progress import track, Progress, SpinnerColumn, TextColumn
29
+
30
+ # Initialize Rich Console
31
+ console = Console()
32
+
33
+ # Enable ANSI colors on Windows legacy terminals if needed
34
+ if os.name == 'nt':
35
+ os.system('')
36
+
37
+ # Custom Questionary Style
38
+ MENU_STYLE = questionary.Style([
39
+ ('qmark', 'fg:cyan bold'),
40
+ ('question', 'fg:cyan bold'),
41
+ ('answer', 'fg:green bold'),
42
+ ('pointer', 'fg:cyan bold'),
43
+ ('highlighted', 'fg:black bg:cyan bold'),
44
+ ('selected', 'fg:green bold'),
45
+ ('separator', 'fg:gray'),
46
+ ('instruction', 'fg:gray italic'),
47
+ ])
48
+
49
+ def clear_screen():
50
+ """Clears terminal screen cross-platform."""
51
+ os.system('cls' if os.name == 'nt' else 'clear')
52
+
53
+ def show_banner():
54
+ """Displays the stylized AstroTools banner."""
55
+
56
+ clear_screen()
57
+
58
+ banner_art = r"""
59
+ █████╗ ███████╗████████╗██████╗ ██████╗ ████████╗ ██████╗ ██████╗ ██╗ ███████╗
60
+ ██╔══██╗██╔════╝╚══██╔══╝██╔══██╗██╔═══██╗ ╚══██╔══╝██╔═══██╗██╔═══██╗██║ ██╔════╝
61
+ ███████║███████╗ ██║ ██████╔╝██║ ██║ ██║ ██║ ██║██║ ██║██║ ███████╗
62
+ ██╔══██║╚════██║ ██║ ██╔══██╗██║ ██║ ██║ ██║ ██║██║ ██║██║ ╚════██║
63
+ ██║ ██║███████║ ██║ ██║ ██║╚██████╔╝ ██║ ╚██████╔╝╚██████╔╝███████╗ ███████║
64
+ ╚═╝ ╚═╝╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═════╝ ╚═════╝ ╚══════╝ ╚══════╝
65
+
66
+
67
+ """
68
+
69
+ console.print(banner_art, style="bold cyan")
70
+ console.print(Panel(
71
+ "[bold green]⚡ AstroTools Beta (Proffessional Networking tools)[/bold green]\n"
72
+ "[italic gray]By CINEFy. Future updates comming soon...[/italic gray]",
73
+ border_style="cyan"
74
+ ))
75
+ print()
76
+
77
+ def pause():
78
+ """Prompts user to press Enter before returning to menu."""
79
+ console.print("\n[dim]Press [bold]Enter[/bold] to return to main menu...[/dim]")
80
+ try:
81
+ input()
82
+ except (KeyboardInterrupt, EOFError):
83
+ pass
84
+
85
+ def clean_host(target):
86
+ """Normalizes a URL, hostname, or host:port string to just the hostname."""
87
+ target = target.strip()
88
+ if not target:
89
+ return ""
90
+ if "://" in target:
91
+ parsed = urlparse(target)
92
+ return parsed.hostname or ""
93
+ return target.split("/")[0].split(":")[0].strip()
94
+
95
+ def ensure_url(target, default_scheme="https"):
96
+ """Returns a URL with a scheme for HTTP tools."""
97
+ target = target.strip()
98
+ if not target:
99
+ return ""
100
+ if target.startswith(("http://", "https://")):
101
+ return target
102
+ return f"{default_scheme}://{target}"
103
+
104
+ def run_command(cmd, timeout=12):
105
+ """Runs a system command and returns combined stdout/stderr text."""
106
+ try:
107
+ result = subprocess.run(
108
+ cmd,
109
+ capture_output=True,
110
+ text=True,
111
+ encoding="utf-8",
112
+ errors="replace",
113
+ timeout=timeout,
114
+ )
115
+ output = (result.stdout or "") + (result.stderr or "")
116
+ return output.strip(), result.returncode
117
+ except FileNotFoundError:
118
+ return f"Command not found: {cmd[0]}", 127
119
+ except subprocess.TimeoutExpired:
120
+ return f"Command timed out after {timeout}s.", 124
121
+
122
+ # =============================================================================
123
+ # 1. CRYPTOGRAPHY & SECURITY TOOLS
124
+ # =============================================================================
125
+ def password_generator():
126
+ """Generates strong passwords and evaluates security strength."""
127
+ show_banner()
128
+ console.print(Panel("[bold yellow]🔑 Password Generator & Strength Evaluator[/bold yellow]", border_style="yellow"))
129
+ try:
130
+ raw_len = console.input("[bold cyan]Enter password length [default: 16]: [/bold cyan]").strip()
131
+ length = int(raw_len) if raw_len else 16
132
+ if length < 4:
133
+ console.print("[yellow][!] Minimum length is 4. Adjusted to 4.[/yellow]")
134
+ length = 4
135
+ except ValueError:
136
+ console.print("[yellow][!] Invalid number. Using default length of 16.[/yellow]")
137
+ length = 16
138
+
139
+ use_symbols = questionary.confirm("Include special symbols (!@#$%^&*...)?", default=True, style=MENU_STYLE).ask()
140
+ use_digits = questionary.confirm("Include numbers (0-9)?", default=True, style=MENU_STYLE).ask()
141
+ use_upper = questionary.confirm("Include uppercase letters (A-Z)?", default=True, style=MENU_STYLE).ask()
142
+
143
+ char_pool = string.ascii_lowercase
144
+ guaranteed = [random.choice(string.ascii_lowercase)]
145
+ if use_upper:
146
+ char_pool += string.ascii_uppercase
147
+ guaranteed.append(random.choice(string.ascii_uppercase))
148
+ if use_digits:
149
+ char_pool += string.digits
150
+ guaranteed.append(random.choice(string.digits))
151
+ if use_symbols:
152
+ symbols = "!@#$%^&*()_+-=[]{}|;:,.<>?"
153
+ char_pool += symbols
154
+ guaranteed.append(random.choice(symbols))
155
+
156
+ remaining = length - len(guaranteed)
157
+ password_list = guaranteed + [random.choice(char_pool) for _ in range(max(0, remaining))]
158
+ random.shuffle(password_list)
159
+ password = "".join(password_list)
160
+
161
+ # Score calculation
162
+ score = 0
163
+ if len(password) >= 16: score += 3
164
+ elif len(password) >= 12: score += 2
165
+ elif len(password) >= 8: score += 1
166
+ if any(c in string.ascii_lowercase for c in password): score += 1
167
+ if any(c in string.ascii_uppercase for c in password): score += 1
168
+ if any(c in string.digits for c in password): score += 1
169
+ if any(c in "!@#$%^&*()_+-=[]{}|;:,.<>?" for c in password): score += 1
170
+
171
+ if score >= 6: rating = "[bold green]Very Strong / Excellent[/bold green]"
172
+ elif score >= 4: rating = "[bold cyan]Strong / Good[/bold cyan]"
173
+ elif score >= 3: rating = "[bold yellow]Moderate[/bold yellow]"
174
+ else: rating = "[bold red]Weak[/bold red]"
175
+
176
+ table = Table(title="Generated Password Details", border_style="cyan")
177
+ table.add_column("Property", style="bold cyan")
178
+ table.add_column("Value", style="bold white")
179
+ table.add_row("Password", f"[bold green]{password}[/bold green]")
180
+ table.add_row("Length", str(len(password)))
181
+ table.add_row("Strength Rating", rating)
182
+ table.add_row("Entropy Estimate", f"~{len(password) * 6:.1f} bits")
183
+ console.print("\n", table)
184
+ pause()
185
+
186
+ def hash_tool():
187
+ """Generates and verifies cryptographic hashes."""
188
+ show_banner()
189
+ console.print(Panel("[bold yellow]🔒 Hash Generator & Verifier[/bold yellow]", border_style="yellow"))
190
+ action = questionary.select(
191
+ "Select Hash Operation:",
192
+ choices=[
193
+ "1. Generate Hashes for String (MD5, SHA1, SHA256, SHA512, etc.)",
194
+ "2. Verify Hash Match",
195
+ "Back to Main Menu"
196
+ ],
197
+ style=MENU_STYLE
198
+ ).ask()
199
+
200
+ if not action or action == "Back to Main Menu":
201
+ return
202
+
203
+ if "1. Generate" in action:
204
+ text = console.input("\n[bold cyan]Enter text to hash: [/bold cyan]")
205
+ encoded = text.encode('utf-8', errors='replace')
206
+ table = Table(title=f"Hashes for '{text}'", border_style="cyan")
207
+ table.add_column("Algorithm", style="cyan bold", width=12)
208
+ table.add_column("Hash Hex Digest", style="green")
209
+
210
+ algorithms = [
211
+ ("MD5", hashlib.md5(encoded).hexdigest()),
212
+ ("SHA-1", hashlib.sha1(encoded).hexdigest()),
213
+ ("SHA-224", hashlib.sha224(encoded).hexdigest()),
214
+ ("SHA-256", hashlib.sha256(encoded).hexdigest()),
215
+ ("SHA-384", hashlib.sha384(encoded).hexdigest()),
216
+ ("SHA-512", hashlib.sha512(encoded).hexdigest()),
217
+ ]
218
+ for algo, h_val in algorithms:
219
+ table.add_row(algo, h_val)
220
+ console.print("\n", table)
221
+
222
+ elif "2. Verify" in action:
223
+ text = console.input("\n[bold cyan]Enter plain text: [/bold cyan]")
224
+ target_hash = console.input("[bold cyan]Enter hash to verify against: [/bold cyan]").strip().lower()
225
+ encoded = text.encode('utf-8', errors='replace')
226
+ hash_map = {
227
+ "MD5": hashlib.md5(encoded).hexdigest(),
228
+ "SHA-1": hashlib.sha1(encoded).hexdigest(),
229
+ "SHA-224": hashlib.sha224(encoded).hexdigest(),
230
+ "SHA-256": hashlib.sha256(encoded).hexdigest(),
231
+ "SHA-384": hashlib.sha384(encoded).hexdigest(),
232
+ "SHA-512": hashlib.sha512(encoded).hexdigest(),
233
+ }
234
+ match_found = None
235
+ for algo, h_val in hash_map.items():
236
+ if h_val.lower() == target_hash:
237
+ match_found = algo
238
+ break
239
+
240
+ if match_found:
241
+ console.print(Panel(
242
+ f"[bold green]✔ MATCH CONFIRMED![/bold green]\n"
243
+ f"The provided hash matches [bold cyan]{match_found}[/bold cyan] of the text.",
244
+ title="Result", border_style="green"
245
+ ))
246
+ else:
247
+ console.print(Panel(
248
+ f"[bold red]✘ MISMATCH![/bold red]\n"
249
+ f"The provided hash does NOT match any standard algorithm.",
250
+ title="Result", border_style="red"
251
+ ))
252
+ pause()
253
+
254
+ # =============================================================================
255
+ # 2. NETWORK INTELLIGENCE & RECON TOOLS
256
+ # =============================================================================
257
+ def my_network_info():
258
+ """Displays local network parameters and public IP with geo summary."""
259
+ show_banner()
260
+ console.print(Panel("[bold yellow]📡 My Network & Public IP Info[/bold yellow]", border_style="yellow"))
261
+ hostname = socket.gethostname()
262
+ try:
263
+ local_ip = socket.gethostbyname(hostname)
264
+ except Exception:
265
+ local_ip = "127.0.0.1"
266
+
267
+ table = Table(title="Host & Public Network Information", border_style="cyan")
268
+ table.add_column("Property", style="bold cyan")
269
+ table.add_column("Value", style="bold green")
270
+ table.add_row("Hostname", hostname)
271
+ table.add_row("Local IP", local_ip)
272
+
273
+ with Progress(SpinnerColumn(), TextColumn("[progress.description]{task.description}"), transient=True) as progress:
274
+ progress.add_task(description="Fetching public IP & geolocation data...", total=None)
275
+ try:
276
+ res = requests.get("http://ip-api.com/json/", timeout=6).json()
277
+ if res.get("status") == "success":
278
+ table.add_row("Public IP", res.get("query", "Unknown"))
279
+ table.add_row("Country", f"{res.get('country')} ({res.get('countryCode')})")
280
+ table.add_row("Region / City", f"{res.get('regionName')}, {res.get('city')}")
281
+ table.add_row("ISP / Organization", f"{res.get('isp')} / {res.get('org')}")
282
+ table.add_row("Coordinates", f"{res.get('lat')}, {res.get('lon')}")
283
+ table.add_row("Timezone", res.get("timezone", "N/A"))
284
+ else:
285
+ ip_res = requests.get("https://api.ipify.org?format=json", timeout=5).json()
286
+ table.add_row("Public IP", ip_res.get("ip", "Unknown"))
287
+ except Exception as e:
288
+ table.add_row("Public IP", f"[red]Failed to retrieve ({e})[/red]")
289
+ console.print("\n", table)
290
+ pause()
291
+
292
+ def target_ip_lookup():
293
+ """Performs DNS resolution and reverse lookup on a target."""
294
+ show_banner()
295
+ console.print(Panel("[bold yellow]🌐 Target Website / Domain IP Lookup[/bold yellow]", border_style="yellow"))
296
+ target = console.input("[bold cyan]Enter domain or website URL (e.g. google.com): [/bold cyan]").strip()
297
+ if not target:
298
+ console.print("[red]No domain provided.[/red]")
299
+ pause()
300
+ return
301
+
302
+ clean_target = target.replace("https://", "").replace("http://", "").split("/")[0].split(":")[0]
303
+ try:
304
+ resolved_ip = socket.gethostbyname(clean_target)
305
+ try:
306
+ host_alias = socket.gethostbyaddr(resolved_ip)[0]
307
+ except Exception:
308
+ host_alias = "None / Unresolved PTR"
309
+ try:
310
+ addr_info = socket.getaddrinfo(clean_target, 80)
311
+ all_ips = sorted(list(set(item[4][0] for item in addr_info)))
312
+ except Exception:
313
+ all_ips = [resolved_ip]
314
+
315
+ table = Table(title=f"DNS Lookup: {clean_target}", border_style="cyan")
316
+ table.add_column("Property", style="bold cyan")
317
+ table.add_column("Value", style="bold green")
318
+ table.add_row("Target Domain", clean_target)
319
+ table.add_row("Primary IP", resolved_ip)
320
+ table.add_row("Resolved IP List", ", ".join(all_ips))
321
+ table.add_row("Reverse DNS (PTR)", host_alias)
322
+ console.print("\n", table)
323
+ except socket.gaierror:
324
+ console.print(f"\n[red]✘ Error: Could not resolve domain '{clean_target}'.[/red]")
325
+ except Exception as e:
326
+ console.print(f"\n[red]✘ Unexpected error: {e}[/red]")
327
+ pause()
328
+
329
+ def geoip_lookup():
330
+ """Fetches GeoIP intelligence for any IP or domain."""
331
+ show_banner()
332
+ console.print(Panel("[bold yellow]🌍 GeoIP Intelligence Tracker[/bold yellow]", border_style="yellow"))
333
+ target = console.input("[bold cyan]Enter IP or Domain to trace (leave empty for your IP): [/bold cyan]").strip()
334
+ if target:
335
+ target = target.replace("https://", "").replace("http://", "").split("/")[0]
336
+ endpoint = f"http://ip-api.com/json/{target}" if target else "http://ip-api.com/json/"
337
+
338
+ with Progress(SpinnerColumn(), TextColumn("[progress.description]{task.description}"), transient=True) as progress:
339
+ progress.add_task(description="Querying GeoIP satellite & routing database...", total=None)
340
+ try:
341
+ res = requests.get(endpoint, timeout=6).json()
342
+ except Exception as e:
343
+ res = {"status": "fail", "message": str(e)}
344
+
345
+ if res.get("status") == "success":
346
+ table = Table(title=f"GeoIP Location Intel: {res.get('query')}", border_style="cyan")
347
+ table.add_column("Field", style="bold cyan", width=18)
348
+ table.add_column("Details", style="bold white")
349
+ table.add_row("Target IP", f"[bold green]{res.get('query')}[/bold green]")
350
+ table.add_row("Country", f"{res.get('country')} ({res.get('countryCode')})")
351
+ table.add_row("Region / State", f"{res.get('regionName')} ({res.get('region')})")
352
+ table.add_row("City", res.get('city', 'N/A'))
353
+ table.add_row("ZIP / Postal Code", res.get('zip', 'N/A'))
354
+ table.add_row("Coordinates (Lat, Lon)", f"{res.get('lat')}, {res.get('lon')}")
355
+ table.add_row("Timezone", res.get('timezone', 'N/A'))
356
+ table.add_row("ISP", res.get('isp', 'N/A'))
357
+ table.add_row("Organization", res.get('org', 'N/A'))
358
+ table.add_row("Autonomous System (AS)", res.get('as', 'N/A'))
359
+ table.add_row("Google Maps URL", f"https://www.google.com/maps?q={res.get('lat')},{res.get('lon')}")
360
+ console.print("\n", table)
361
+ else:
362
+ console.print(f"\n[red]✘ GeoIP lookup failed: {res.get('message', 'Target not found')}[/red]")
363
+ pause()
364
+
365
+ def mac_lookup():
366
+ """Looks up MAC vendor and OUI organization."""
367
+ show_banner()
368
+ console.print(Panel("[bold yellow]🏷️ MAC Address Vendor & OUI Lookup[/bold yellow]", border_style="yellow"))
369
+ mac_input = console.input("[bold cyan]Enter MAC Address (e.g. 00:1A:2B:3C:4D:5E or 001A2B): [/bold cyan]").strip()
370
+ if not mac_input:
371
+ console.print("[red]No MAC address provided.[/red]")
372
+ pause()
373
+ return
374
+
375
+ clean_mac = mac_input.replace(":", "").replace("-", "").replace(".", "").upper()
376
+ if len(clean_mac) < 6:
377
+ console.print("[red]Invalid MAC format.[/red]")
378
+ pause()
379
+ return
380
+
381
+ with Progress(SpinnerColumn(), TextColumn("[progress.description]{task.description}"), transient=True) as progress:
382
+ progress.add_task(description="Querying IEEE OUI database...", total=None)
383
+ try:
384
+ url = f"https://api.maclookup.app/v2/macs/{clean_mac}"
385
+ res = requests.get(url, headers={'User-Agent': 'Mozilla/5.0'}, timeout=6).json()
386
+ except Exception as e:
387
+ res = {"found": False, "error": str(e)}
388
+
389
+ if res.get("found"):
390
+ table = Table(title=f"MAC Lookup: {mac_input.upper()}", border_style="cyan")
391
+ table.add_column("Property", style="bold cyan")
392
+ table.add_column("Value", style="bold green")
393
+ table.add_row("MAC Prefix (OUI)", res.get("macPrefix", clean_mac[:6]))
394
+ table.add_row("Vendor / Manufacturer", res.get("company", "Unknown Vendor"))
395
+ table.add_row("Country", res.get("country", "N/A"))
396
+ table.add_row("Address", res.get("address", "N/A"))
397
+ console.print("\n", table)
398
+ else:
399
+ console.print(f"\n[yellow][!] MAC address prefix not found or API limit reached.[/yellow]")
400
+ pause()
401
+
402
+ def subnet_scanner():
403
+ """Calculates CIDR subnet information and performs multi-threaded alive ping sweep."""
404
+ show_banner()
405
+ console.print(Panel("[bold yellow]🖧 CIDR Subnet Calculator & Ping Sweep[/bold yellow]", border_style="yellow"))
406
+ cidr_input = console.input("[bold cyan]Enter Subnet in CIDR format [default: 192.168.1.0/24]: [/bold cyan]").strip()
407
+ if not cidr_input:
408
+ cidr_input = "192.168.1.0/24"
409
+ try:
410
+ network = ipaddress.ip_network(cidr_input, strict=False)
411
+ except ValueError as e:
412
+ console.print(f"\n[red]✘ Error: Invalid CIDR notation '{cidr_input}': {e}[/red]")
413
+ pause()
414
+ return
415
+
416
+ table = Table(title=f"Subnet Overview: {cidr_input}", border_style="cyan")
417
+ table.add_column("Parameter", style="bold cyan")
418
+ table.add_column("Value", style="bold green")
419
+ hosts = list(network.hosts())
420
+ table.add_row("Network Address", str(network.network_address))
421
+ table.add_row("Broadcast Address", str(network.broadcast_address))
422
+ table.add_row("Subnet Netmask", str(network.netmask))
423
+ table.add_row("Wildcard Mask", str(network.hostmask))
424
+ table.add_row("Total IP Count", str(network.num_addresses))
425
+ table.add_row("Usable Host IPs", str(len(hosts)))
426
+ if hosts:
427
+ table.add_row("Usable Host Range", f"{hosts[0]} - {hosts[-1]}")
428
+ console.print("\n", table)
429
+
430
+ run_sweep = questionary.confirm("Run high-speed multi-threaded host discovery on this subnet?", default=True, style=MENU_STYLE).ask()
431
+ if run_sweep:
432
+ if len(hosts) > 512:
433
+ console.print("[yellow][!] Large subnet detected. Scanning first 256 hosts...[/yellow]")
434
+ target_hosts = hosts[:256]
435
+ else:
436
+ target_hosts = hosts
437
+
438
+ console.print(f"\n[bold cyan][*] Sweeping {len(target_hosts)} host(s)...[/bold cyan]\n")
439
+ alive_hosts = []
440
+ def ping_host(ip):
441
+ ip_str = str(ip)
442
+ probe_ports = [80, 443, 22, 445, 135, 8080, 21, 53]
443
+ for port in probe_ports:
444
+ try:
445
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
446
+ s.settimeout(0.35)
447
+ if s.connect_ex((ip_str, port)) == 0:
448
+ return ip_str, True
449
+ except Exception:
450
+ pass
451
+ return ip_str, False
452
+
453
+ start_time = time.time()
454
+ with ThreadPoolExecutor(max_workers=60) as executor:
455
+ futures = {executor.submit(ping_host, ip): ip for ip in target_hosts}
456
+ for future in as_completed(futures):
457
+ ip_str, is_alive = future.result()
458
+ if is_alive:
459
+ console.print(f"[bold green]✔ HOST ALIVE:[/bold green] [bold white]{ip_str}[/bold white]")
460
+ alive_hosts.append(ip_str)
461
+ elapsed = time.time() - start_time
462
+ console.print(f"\n[bold cyan][*] Sweep finished in {elapsed:.2f}s. Discovered {len(alive_hosts)} responsive host(s).[/bold cyan]")
463
+ pause()
464
+
465
+ # =============================================================================
466
+ # 3. SCANNING & FUZZING TOOLS
467
+ # =============================================================================
468
+ def port_scanner():
469
+ """Multi-threaded high speed TCP port scanner."""
470
+ show_banner()
471
+ console.print(Panel("[bold yellow]⚡ High-Speed Multi-Threaded Port Scanner[/bold yellow]", border_style="yellow"))
472
+ target = console.input("[bold cyan]Enter target IP or Hostname [default: 127.0.0.1]: [/bold cyan]").strip()
473
+ if not target: target = "127.0.0.1"
474
+ clean_target = target.replace("https://", "").replace("http://", "").split("/")[0].split(":")[0]
475
+ try:
476
+ target_ip = socket.gethostbyname(clean_target)
477
+ except socket.gaierror:
478
+ console.print(f"[red]✘ Error: Could not resolve hostname '{clean_target}'.[/red]")
479
+ pause()
480
+ return
481
+
482
+ port_preset = questionary.select(
483
+ "Select Port Range Option:",
484
+ choices=[
485
+ "1. Common Top Ports",
486
+ "2. Fast Scan Top 100 Ports",
487
+ "3. Standard Scan (1 - 1024)",
488
+ "4. Custom Ports or Range"
489
+ ], style=MENU_STYLE
490
+ ).ask()
491
+
492
+ if "1. Common" in port_preset:
493
+ ports = [21, 22, 23, 25, 53, 80, 110, 135, 139, 443, 445, 1433, 3306, 3389, 5432, 8000, 8080, 8443]
494
+ elif "2. Fast" in port_preset:
495
+ ports = list(range(1, 101))
496
+ elif "3. Standard" in port_preset:
497
+ ports = list(range(1, 1025))
498
+ else:
499
+ custom_input = console.input("[bold cyan]Enter ports (e.g. 21,22,80 or 1-500): [/bold cyan]").strip()
500
+ if "-" in custom_input:
501
+ try:
502
+ start_p, end_p = map(int, custom_input.split("-"))
503
+ ports = list(range(max(1, start_p), min(65535, end_p) + 1))
504
+ except ValueError:
505
+ ports = [21, 22, 80, 443, 3306, 8080]
506
+ else:
507
+ try:
508
+ ports = [int(p.strip()) for p in custom_input.split(",") if p.strip()]
509
+ except ValueError:
510
+ ports = [21, 22, 80, 443, 3306, 8080]
511
+
512
+ console.print(f"\n[bold cyan][*] Launching scan on {clean_target} ({target_ip}) across {len(ports)} port(s)...[/bold cyan]\n")
513
+
514
+ def scan_port(ip, port):
515
+ try:
516
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
517
+ s.settimeout(0.8)
518
+ if s.connect_ex((ip, port)) == 0:
519
+ try: service = socket.getservbyport(port, "tcp")
520
+ except Exception: service = "unknown"
521
+ return port, True, service
522
+ except Exception: pass
523
+ return port, False, None
524
+
525
+ open_ports = []
526
+ start_time = time.time()
527
+ max_threads = min(120, len(ports)) if ports else 1
528
+ with ThreadPoolExecutor(max_workers=max_threads) as executor:
529
+ futures = {executor.submit(scan_port, target_ip, p): p for p in ports}
530
+ for future in as_completed(futures):
531
+ port, is_open, service = future.result()
532
+ if is_open:
533
+ console.print(f"[bold green]✔ OPEN[/bold green] - Port [cyan]{port:<5}/tcp[/cyan] ({service})")
534
+ open_ports.append((port, service))
535
+ open_ports.sort(key=lambda x: x[0])
536
+ elapsed = time.time() - start_time
537
+
538
+ table = Table(title=f"Scan Summary for {clean_target}", border_style="cyan")
539
+ table.add_column("Port", style="bold cyan", width=10)
540
+ table.add_column("Status", style="bold green", width=12)
541
+ table.add_column("Service Name", style="bold white")
542
+ for port, service in open_ports:
543
+ table.add_row(f"{port}/tcp", "OPEN", service)
544
+ console.print("\n", table)
545
+ console.print(f"[bold cyan][*] Scan finished in {elapsed:.2f}s.[/bold cyan]")
546
+ pause()
547
+
548
+ def subdomain_finder():
549
+ """Multi-threaded Subdomain & DNS Enumeration."""
550
+ show_banner()
551
+ console.print(Panel("[bold yellow]🕵️ Subdomain Finder & DNS Recon[/bold yellow]", border_style="yellow"))
552
+ domain = console.input("[bold cyan]Enter target domain (e.g. google.com): [/bold cyan]").strip()
553
+ if not domain:
554
+ console.print("[red]No domain specified.[/red]")
555
+ pause()
556
+ return
557
+ domain = domain.replace("https://", "").replace("http://", "").split("/")[0].split(":")[0]
558
+
559
+ default_subdomains = ["www", "mail", "remote", "blog", "webmail", "server", "ns1", "ns2", "smtp", "secure", "vpn", "api", "dev", "staging", "test", "portal", "admin", "panel", "app", "cdn", "cloud", "shop", "m", "support", "beta", "git", "db", "dashboard", "status", "auth", "login", "v1", "backend", "internal", "corp", "assets", "monitor", "ftp", "ssh", "direct", "gateway", "proxy", "jenkins", "gitlab", "jira", "s3"]
560
+ custom_input = console.input("[bold cyan]Enter custom subdomains separated by comma (leave blank for default): [/bold cyan]").strip()
561
+ subdomains = [s.strip() for s in custom_input.split(",") if s.strip()] if custom_input else default_subdomains
562
+
563
+ console.print(f"\n[bold cyan][*] Enumerating {len(subdomains)} subdomains for {domain}...[/bold cyan]\n")
564
+ def check_sub(sub):
565
+ target_host = f"{sub}.{domain}"
566
+ try:
567
+ ip = socket.gethostbyname(target_host)
568
+ return target_host, ip
569
+ except socket.gaierror:
570
+ return target_host, None
571
+
572
+ found_subs = []
573
+ start_time = time.time()
574
+ with ThreadPoolExecutor(max_workers=50) as executor:
575
+ futures = {executor.submit(check_sub, sub): sub for sub in subdomains}
576
+ for future in as_completed(futures):
577
+ target_host, ip = future.result()
578
+ if ip:
579
+ console.print(f"[bold green]✔ FOUND:[/bold green] [bold cyan]{target_host:<32}[/bold cyan] -> [white]{ip}[/white]")
580
+ found_subs.append((target_host, ip))
581
+ elapsed = time.time() - start_time
582
+ console.print(f"\n[bold cyan][*] DNS Recon complete in {elapsed:.2f}s.[/bold cyan]")
583
+ pause()
584
+
585
+ def dir_fuzzer():
586
+ """Multi-threaded Web Directory & Endpoint Fuzzer."""
587
+ show_banner()
588
+ console.print(Panel("[bold yellow]⚡ Web Directory & Path Fuzzer[/bold yellow]", border_style="yellow"))
589
+ target = console.input("[bold cyan]Enter target URL/Host: [/bold cyan]").strip()
590
+ if not target:
591
+ console.print("[red]No target specified.[/red]")
592
+ pause()
593
+ return
594
+ base_url = target if target.startswith(("http://", "https://")) else "http://" + target
595
+ base_url = base_url.rstrip("/")
596
+
597
+ default_paths = ["/admin", "/login", "/dashboard", "/.env", "/config.php", "/robots.txt", "/api", "/api/v1", "/backup", "/uploads", "/.git", "/swagger.json", "/health", "/console", "/actuator", "/graphql"]
598
+ custom_paths = console.input("[bold cyan]Enter paths separated by comma (leave blank for built-in): [/bold cyan]").strip()
599
+ wordlist = [p.strip() if p.strip().startswith("/") else "/" + p.strip() for p in custom_paths.split(",") if p.strip()] if custom_paths else default_paths
600
+
601
+ console.print(f"\n[bold cyan][*] Fuzzing {len(wordlist)} paths against {base_url}...[/bold cyan]\n")
602
+ def scan_path(path):
603
+ url = base_url + path
604
+ try:
605
+ r = requests.get(url, headers={'User-Agent': 'Mozilla/5.0'}, timeout=4, allow_redirects=False)
606
+ return path, r.status_code, len(r.content)
607
+ except Exception:
608
+ return path, None, 0
609
+
610
+ found_paths = []
611
+ start_time = time.time()
612
+ with ThreadPoolExecutor(max_workers=30) as executor:
613
+ futures = {executor.submit(scan_path, path): path for path in wordlist}
614
+ for future in as_completed(futures):
615
+ path, status, length = future.result()
616
+ if status and status != 404:
617
+ status_style = "bold green" if status == 200 else "bold cyan" if status in (301, 302) else "bold yellow" if status in (401, 403) else "dim white"
618
+ console.print(f"[{status_style}]✔ {status:<15}[/{status_style}] | Path: [cyan]{base_url + path:<45}[/cyan]")
619
+ found_paths.append((path, status, length))
620
+ elapsed = time.time() - start_time
621
+ console.print(f"\n[bold cyan][*] Fuzzing finished in {elapsed:.2f}s.[/bold cyan]")
622
+ pause()
623
+
624
+ def header_inspector():
625
+ """Inspects HTTP response headers and conducts automated security headers audit."""
626
+ show_banner()
627
+ console.print(Panel("[bold yellow]🔍 HTTP Header Inspector & Security Auditor[/bold yellow]", border_style="yellow"))
628
+ url = console.input("[bold cyan]Enter URL: [/bold cyan]").strip()
629
+ if not url:
630
+ console.print("[red]No URL provided.[/red]")
631
+ pause()
632
+ return
633
+ if not url.startswith(("http://", "https://")): url = "https://" + url
634
+
635
+ try:
636
+ response = requests.get(url, headers={'User-Agent': 'Mozilla/5.0'}, timeout=6, allow_redirects=True)
637
+ headers_table = Table(title="HTTP Response Headers", border_style="cyan")
638
+ headers_table.add_column("Header", style="bold cyan")
639
+ headers_table.add_column("Value", style="white")
640
+ for key, val in response.headers.items():
641
+ headers_table.add_row(key, val)
642
+ console.print(headers_table)
643
+
644
+ security_headers = {
645
+ "Strict-Transport-Security": "Protects against MITM",
646
+ "Content-Security-Policy": "Mitigates XSS",
647
+ "X-Frame-Options": "Prevents Clickjacking",
648
+ "X-Content-Type-Options": "Stops MIME-sniffing"
649
+ }
650
+ audit_table = Table(title="Security Headers Audit", border_style="yellow")
651
+ audit_table.add_column("Security Header", style="bold cyan", width=28)
652
+ audit_table.add_column("Status", width=12)
653
+ for sh, desc in security_headers.items():
654
+ status = "[bold green]✔ PRESENT[/bold green]" if sh.lower() in [h.lower() for h in response.headers] else "[bold red]✘ MISSING[/bold red]"
655
+ audit_table.add_row(sh, status)
656
+ console.print("\n", audit_table)
657
+ except Exception as e:
658
+ console.print(f"[bold red]✘ Connection error: {e}[/bold red]")
659
+ pause()
660
+
661
+ # =============================================================================
662
+ # 4. EXTRA NETWORK DIAGNOSTIC TOOLS
663
+ # =============================================================================
664
+ def dns_record_lookup():
665
+ """Looks up common DNS records using the system resolver tools."""
666
+ show_banner()
667
+ console.print(Panel("[bold yellow]DNS Record Lookup[/bold yellow]", border_style="yellow"))
668
+ domain = clean_host(console.input("[bold cyan]Enter domain (e.g. example.com): [/bold cyan]"))
669
+ if not domain:
670
+ console.print("[red]No domain provided.[/red]")
671
+ pause()
672
+ return
673
+
674
+ record_choices = ["A", "AAAA", "MX", "NS", "TXT", "SOA", "CNAME"]
675
+ selected = questionary.checkbox(
676
+ "Select DNS record types:",
677
+ choices=record_choices,
678
+ default=["A", "AAAA", "MX", "NS", "TXT"],
679
+ style=MENU_STYLE
680
+ ).ask()
681
+ if not selected:
682
+ selected = ["A"]
683
+
684
+ table = Table(title=f"DNS Records: {domain}", border_style="cyan")
685
+ table.add_column("Type", style="bold cyan", width=8)
686
+ table.add_column("Result", style="white")
687
+
688
+ for record_type in selected:
689
+ if os.name == "nt":
690
+ output, _ = run_command(["nslookup", f"-type={record_type}", domain])
691
+ else:
692
+ output, _ = run_command(["dig", "+short", domain, record_type])
693
+ if not output:
694
+ output, _ = run_command(["nslookup", f"-type={record_type}", domain])
695
+ table.add_row(record_type, output or "No records found")
696
+
697
+ console.print("\n", table)
698
+ pause()
699
+
700
+ def http_status_checker():
701
+ """Checks HTTP status, redirects, server headers, and response timing."""
702
+ show_banner()
703
+ console.print(Panel("[bold yellow]HTTP Status & Redirect Checker[/bold yellow]", border_style="yellow"))
704
+ raw_url = console.input("[bold cyan]Enter URL or host: [/bold cyan]").strip()
705
+ url = ensure_url(raw_url)
706
+ if not url:
707
+ console.print("[red]No URL provided.[/red]")
708
+ pause()
709
+ return
710
+
711
+ try:
712
+ start_time = time.perf_counter()
713
+ response = requests.get(url, headers={"User-Agent": "Mozilla/5.0"}, timeout=10, allow_redirects=True)
714
+ elapsed_ms = (time.perf_counter() - start_time) * 1000
715
+
716
+ table = Table(title=f"HTTP Check: {url}", border_style="cyan")
717
+ table.add_column("Property", style="bold cyan")
718
+ table.add_column("Value", style="bold white")
719
+ table.add_row("Final URL", response.url)
720
+ table.add_row("Status Code", str(response.status_code))
721
+ table.add_row("Reason", response.reason)
722
+ table.add_row("Response Time", f"{elapsed_ms:.1f} ms")
723
+ table.add_row("Content Length", response.headers.get("Content-Length", str(len(response.content))))
724
+ table.add_row("Content Type", response.headers.get("Content-Type", "N/A"))
725
+ table.add_row("Server", response.headers.get("Server", "N/A"))
726
+ console.print("\n", table)
727
+
728
+ if response.history:
729
+ redirects = Table(title="Redirect Chain", border_style="yellow")
730
+ redirects.add_column("#", style="bold cyan", width=4)
731
+ redirects.add_column("Status", style="bold yellow", width=8)
732
+ redirects.add_column("URL", style="white")
733
+ for idx, item in enumerate(response.history, start=1):
734
+ redirects.add_row(str(idx), str(item.status_code), item.url)
735
+ redirects.add_row(str(len(response.history) + 1), str(response.status_code), response.url)
736
+ console.print("\n", redirects)
737
+ except requests.RequestException as e:
738
+ console.print(f"[bold red]Connection error: {e}[/bold red]")
739
+ pause()
740
+
741
+ def tech_stack_detector():
742
+ """Detects likely web technologies, CDN/WAF hints, cookies, and metadata."""
743
+ show_banner()
744
+ console.print(Panel("[bold yellow]HTTP Tech Stack Detector[/bold yellow]", border_style="yellow"))
745
+ raw_url = console.input("[bold cyan]Enter URL or host: [/bold cyan]").strip()
746
+ url = ensure_url(raw_url)
747
+ if not url:
748
+ console.print("[red]No URL provided.[/red]")
749
+ pause()
750
+ return
751
+
752
+ try:
753
+ response = requests.get(url, headers={"User-Agent": "Mozilla/5.0"}, timeout=12, allow_redirects=True)
754
+ except requests.RequestException as e:
755
+ console.print(f"[bold red]Connection error: {e}[/bold red]")
756
+ pause()
757
+ return
758
+
759
+ headers = response.headers
760
+ header_text = "\n".join(f"{k}: {v}" for k, v in headers.items()).lower()
761
+ body = response.text[:250000]
762
+ body_lower = body.lower()
763
+ detections = []
764
+
765
+ header_hints = {
766
+ "Server": headers.get("Server"),
767
+ "X-Powered-By": headers.get("X-Powered-By"),
768
+ "Via": headers.get("Via"),
769
+ "X-Generator": headers.get("X-Generator"),
770
+ "X-AspNet-Version": headers.get("X-AspNet-Version"),
771
+ }
772
+ for name, value in header_hints.items():
773
+ if value:
774
+ detections.append((name, value, "Header"))
775
+
776
+ cdn_patterns = [
777
+ ("Cloudflare", ["cf-ray", "cf-cache-status", "__cf_bm", "cloudflare"]),
778
+ ("Akamai", ["akamai", "akamaighost", "x-akamai"]),
779
+ ("Fastly", ["fastly", "x-served-by", "x-cache-hits"]),
780
+ ("Amazon CloudFront", ["cloudfront", "x-amz-cf-id", "x-amz-cf-pop"]),
781
+ ("Azure Front Door", ["azurefd", "x-azure-ref"]),
782
+ ("Vercel", ["vercel", "x-vercel-id"]),
783
+ ("Netlify", ["netlify", "x-nf-request-id"]),
784
+ ]
785
+ for tech, needles in cdn_patterns:
786
+ if any(needle in header_text for needle in needles):
787
+ detections.append((tech, "CDN/WAF/proxy hint", "Header"))
788
+
789
+ body_patterns = [
790
+ ("WordPress", ["wp-content", "wp-includes", "wp-json"]),
791
+ ("Drupal", ["drupal-settings-json", "/sites/default/"]),
792
+ ("Joomla", ["content=\"joomla", "/media/system/js/"]),
793
+ ("React", ["reactroot", "__react", "react-dom"]),
794
+ ("Next.js", ["__next_data__", "/_next/static/"]),
795
+ ("Vue.js", ["__vue__", "vue.js", "data-v-"]),
796
+ ("Angular", ["ng-version", "ng-app", "angular.js"]),
797
+ ("Svelte", ["svelte-"]),
798
+ ("jQuery", ["jquery"]),
799
+ ("Bootstrap", ["bootstrap.min.css", "bootstrap.min.js"]),
800
+ ("Tailwind CSS", ["tailwind"]),
801
+ ("Shopify", ["cdn.shopify.com", "shopify-features"]),
802
+ ("Laravel", ["laravel_session"]),
803
+ ("Django", ["csrftoken", "django"]),
804
+ ]
805
+ for tech, needles in body_patterns:
806
+ if any(needle in body_lower for needle in needles):
807
+ detections.append((tech, "Matched page/source marker", "Body/Cookie"))
808
+
809
+ generator_match = re.search(
810
+ r'<meta[^>]+name=["\']generator["\'][^>]+content=["\']([^"\']+)["\']',
811
+ body,
812
+ re.IGNORECASE,
813
+ )
814
+ if generator_match:
815
+ detections.append(("Meta generator", generator_match.group(1), "HTML"))
816
+
817
+ script_hosts = sorted(set(re.findall(r'<script[^>]+src=["\']https?://([^/"\']+)', body, re.IGNORECASE)))
818
+ css_hosts = sorted(set(re.findall(r'<link[^>]+href=["\']https?://([^/"\']+)', body, re.IGNORECASE)))
819
+
820
+ summary = Table(title=f"Tech Stack Hints: {response.url}", border_style="cyan")
821
+ summary.add_column("Technology / Signal", style="bold cyan")
822
+ summary.add_column("Evidence", style="white")
823
+ summary.add_column("Source", style="bold yellow", width=14)
824
+ seen = set()
825
+ for name, evidence, source in detections:
826
+ key = (name, evidence, source)
827
+ if key not in seen:
828
+ summary.add_row(name, str(evidence), source)
829
+ seen.add(key)
830
+ if not seen:
831
+ summary.add_row("No strong fingerprint", "Review raw headers/source manually", "N/A")
832
+ console.print("\n", summary)
833
+
834
+ assets = Table(title="External Asset Hosts", border_style="yellow")
835
+ assets.add_column("Type", style="bold cyan", width=10)
836
+ assets.add_column("Hosts", style="white")
837
+ assets.add_row("Scripts", "\n".join(script_hosts[:20]) if script_hosts else "None found")
838
+ assets.add_row("Styles", "\n".join(css_hosts[:20]) if css_hosts else "None found")
839
+ console.print("\n", assets)
840
+
841
+ cookie_table = Table(title="Cookie Security Flags", border_style="magenta")
842
+ cookie_table.add_column("Cookie", style="bold cyan")
843
+ cookie_table.add_column("Secure", style="bold green")
844
+ cookie_table.add_column("HttpOnly", style="bold green")
845
+ cookie_table.add_column("SameSite", style="bold green")
846
+ raw_cookies = headers.get("Set-Cookie", "")
847
+ if raw_cookies:
848
+ for cookie_part in re.split(r", (?=[^;,]+=)", raw_cookies)[:20]:
849
+ name = cookie_part.split("=", 1)[0]
850
+ lower_cookie = cookie_part.lower()
851
+ same_site = re.search(r"samesite=([^;]+)", cookie_part, re.IGNORECASE)
852
+ cookie_table.add_row(
853
+ name,
854
+ "yes" if "secure" in lower_cookie else "no",
855
+ "yes" if "httponly" in lower_cookie else "no",
856
+ same_site.group(1) if same_site else "missing",
857
+ )
858
+ else:
859
+ cookie_table.add_row("None", "N/A", "N/A", "N/A")
860
+ console.print("\n", cookie_table)
861
+ pause()
862
+
863
+ def web_endpoint_auditor():
864
+ """Audits well-known web endpoints, methods, CORS, and cookie flags."""
865
+ show_banner()
866
+ console.print(Panel("[bold yellow]Web Endpoint Auditor[/bold yellow]", border_style="yellow"))
867
+ raw_url = console.input("[bold cyan]Enter URL or host: [/bold cyan]").strip()
868
+ base_url = ensure_url(raw_url).rstrip("/")
869
+ if not base_url:
870
+ console.print("[red]No URL provided.[/red]")
871
+ pause()
872
+ return
873
+
874
+ session = requests.Session()
875
+ session.headers.update({"User-Agent": "Mozilla/5.0"})
876
+ paths = [
877
+ ("robots.txt", "/robots.txt", "Public crawl rules"),
878
+ ("sitemap.xml", "/sitemap.xml", "Public URL index"),
879
+ ("security.txt", "/.well-known/security.txt", "Security contact policy"),
880
+ ("change-password", "/.well-known/change-password", "Password-change discovery"),
881
+ ("humans.txt", "/humans.txt", "Site/team metadata"),
882
+ ("favicon.ico", "/favicon.ico", "Site fingerprint asset"),
883
+ (".env", "/.env", "Sensitive config exposure"),
884
+ (".git HEAD", "/.git/HEAD", "Sensitive repository exposure"),
885
+ ("server-status", "/server-status", "Sensitive Apache status exposure"),
886
+ ]
887
+
888
+ results = Table(title=f"Endpoint Audit: {base_url}", border_style="cyan")
889
+ results.add_column("Endpoint", style="bold cyan")
890
+ results.add_column("Status", style="bold yellow", width=10)
891
+ results.add_column("Bytes", style="white", width=10)
892
+ results.add_column("Notes", style="white")
893
+
894
+ for label, path, note in paths:
895
+ try:
896
+ response = session.get(base_url + path, timeout=7, allow_redirects=False)
897
+ status = response.status_code
898
+ length = len(response.content)
899
+ warning = note
900
+ if path in ("/.env", "/.git/HEAD", "/server-status") and status == 200:
901
+ warning = f"[bold red]POSSIBLE EXPOSURE[/bold red] - {note}"
902
+ elif status in (401, 403):
903
+ warning = f"Protected - {note}"
904
+ elif status in (301, 302, 307, 308):
905
+ warning = f"Redirects to {response.headers.get('Location', 'unknown')}"
906
+ results.add_row(label, str(status), str(length), warning)
907
+ except requests.RequestException as e:
908
+ results.add_row(label, "ERR", "0", str(e))
909
+ console.print("\n", results)
910
+
911
+ try:
912
+ options_response = session.options(
913
+ base_url,
914
+ timeout=7,
915
+ headers={"Origin": "https://example.com", "Access-Control-Request-Method": "GET"},
916
+ allow_redirects=False,
917
+ )
918
+ methods = options_response.headers.get("Allow", options_response.headers.get("Access-Control-Allow-Methods", "Not advertised"))
919
+ cors_origin = options_response.headers.get("Access-Control-Allow-Origin", "Not advertised")
920
+ cors_creds = options_response.headers.get("Access-Control-Allow-Credentials", "Not advertised")
921
+
922
+ policy = Table(title="HTTP Methods & CORS", border_style="yellow")
923
+ policy.add_column("Check", style="bold cyan")
924
+ policy.add_column("Result", style="white")
925
+ policy.add_row("OPTIONS Status", str(options_response.status_code))
926
+ policy.add_row("Allowed Methods", methods)
927
+ policy.add_row("CORS Origin", cors_origin)
928
+ policy.add_row("CORS Credentials", cors_creds)
929
+ if cors_origin == "*" and cors_creds.lower() == "true":
930
+ policy.add_row("CORS Risk", "[bold red]Wildcard origin with credentials advertised[/bold red]")
931
+ console.print("\n", policy)
932
+ except requests.RequestException as e:
933
+ console.print(f"[yellow][!] OPTIONS/CORS check failed: {e}[/yellow]")
934
+
935
+ try:
936
+ home = session.get(base_url, timeout=7, allow_redirects=True)
937
+ cookie_table = Table(title="Homepage Cookie Flags", border_style="magenta")
938
+ cookie_table.add_column("Cookie", style="bold cyan")
939
+ cookie_table.add_column("Secure", style="bold green")
940
+ cookie_table.add_column("HttpOnly", style="bold green")
941
+ cookie_table.add_column("SameSite", style="bold green")
942
+ raw_cookies = home.headers.get("Set-Cookie", "")
943
+ if raw_cookies:
944
+ for cookie_part in re.split(r", (?=[^;,]+=)", raw_cookies)[:20]:
945
+ name = cookie_part.split("=", 1)[0]
946
+ lower_cookie = cookie_part.lower()
947
+ same_site = re.search(r"samesite=([^;]+)", cookie_part, re.IGNORECASE)
948
+ cookie_table.add_row(
949
+ name,
950
+ "yes" if "secure" in lower_cookie else "no",
951
+ "yes" if "httponly" in lower_cookie else "no",
952
+ same_site.group(1) if same_site else "missing",
953
+ )
954
+ else:
955
+ cookie_table.add_row("None", "N/A", "N/A", "N/A")
956
+ console.print("\n", cookie_table)
957
+ except requests.RequestException as e:
958
+ console.print(f"[yellow][!] Cookie check failed: {e}[/yellow]")
959
+ pause()
960
+
961
+ def tcp_ping_tool():
962
+ """Measures TCP connection latency to a host and port."""
963
+ show_banner()
964
+ console.print(Panel("[bold yellow]TCP Ping / Latency Tester[/bold yellow]", border_style="yellow"))
965
+ host = clean_host(console.input("[bold cyan]Enter host or IP [default: 127.0.0.1]: [/bold cyan]")) or "127.0.0.1"
966
+ try:
967
+ port = int(console.input("[bold cyan]Enter TCP port [default: 443]: [/bold cyan]").strip() or 443)
968
+ attempts = int(console.input("[bold cyan]Enter attempts [default: 5]: [/bold cyan]").strip() or 5)
969
+ except ValueError:
970
+ console.print("[yellow][!] Invalid number entered. Using port 443 and 5 attempts.[/yellow]")
971
+ port, attempts = 443, 5
972
+
973
+ attempts = max(1, min(attempts, 25))
974
+ results = []
975
+ console.print(f"\n[bold cyan][*] Testing TCP connectivity to {host}:{port}...[/bold cyan]\n")
976
+
977
+ for attempt in range(1, attempts + 1):
978
+ start_time = time.perf_counter()
979
+ try:
980
+ with socket.create_connection((host, port), timeout=3):
981
+ elapsed_ms = (time.perf_counter() - start_time) * 1000
982
+ results.append(elapsed_ms)
983
+ console.print(f"[bold green]OPEN[/bold green] attempt {attempt}: {elapsed_ms:.1f} ms")
984
+ except Exception as e:
985
+ console.print(f"[bold red]FAILED[/bold red] attempt {attempt}: {e}")
986
+ time.sleep(0.2)
987
+
988
+ if results:
989
+ table = Table(title=f"Latency Summary: {host}:{port}", border_style="cyan")
990
+ table.add_column("Metric", style="bold cyan")
991
+ table.add_column("Value", style="bold white")
992
+ table.add_row("Successful Attempts", f"{len(results)}/{attempts}")
993
+ table.add_row("Minimum", f"{min(results):.1f} ms")
994
+ table.add_row("Average", f"{sum(results) / len(results):.1f} ms")
995
+ table.add_row("Maximum", f"{max(results):.1f} ms")
996
+ console.print("\n", table)
997
+ pause()
998
+
999
+ def listening_ports():
1000
+ """Displays local listening TCP/UDP ports using OS networking tools."""
1001
+ show_banner()
1002
+ console.print(Panel("[bold yellow]Local Listening Ports Viewer[/bold yellow]", border_style="yellow"))
1003
+ if os.name == "nt":
1004
+ cmd = ["netstat", "-ano"]
1005
+ filter_text = "LISTENING"
1006
+ else:
1007
+ cmd = ["ss", "-tulpen"]
1008
+ filter_text = "LISTEN"
1009
+
1010
+ output, code = run_command(cmd, timeout=15)
1011
+ if code == 127 and os.name != "nt":
1012
+ output, code = run_command(["netstat", "-tulpen"], timeout=15)
1013
+
1014
+ lines = [line for line in output.splitlines() if filter_text.lower() in line.lower()]
1015
+ if not lines:
1016
+ console.print(output or "[yellow]No listening ports found.[/yellow]")
1017
+ pause()
1018
+ return
1019
+
1020
+ table = Table(title="Listening Ports", border_style="cyan")
1021
+ table.add_column("Raw Entry", style="white")
1022
+ for line in lines[:200]:
1023
+ table.add_row(line.strip())
1024
+ console.print("\n", table)
1025
+ if len(lines) > 200:
1026
+ console.print(f"[yellow][!] Showing first 200 of {len(lines)} listening entries.[/yellow]")
1027
+ pause()
1028
+
1029
+ # =============================================================================
1030
+ # 5. NETWORK TRAFFIC & LIVE PACKET SNIFFING
1031
+ # =============================================================================
1032
+ def packet_sniffer():
1033
+ """Real-time live packet sniffer with IP header decoding."""
1034
+ show_banner()
1035
+ console.print(Panel("[bold yellow]🦈 Real-Time Live Packet Sniffer[/bold yellow]", border_style="yellow"))
1036
+ console.print("[dim yellow]Note: Windows requires running terminal as Administrator.[/dim yellow]\n")
1037
+ try:
1038
+ max_packets = int(console.input("[bold cyan]Enter number of packets to capture [default: 15]: [/bold cyan]").strip() or 15)
1039
+ except ValueError:
1040
+ max_packets = 15
1041
+
1042
+ console.print(f"\n[bold red]⚡ Listening for incoming network packets... (Ctrl+C to stop)[/bold red]\n")
1043
+ sniffer = None
1044
+ try:
1045
+ if os.name == 'nt':
1046
+ sniffer = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_IP)
1047
+ host_ip = socket.gethostbyname(socket.gethostname())
1048
+ sniffer.bind((host_ip, 0))
1049
+ sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
1050
+ sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)
1051
+ else:
1052
+ sniffer = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_TCP)
1053
+
1054
+ protocol_map = {1: "ICMP", 2: "IGMP", 6: "TCP", 17: "UDP"}
1055
+ for i in range(1, max_packets + 1):
1056
+ raw_data, addr = sniffer.recvfrom(65535)
1057
+ src_ip, dst_ip, proto_name = addr[0], "Local Machine", "RAW"
1058
+ if len(raw_data) >= 20:
1059
+ iph = struct.unpack('!BBHHHBBH4s4s', raw_data[:20])
1060
+ proto_name = protocol_map.get(iph[6], f"Proto-{iph[6]}")
1061
+ src_ip = socket.inet_ntoa(iph[8])
1062
+ dst_ip = socket.inet_ntoa(iph[9])
1063
+ console.print(f"[bold green]✔ Packet #{i:02d}[/bold green] | [cyan]{src_ip}[/cyan] -> [magenta]{dst_ip}[/magenta] | [yellow]{proto_name:<5}[/yellow] | {len(raw_data)} bytes")
1064
+ except PermissionError:
1065
+ console.print("[bold red]✘ Permission Denied: Run as Administrator / root![/bold red]")
1066
+ except KeyboardInterrupt:
1067
+ console.print("\n[yellow][!] Sniffing stopped by user.[/yellow]")
1068
+ except Exception as e:
1069
+ console.print(f"[bold red]✘ Sniffer Error: {e}[/bold red]")
1070
+ finally:
1071
+ if sniffer and os.name == 'nt':
1072
+ try: sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)
1073
+ except Exception: pass
1074
+ pause()
1075
+
1076
+ # =============================================================================
1077
+ # 5. ADVANCED SERIOUS NETWORK TOOLS
1078
+ # =============================================================================
1079
+ def ssl_inspector():
1080
+ """Inspects SSL/TLS certificates for a given domain."""
1081
+ show_banner()
1082
+ console.print(Panel("[bold yellow]📜 SSL/TLS Certificate Inspector[/bold yellow]", border_style="yellow"))
1083
+ domain = console.input("[bold cyan]Enter domain (e.g. google.com): [/bold cyan]").strip()
1084
+ if not domain:
1085
+ console.print("[red]No domain provided.[/red]")
1086
+ pause()
1087
+ return
1088
+ domain = domain.replace("https://", "").replace("http://", "").split("/")[0].split(":")[0]
1089
+
1090
+ console.print(f"\n[bold cyan][*] Fetching SSL certificate for {domain}...[/bold cyan]\n")
1091
+ try:
1092
+ context = ssl.create_default_context()
1093
+ with socket.create_connection((domain, 443), timeout=5) as sock:
1094
+ with context.wrap_socket(sock, server_hostname=domain) as ssock:
1095
+ cert = ssock.getpeercert()
1096
+
1097
+ table = Table(title=f"SSL Certificate Details: {domain}", border_style="cyan")
1098
+ table.add_column("Property", style="bold cyan")
1099
+ table.add_column("Value", style="bold white")
1100
+
1101
+ subject = dict(x[0] for x in cert['subject'])
1102
+ issuer = dict(x[0] for x in cert['issuer'])
1103
+
1104
+ table.add_row("Subject (CN)", subject.get('commonName', 'N/A'))
1105
+ table.add_row("Issuer (CN)", issuer.get('commonName', 'N/A'))
1106
+ table.add_row("Organization", subject.get('organizationName', 'N/A'))
1107
+ table.add_row("Valid From", cert.get('notBefore', 'N/A'))
1108
+ table.add_row("Valid Until", cert.get('notAfter', 'N/A'))
1109
+ table.add_row("Serial Number", cert.get('serialNumber', 'N/A'))
1110
+
1111
+ sans = [san for type_, san in cert.get('subjectAltName', []) if type_ == 'DNS']
1112
+ table.add_row("Subject Alt Names (SANs)", "\n".join(sans) if sans else "None")
1113
+ console.print(table)
1114
+ except ssl.SSLCertVerificationError as e:
1115
+ console.print(f"[bold red]✘ SSL Verification Failed: {e}[/bold red]")
1116
+ except Exception as e:
1117
+ console.print(f"[bold red]✘ Error fetching certificate: {e}[/bold red]")
1118
+ pause()
1119
+
1120
+ def whois_lookup():
1121
+ """Fetches RDAP/WHOIS domain registration data."""
1122
+ show_banner()
1123
+ console.print(Panel("[bold yellow]🕸️ RDAP / WHOIS Domain Intelligence[/bold yellow]", border_style="yellow"))
1124
+ domain = console.input("[bold cyan]Enter domain (e.g. example.com): [/bold cyan]").strip()
1125
+ if not domain:
1126
+ console.print("[red]No domain provided.[/red]")
1127
+ pause()
1128
+ return
1129
+ domain = domain.replace("https://", "").replace("http://", "").split("/")[0].split(":")[0]
1130
+
1131
+ console.print(f"\n[bold cyan][*] Querying RDAP registry for {domain}...[/bold cyan]\n")
1132
+ try:
1133
+ url = f"https://rdap.org/domain/{domain}"
1134
+ res = requests.get(url, headers={'User-Agent': 'Mozilla/5.0'}, timeout=8).json()
1135
+
1136
+ table = Table(title=f"Domain Registration Data: {domain}", border_style="cyan")
1137
+ table.add_column("Property", style="bold cyan")
1138
+ table.add_column("Value", style="bold white")
1139
+
1140
+ table.add_row("Handle", res.get('handle', 'N/A'))
1141
+ table.add_row("LDH Name", res.get('ldhName', 'N/A'))
1142
+ table.add_row("Status", ", ".join(res.get('status', [])))
1143
+
1144
+ for event in res.get('events', []):
1145
+ table.add_row(f"Event: {event.get('eventAction', 'Unknown')}", event.get('eventDate', 'N/A'))
1146
+
1147
+ for entity in res.get('entities', []):
1148
+ roles = entity.get('roles', [])
1149
+ vcard_array = entity.get('vcardArray', [None, []])[1]
1150
+ fn = next((v[3] for v in vcard_array if v[0] == 'fn'), "Unknown")
1151
+ table.add_row(f"Entity ({', '.join(roles)})", fn)
1152
+
1153
+ ns_list = [ns.get('ldhName') for ns in res.get('nameservers', []) if ns.get('ldhName')]
1154
+ table.add_row("Nameservers", "\n".join(ns_list) if ns_list else "N/A")
1155
+ console.print(table)
1156
+ except Exception as e:
1157
+ console.print(f"[bold red]✘ Error parsing RDAP data: {e}[/bold red]")
1158
+ pause()
1159
+
1160
+ def traceroute_tool():
1161
+ """Executes system traceroute/tracert."""
1162
+ show_banner()
1163
+ console.print(Panel("[bold yellow]🛤️ Traceroute & Route Analysis[/bold yellow]", border_style="yellow"))
1164
+ target = console.input("[bold cyan]Enter target IP or Domain: [/bold cyan]").strip()
1165
+ if not target:
1166
+ console.print("[red]No target provided.[/red]")
1167
+ pause()
1168
+ return
1169
+
1170
+ console.print(f"\n[bold cyan][*] Running traceroute to {target}...[/bold cyan]\n")
1171
+ try:
1172
+ cmd = ['tracert', '-d', '-w', '2', '-h', '30', target] if os.name == 'nt' else ['traceroute', '-n', '-w', '2', '-m', '30', target]
1173
+ process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, encoding='utf-8', errors='replace')
1174
+ for line in process.stdout:
1175
+ console.print(f"[dim]{line.strip()}[/dim]")
1176
+ process.wait()
1177
+ except FileNotFoundError:
1178
+ console.print("[bold red]✘ Traceroute command not found on this system.[/bold red]")
1179
+ except Exception as e:
1180
+ console.print(f"[bold red]✘ Error: {e}[/bold red]")
1181
+ pause()
1182
+
1183
+ def network_interfaces():
1184
+ """Displays network interfaces and ARP table."""
1185
+ show_banner()
1186
+ console.print(Panel("[bold yellow]📡 Network Interfaces & ARP Table[/bold yellow]", border_style="yellow"))
1187
+ try:
1188
+ if os.name == 'nt':
1189
+ cmd_ip, cmd_arp = ['ipconfig', '/all'], ['arp', '-a']
1190
+ else:
1191
+ cmd_ip = ['ip', 'addr'] if os.path.exists('/sbin/ip') else ['ifconfig']
1192
+ cmd_arp = ['ip', 'neigh'] if os.path.exists('/sbin/ip') else ['arp', '-a']
1193
+
1194
+ console.print(Panel("[bold green]IP Configuration[/bold green]", border_style="green"))
1195
+ console.print(f"[dim]{subprocess.run(cmd_ip, capture_output=True, text=True, encoding='utf-8', errors='replace').stdout}[/dim]")
1196
+
1197
+ console.print(Panel("[bold green]ARP / Neighbor Table[/bold green]", border_style="green"))
1198
+ console.print(f"[dim]{subprocess.run(cmd_arp, capture_output=True, text=True, encoding='utf-8', errors='replace').stdout}[/dim]")
1199
+ except Exception as e:
1200
+ console.print(f"[bold red]✘ Error fetching network info: {e}[/bold red]")
1201
+ pause()
1202
+
1203
+ # =============================================================================
1204
+ # 6. ACTIVATION & MAIN MENU
1205
+ # =============================================================================
1206
+ def activation_screen():
1207
+ """Welcome screen and activation animation."""
1208
+ show_banner()
1209
+ name = console.input("[bold cyan]Hi user, what is your name? [/bold cyan]").strip()
1210
+ if not name:
1211
+ name = "Operative"
1212
+ console.print(f"\n[bold green]Welcome {name}![/bold green] Ready to access the Super Toolkit.\n")
1213
+
1214
+ for _ in track(range(100), description="[bold cyan]please wait...[/bold cyan]"):
1215
+ time.sleep(0.008)
1216
+ time.sleep(0.3)
1217
+
1218
+ def main():
1219
+ """Main interactive loop."""
1220
+ try:
1221
+ activation_screen()
1222
+ except (KeyboardInterrupt, EOFError):
1223
+ clear_screen()
1224
+ sys.exit(0)
1225
+
1226
+ while True:
1227
+ try:
1228
+ show_banner()
1229
+ options = [
1230
+ "1. 🔑 Password Generator & Security Evaluator",
1231
+ "2. 🔒 Hash Generator & Verifier (MD5, SHA-256, SHA-512...)",
1232
+ "3. 📡 My Network & Public IP Information",
1233
+ "4. 🌐 Target Domain / Website IP & DNS Lookup",
1234
+ "5. 🌍 GeoIP Intelligence Location Tracker",
1235
+ "6. 🏷️ MAC Address Vendor & OUI Lookup",
1236
+ "7. 🖧 CIDR Subnet Calculator & Ping Sweep",
1237
+ "8. ⚡ Multi-Threaded Port Scanner (with Service Detection)",
1238
+ "9. 🕵️ Subdomain Finder & DNS Recon",
1239
+ "10. 📁 Web Directory & Endpoint Fuzzer",
1240
+ "11. 🔍 HTTP Header Inspector & Security Auditor",
1241
+ "12. 🦈 Real-Time Live Packet Sniffer",
1242
+ "13. 📜 SSL/TLS Certificate Inspector",
1243
+ "14. 🕸️ RDAP / WHOIS Domain Intelligence",
1244
+ "15. 🛤️ Traceroute & Route Analysis",
1245
+ "16. 📡 Network Interfaces & ARP Table Analyzer",
1246
+ "0. 🚪 Exit Super Toolkit",
1247
+ ]
1248
+ options = [
1249
+ "1. Password Generator - Create strong passwords and estimate strength",
1250
+ "2. Hash Generator & Verifier - Generate or verify MD5/SHA hashes",
1251
+ "3. My Network Info - Show local/public IP and geolocation summary",
1252
+ "4. Domain IP Lookup - Resolve domains and reverse DNS records",
1253
+ "5. GeoIP Tracker - Locate an IP or domain by public GeoIP data",
1254
+ "6. MAC Vendor Lookup - Identify manufacturer from a MAC/OUI prefix",
1255
+ "7. Subnet Calculator & Sweep - Calculate CIDR details and find live hosts",
1256
+ "8. Port Scanner - Scan TCP ports and identify common services",
1257
+ "9. Subdomain Finder - Resolve common subdomains for a target domain",
1258
+ "10. Web Directory Fuzzer - Check common web paths and endpoints",
1259
+ "11. HTTP Header Inspector - View headers and audit security headers",
1260
+ "12. Packet Sniffer - Capture and summarize live raw IP packets",
1261
+ "13. SSL/TLS Inspector - Read certificate issuer, validity, and SANs",
1262
+ "14. RDAP / WHOIS Lookup - Fetch domain registration intelligence",
1263
+ "15. Traceroute - Trace network path hops to a target",
1264
+ "16. Interfaces & ARP - Show local adapters and ARP/neighbor table",
1265
+ "17. DNS Record Lookup - Query A, AAAA, MX, NS, TXT, SOA, and CNAME",
1266
+ "18. HTTP Status Checker - Show status, redirects, timing, and server info",
1267
+ "19. TCP Ping - Measure TCP connection latency to a host and port",
1268
+ "20. Listening Ports - List local services listening on network ports",
1269
+ "21. Tech Stack Detector - Fingerprint web frameworks, CDN/WAF, and cookies",
1270
+ "22. Web Endpoint Auditor - Check common web metadata and exposure paths",
1271
+ "0. Exit - Close Super Toolkit",
1272
+ ]
1273
+ choice = questionary.select(
1274
+ "Select a Tool to Launch:",
1275
+ choices=options,
1276
+ pointer="▶ ",
1277
+ style=MENU_STYLE
1278
+ ).ask()
1279
+
1280
+ if choice and choice.startswith("0."):
1281
+ clear_screen()
1282
+ console.print(Panel("[bold green]Thank you for using Super Toolkit. Stay safe & secure![/bold green]", border_style="cyan"))
1283
+ break
1284
+
1285
+ if not choice or "0. 🚪 Exit" in choice:
1286
+ clear_screen()
1287
+ console.print(Panel("[bold green]Thank you for using Super Toolkit. Stay safe & secure![/bold green]", border_style="cyan"))
1288
+ break
1289
+
1290
+ if "1. 🔑" in choice: password_generator()
1291
+ elif "2. 🔒" in choice: hash_tool()
1292
+ elif "3. 📡" in choice: my_network_info()
1293
+ elif "4. 🌐" in choice: target_ip_lookup()
1294
+ elif "5. 🌍" in choice: geoip_lookup()
1295
+ elif "6. 🏷️" in choice: mac_lookup()
1296
+ elif "7. 🖧" in choice: subnet_scanner()
1297
+ elif "8. ⚡" in choice: port_scanner()
1298
+ elif "9. 🕵️" in choice: subdomain_finder()
1299
+ elif "10. 📁" in choice: dir_fuzzer()
1300
+ elif "11. 🔍" in choice: header_inspector()
1301
+ elif "12. 🦈" in choice: packet_sniffer()
1302
+ elif "13. 📜" in choice: ssl_inspector()
1303
+ elif "14. 🕸️" in choice: whois_lookup()
1304
+ elif "15. 🛤️" in choice: traceroute_tool()
1305
+ elif "16. 📡" in choice: network_interfaces()
1306
+
1307
+ menu_tools = {
1308
+ "1.": password_generator,
1309
+ "2.": hash_tool,
1310
+ "3.": my_network_info,
1311
+ "4.": target_ip_lookup,
1312
+ "5.": geoip_lookup,
1313
+ "6.": mac_lookup,
1314
+ "7.": subnet_scanner,
1315
+ "8.": port_scanner,
1316
+ "9.": subdomain_finder,
1317
+ "10.": dir_fuzzer,
1318
+ "11.": header_inspector,
1319
+ "12.": packet_sniffer,
1320
+ "13.": ssl_inspector,
1321
+ "14.": whois_lookup,
1322
+ "15.": traceroute_tool,
1323
+ "16.": network_interfaces,
1324
+ "17.": dns_record_lookup,
1325
+ "18.": http_status_checker,
1326
+ "19.": tcp_ping_tool,
1327
+ "20.": listening_ports,
1328
+ "21.": tech_stack_detector,
1329
+ "22.": web_endpoint_auditor,
1330
+ }
1331
+ for prefix, tool_func in sorted(menu_tools.items(), key=lambda item: len(item[0]), reverse=True):
1332
+ if choice.startswith(prefix):
1333
+ tool_func()
1334
+ break
1335
+
1336
+ except (KeyboardInterrupt, EOFError):
1337
+ clear_screen()
1338
+ console.print("[bold red]\nOperation interrupted. Exiting...[/bold red]")
1339
+ break
1340
+ except Exception as e:
1341
+ console.print(f"[bold red]\nUnexpected error: {e}[/bold red]")
1342
+ pause()
1343
+
1344
+ if __name__ == "__main__":
1345
+ main()