From a77ff3b42c6a82ae68dac7a0d7a5f09bfbb9cdfd Mon Sep 17 00:00:00 2001 From: abhinavmehra2004 Date: Tue, 30 Jun 2026 17:02:25 +0530 Subject: [PATCH 1/2] feat: add 7 new network diagnostic modules - traceroute.py: ICMP/UDP TTL-based path analysis with hostname resolution - whois_lookup.py: WHOIS registration + IP geolocation via ip-api.com - ping_monitor.py: continuous TCP ping with live min/max/avg/jitter/loss stats - waf_fingerprint.py: multi-probe WAF/CDN detection against 15 known signatures - speedtest.py: live bandwidth estimator via CDN streaming with tier rating - net_monitor.py: live per-interface dashboard (RX/TX/packets/errors/drops) - session_logger.py: TeeStream stdout capture with .txt/.json report export Updated main.py and menu.py to route new menu choices [5]-[7], [A], [B], [C], [L] Added python-whois to requirements.txt Added CONTRIBUTION.md documenting all changes --- CONTRIBUTION.md | 55 +++++++++++++ main.py | 39 +++++++++- modules/menu.py | 94 +++++++++++++++++++++- modules/net_monitor.py | 130 +++++++++++++++++++++++++++++++ modules/ping_monitor.py | 110 ++++++++++++++++++++++++++ modules/session_logger.py | 156 +++++++++++++++++++++++++++++++++++++ modules/speedtest.py | 117 ++++++++++++++++++++++++++++ modules/traceroute.py | 72 +++++++++++++++++ modules/waf_fingerprint.py | 140 +++++++++++++++++++++++++++++++++ modules/whois_lookup.py | 103 ++++++++++++++++++++++++ requirements.txt | 3 +- 11 files changed, 1012 insertions(+), 7 deletions(-) create mode 100644 CONTRIBUTION.md create mode 100644 modules/net_monitor.py create mode 100644 modules/ping_monitor.py create mode 100644 modules/session_logger.py create mode 100644 modules/speedtest.py create mode 100644 modules/traceroute.py create mode 100644 modules/waf_fingerprint.py create mode 100644 modules/whois_lookup.py diff --git a/CONTRIBUTION.md b/CONTRIBUTION.md new file mode 100644 index 0000000..d8fcada --- /dev/null +++ b/CONTRIBUTION.md @@ -0,0 +1,55 @@ +# πŸ› οΈ Contribution β€” NET-Shell Feature Expansion + +## Overview + +This contribution significantly expands the NET-Shell toolkit from a 4-tool diagnostic suite into a comprehensive **7-module operator platform**, adding active reconnaissance, infrastructure analysis, performance benchmarking, and full session logging capabilities. + +--- + +## Modules Added + +### 1. πŸ—ΊοΈ Traceroute Path Analysis (`modules/traceroute.py`) +Implements network hop tracing using raw ICMP/UDP socket probes with incrementing TTL values β€” the same technique used by the Unix `traceroute` utility. Each hop is resolved to a hostname where possible, with per-hop RTT displayed in a formatted operator table. Supports configurable max hop depth and graceful Ctrl+C interruption. + +### 2. 🌍 WHOIS & IP Geolocation Lookup (`modules/whois_lookup.py`) +Performs a two-part intelligence query on any domain or IP address: +- **Geolocation** via the `ip-api.com` JSON API β€” returns country, region, city, ISP, ASN, and organization. +- **WHOIS registration data** via the `python-whois` library β€” returns registrar, creation/expiry dates, name servers, and contact emails. + +### 3. πŸ“‘ Continuous Ping / Latency Monitor (`modules/ping_monitor.py`) +A TCP-based connectivity probe that continuously measures round-trip time to a target host on a configurable port. Displays per-probe results with a color-coded latency bar (green/yellow/red) and live statistics including minimum, maximum, average latency, jitter (mean absolute deviation), and packet loss percentage. Final summary printed on exit. + +### 4. πŸ” WAF / Firewall Fingerprinter (`modules/waf_fingerprint.py`) +Sends a battery of 5 crafted HTTP probes (normal request, SQL injection, XSS, path traversal, malicious User-Agent) to a target and analyzes response status codes and headers against a database of **15 known WAF/CDN signatures** including Cloudflare, Akamai, AWS CloudFront, Imperva Incapsula, ModSecurity, F5 BIG-IP, and more. Produces a fingerprint report with server banner and detected protection layers. + +### 5. ⚑ Bandwidth Speed Estimator (`modules/speedtest.py`) +Measures real-world download throughput by streaming test payloads from public CDN endpoints (Cloudflare Workers, GitHub). Displays a live animated progress bar with real-time Mbps readout during download, then produces a summary report with per-endpoint results, average speed, and a connection tier rating (Excellent / Good / Fair / Poor). + +### 6. πŸ“Š Network Interface Monitor (`modules/net_monitor.py`) +A live, auto-refreshing terminal dashboard powered by `psutil` that displays per-interface network statistics for every adapter on the system. Metrics include download/upload rates (human-readable), packet counts, error counts, and drop counts β€” all updated every configurable interval using in-place ANSI cursor redraw. Activity levels are visualized with a color-coded bar that saturates at 10 MB/s. + +### 7. πŸ—‚οΈ Session Logger & Report Exporter (`modules/session_logger.py`) +A stdout interception system using a `TeeStream` wrapper that transparently captures the output of any tool run during the session without disrupting live display. Captured entries are stored in memory with timestamps and tool labels. Operators can view a paginated in-session log or export the full session as: +- A structured **plain-text report** (`.txt`) +- A machine-readable **JSON report** (`.json`) + +Automatic capture is wired into the WAF Fingerprinter, Speed Estimator, and Interface Monitor β€” no manual intervention required. + +--- + +## Files Modified + +| File | Change | +|------|--------| +| `modules/menu.py` | Added imports, 7 new menu entries (`[5]`–`[7]`, `[A]`–`[C]`, `[L]`), and handler functions for each new tool | +| `main.py` | Added imports and routing for all new menu choices in the main event loop | +| `requirements.txt` | Added `python-whois` dependency | + +--- + +## Technical Highlights + +- **No heavy new dependencies** β€” all network operations use Python stdlib (`socket`, `urllib`, `ssl`, `asyncio`); only `python-whois` added +- **macOS SSL compatibility** β€” all HTTPS requests use a safe SSL context (certifi bundle or unverified fallback) to prevent `CERTIFICATE_VERIFY_FAILED` errors common on macOS Python installs +- **Consistent operator UX** β€” all modules follow the existing ANSI color scheme (green/cyan/yellow/red), use the same interrupt-handling pattern (`KeyboardInterrupt`), and return cleanly to the main menu +- **Modular architecture** β€” each feature is fully self-contained in its own module file, keeping `main.py` and `menu.py` as thin orchestration layers diff --git a/main.py b/main.py index 4be7bb3..c00e8a2 100644 --- a/main.py +++ b/main.py @@ -4,7 +4,8 @@ # Add current directory to path to ensure modules are found sys.path.append(os.path.dirname(os.path.abspath(__file__))) -from modules import menu, host, utils, scanner, system +from modules import menu, host, utils, scanner, system, traceroute, whois_lookup, ping_monitor +from modules import waf_fingerprint, speedtest, session_logger, net_monitor # Conditional imports for setup, update, and maintenance utilities try: @@ -61,16 +62,50 @@ def start(): input("\nPress Enter to return...") elif choice == '5': + menu.handle_traceroute_input() + input("\nPress Enter to return...") + + elif choice == '6': + menu.handle_whois_input() + input("\nPress Enter to return...") + + elif choice == '7': + menu.handle_ping_monitor_input() + input("\nPress Enter to return...") + + elif choice == '8': m_choice = menu.maintenance_menu() if m_choice == '1': setup.install_requirements() elif m_choice == '2': cleaner.purge_cache() - elif choice == '6': + elif choice == '9': update.check_for_updates() input("\nPress Enter to return...") + elif choice == 'a': + session_logger.start_capture("WAF / Firewall Fingerprinter") + menu.handle_waf_input() + session_logger.stop_capture() + input("\nPress Enter to return...") + + elif choice == 'b': + session_logger.start_capture("Bandwidth Speed Estimator") + menu.handle_speedtest_input() + session_logger.stop_capture() + input("\nPress Enter to return...") + + elif choice == 'c': + session_logger.start_capture("Network Interface Monitor") + menu.handle_net_monitor_input() + session_logger.stop_capture() + input("\nPress Enter to return...") + + elif choice == 'l': + menu.handle_session_logger_input() + input("\nPress Enter to return...") + elif choice == 'q': utils.typing_effect("\033[91mSession Terminated. Goodbye, Operator.\033[0m") sys.exit(0) diff --git a/modules/menu.py b/modules/menu.py index a1be0a9..4e8a662 100644 --- a/modules/menu.py +++ b/modules/menu.py @@ -1,7 +1,8 @@ import os import time from ui import name -from modules import host, flood, utils, scanner, system +from modules import host, flood, utils, scanner, system, traceroute, whois_lookup, ping_monitor +from modules import waf_fingerprint, speedtest, session_logger, net_monitor # ANSI Styling for a cohesive terminal look GREEN = "\033[92m" @@ -31,10 +32,17 @@ def display_main_menu(): print(f"{GREEN}[2]{RESET} HTTP Request Flood (Full Config)") print(f"{GREEN}[3]{RESET} TCP Port Scanner") print(f"{GREEN}[4]{RESET} Local System Information") - print(f"{YELLOW}[5]{RESET} Maintenance & Setup") - print(f"{YELLOW}[6]{RESET} Check for Updates") + print(f"{CYAN}[5]{RESET} Traceroute Path Analysis") + print(f"{CYAN}[6]{RESET} WHOIS & IP Geolocation Lookup") + print(f"{CYAN}[7]{RESET} Continuous Ping / Latency Monitor") + print(f"{CYAN}[A]{RESET} WAF / Firewall Fingerprinter") + print(f"{CYAN}[B]{RESET} Bandwidth Speed Estimator") + print(f"{CYAN}[C]{RESET} Network Interface Monitor") + print(f"{CYAN}[L]{RESET} Session Logger & Report Exporter") + print(f"{YELLOW}[8]{RESET} Maintenance & Setup") + print(f"{YELLOW}[9]{RESET} Check for Updates") print(f"{RED}[Q]{RESET} Exit System") - print(f"{CYAN}{'━'*35}{RESET}") + print(f"{CYAN}{'━'*40}{RESET}") try: return input(PROMPT).strip().lower() @@ -74,6 +82,84 @@ def handle_flood_input(): except ValueError: print(f"{RED}[!] Input Error: Please enter numeric values for counts and intervals.{RESET}") +def handle_traceroute_input(): + """Collects target and runs traceroute path analysis.""" + print(f"\n{CYAN}--- TRACEROUTE ---{RESET}") + try: + target = input(f"{CYAN}Target (IP or Domain): {RESET}").strip() + hops_input = input(f"{CYAN}Max Hops [30]: {RESET}").strip() + max_hops = int(hops_input) if hops_input.isdigit() else 30 + traceroute.traceroute(target, max_hops=max_hops) + except KeyboardInterrupt: + print(f"\n{YELLOW}[!] Aborted. Returning to net-shell...{RESET}") + time.sleep(1) + +def handle_whois_input(): + """Collects target and runs WHOIS + geolocation lookup.""" + print(f"\n{CYAN}--- WHOIS & GEOLOCATION ---{RESET}") + try: + target = input(f"{CYAN}Target (Domain or IP): {RESET}").strip() + whois_lookup.whois_lookup(target) + except KeyboardInterrupt: + print(f"\n{YELLOW}[!] Aborted. Returning to net-shell...{RESET}") + time.sleep(1) + +def handle_ping_monitor_input(): + """Collects config and runs the continuous latency monitor.""" + print(f"\n{CYAN}--- PING / LATENCY MONITOR ---{RESET}") + try: + target = input(f"{CYAN}Target (IP or Domain): {RESET}").strip() + count_input = input(f"{CYAN}Probe Count [20]: {RESET}").strip() + interval_input = input(f"{CYAN}Interval Seconds [1]: {RESET}").strip() + port_input = input(f"{CYAN}TCP Port [80]: {RESET}").strip() + count = int(count_input) if count_input.isdigit() else 20 + interval = float(interval_input) if interval_input else 1.0 + port = int(port_input) if port_input.isdigit() else 80 + ping_monitor.ping_monitor(target, count=count, interval=interval, port=port) + except KeyboardInterrupt: + print(f"\n{YELLOW}[!] Aborted. Returning to net-shell...{RESET}") + time.sleep(1) + except ValueError: + print(f"{RED}[!] Input Error: Please enter numeric values.{RESET}") + +def handle_waf_input(): + """Collects target URL and runs WAF/firewall fingerprinting.""" + print(f"\n{CYAN}--- WAF / FIREWALL FINGERPRINTER ---{RESET}") + try: + target = input(f"{CYAN}Target URL (e.g. https://example.com): {RESET}").strip() + waf_fingerprint.fingerprint(target) + except KeyboardInterrupt: + print(f"\n{YELLOW}[!] Aborted. Returning to net-shell...{RESET}") + time.sleep(1) + +def handle_speedtest_input(): + """Runs the bandwidth speed estimator.""" + print(f"\n{CYAN}--- BANDWIDTH SPEED ESTIMATOR ---{RESET}") + try: + speedtest.run_speed_test() + except KeyboardInterrupt: + print(f"\n{YELLOW}[!] Aborted. Returning to net-shell...{RESET}") + time.sleep(1) + +def handle_net_monitor_input(): + """Collects config and runs the live network interface monitor.""" + print(f"\n{CYAN}--- NETWORK INTERFACE MONITOR ---{RESET}") + try: + interval_input = input(f"{CYAN}Refresh interval seconds [1]: {RESET}").strip() + duration_input = input(f"{CYAN}Monitor duration seconds [60]: {RESET}").strip() + interval = float(interval_input) if interval_input else 1.0 + duration = int(duration_input) if duration_input.isdigit() else 60 + net_monitor.run_interface_monitor(interval=interval, duration=duration) + except KeyboardInterrupt: + print(f"\n{YELLOW}[!] Aborted. Returning to net-shell...{RESET}") + time.sleep(1) + except ValueError: + print(f"{RED}[!] Input Error: Please enter numeric values.{RESET}") + +def handle_session_logger_input(): + """Opens the session logger sub-menu.""" + session_logger.session_menu() + def maintenance_menu(): """Renders the maintenance sub-menu with interrupt handling.""" header() diff --git a/modules/net_monitor.py b/modules/net_monitor.py new file mode 100644 index 0000000..e941d32 --- /dev/null +++ b/modules/net_monitor.py @@ -0,0 +1,130 @@ +import psutil +import time +import sys +import os + +GREEN = "\033[92m" +RED = "\033[91m" +YELLOW = "\033[93m" +CYAN = "\033[96m" +DIM = "\033[2m" +BOLD = "\033[1m" +RESET = "\033[0m" +CLEAR_LINE = "\033[2K\r" + +def _bytes_fmt(n: float) -> str: + """Formats bytes into a human-readable string.""" + for unit in ("B", "KB", "MB", "GB"): + if abs(n) < 1024: + return f"{n:.1f} {unit}" + n /= 1024 + return f"{n:.1f} TB" + +def _bar(ratio: float, width: int = 18) -> str: + ratio = max(0.0, min(ratio, 1.0)) + filled = int(ratio * width) + color = GREEN if ratio < 0.6 else (YELLOW if ratio < 0.85 else RED) + return f"{color}{'β–ˆ' * filled}{'β–‘' * (width - filled)}{RESET}" + +def _get_interface_stats() -> dict: + """Returns per-interface counters from psutil.""" + return psutil.net_io_counters(pernic=True) + +def _clear_dashboard(line_count: int): + """Moves cursor up 'line_count' lines to redraw the dashboard in-place.""" + sys.stdout.write(f"\033[{line_count}A") + +def run_interface_monitor(interval: float = 1.0, duration: int = 60): + """ + Displays a live, auto-refreshing dashboard of per-interface network stats: + bytes sent/received, packets, errors, and drops β€” updated every `interval` seconds. + Press Ctrl+C to stop early. + """ + print(f"\n{CYAN}{'━'*65}{RESET}") + print(f"{CYAN} NETWORK INTERFACE MONITOR (refresh: {interval}s | runtime: {duration}s){RESET}") + print(f"{CYAN} Press Ctrl+C to stop{RESET}") + print(f"{CYAN}{'━'*65}{RESET}\n") + + try: + prev_stats = _get_interface_stats() + prev_time = time.perf_counter() + first_draw = True + drawn_lines = 0 + elapsed = 0 + + while elapsed < duration: + time.sleep(interval) + curr_stats = _get_interface_stats() + curr_time = time.perf_counter() + dt = curr_time - prev_time + + # Collect all rows to know how many lines to redraw + rows = [] + for iface, curr in curr_stats.items(): + prev = prev_stats.get(iface) + if prev is None: + continue + + rx_rate = (curr.bytes_recv - prev.bytes_recv) / dt + tx_rate = (curr.bytes_sent - prev.bytes_sent) / dt + pkt_rx = (curr.packets_recv - prev.packets_recv) + pkt_tx = (curr.packets_sent - prev.packets_sent) + errin = curr.errin + errout = curr.errout + dropin = curr.dropin + dropout = curr.dropout + + rows.append((iface, rx_rate, tx_rate, pkt_rx, pkt_tx, errin, errout, dropin, dropout)) + + # Redraw β€” move cursor up on subsequent draws + if not first_draw: + _clear_dashboard(drawn_lines) + + lines_written = 0 + header = ( + f"{DIM}{'INTERFACE':<14} {'β–Ό RX':>12} {'β–² TX':>12} " + f"{'PKT RX':>8} {'PKT TX':>8} " + f"{'ERR':>5} {'DROP':>5} {'ACTIVITY'}{RESET}" + ) + print(header) + print(f"{DIM}{'─'*75}{RESET}") + lines_written += 2 + + for (iface, rx_rate, tx_rate, pkt_rx, pkt_tx, + errin, errout, dropin, dropout) in rows: + + err_total = errin + errout + drop_total = dropin + dropout + activity = _bar(min((rx_rate + tx_rate) / (10 * 1024 * 1024), 1.0)) # saturates at 10 MB/s + + err_col = f"{RED}{err_total}{RESET}" if err_total else f"{DIM}0{RESET}" + drop_col = f"{RED}{drop_total}{RESET}" if drop_total else f"{DIM}0{RESET}" + + print( + f"{GREEN}{iface:<14}{RESET}" + f"{CYAN}{_bytes_fmt(rx_rate):>10}/s{RESET}" + f" {YELLOW}{_bytes_fmt(tx_rate):>10}/s{RESET}" + f" {pkt_rx:>8} {pkt_tx:>8}" + f" {err_col:>5} {drop_col:>5} {activity}" + ) + lines_written += 1 + + # Footer + elapsed += interval + remaining = duration - elapsed + sys.stdout.write( + f"\n{DIM}╰─ Elapsed: {elapsed:.0f}s | Remaining: {remaining:.0f}s{RESET}\n" + ) + sys.stdout.flush() + lines_written += 2 + + drawn_lines = lines_written + first_draw = False + prev_stats = curr_stats + prev_time = curr_time + + except KeyboardInterrupt: + print(f"\n\n{YELLOW}[!] Monitor stopped by operator.{RESET}") + return + + print(f"\n{GREEN}[+] Monitoring session complete.{RESET}") diff --git a/modules/ping_monitor.py b/modules/ping_monitor.py new file mode 100644 index 0000000..ac52b3b --- /dev/null +++ b/modules/ping_monitor.py @@ -0,0 +1,110 @@ +import socket +import time +import sys + +GREEN = "\033[92m" +RED = "\033[91m" +YELLOW = "\033[93m" +CYAN = "\033[96m" +DIM = "\033[2m" +RESET = "\033[0m" + +def _tcp_ping(host: str, port: int, timeout: float) -> float | None: + """ + Measures TCP connect latency to host:port. + Returns latency in ms, or None on failure/timeout. + """ + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.settimeout(timeout) + start = time.perf_counter() + try: + sock.connect((host, port)) + latency = (time.perf_counter() - start) * 1000 + return round(latency, 2) + except (socket.timeout, socket.error): + return None + finally: + sock.close() + +def _latency_bar(latency: float, threshold_ms: float = 200, width: int = 25) -> str: + """Renders a color-coded latency bar.""" + ratio = min(latency / threshold_ms, 1.0) + filled = int(ratio * width) + color = GREEN if latency < 80 else (YELLOW if latency < 200 else RED) + return f"{color}{'β–ˆ' * filled}{'β–‘' * (width - filled)}{RESET}" + +def ping_monitor(target: str, count: int = 20, interval: float = 1.0, port: int = 80, timeout: float = 2.0): + """ + Continuously TCP-pings a host and prints live latency with statistics. + Press Ctrl+C to stop early and see the final summary. + """ + try: + ip = socket.gethostbyname(target) + except socket.gaierror: + print(f"{RED}[!] Could not resolve host: {target}{RESET}") + return + + print(f"\n{CYAN}{'━'*60}{RESET}") + print(f"{CYAN} PING MONITOR β†’ {target} ({ip}) on port {port}{RESET}") + print(f"{CYAN} Probes: {count} | Interval: {interval}s | Timeout: {timeout}s{RESET}") + print(f"{CYAN}{'━'*60}{RESET}") + print(f"{DIM}{'#':<5} {'LATENCY':>10} {'BAR':<26} STATUS{RESET}") + print(f"{CYAN}{'━'*60}{RESET}") + + latencies = [] + lost = 0 + + try: + for i in range(1, count + 1): + ms = _tcp_ping(ip, port, timeout) + + if ms is None: + lost += 1 + print(f"{RED}{i:<5}{RESET} {'TIMEOUT':>10} {'β–‘'*25} {RED}LOSS{RESET}") + else: + latencies.append(ms) + bar = _latency_bar(ms) + color = GREEN if ms < 80 else (YELLOW if ms < 200 else RED) + print(f"{GREEN}{i:<5}{RESET} {color}{ms:>9.2f}ms{RESET} {bar} {GREEN}OK{RESET}") + + # Live stats footer (overwrite same line) + if latencies: + mn = min(latencies) + mx = max(latencies) + avg = sum(latencies) / len(latencies) + # Jitter = mean absolute deviation of successive differences + jitter = 0.0 + if len(latencies) > 1: + diffs = [abs(latencies[j] - latencies[j-1]) for j in range(1, len(latencies))] + jitter = sum(diffs) / len(diffs) + loss_pct = (lost / i) * 100 + sys.stdout.write( + f"\r{DIM}╰─ min:{mn:.1f}ms max:{mx:.1f}ms " + f"avg:{avg:.1f}ms jitter:{jitter:.1f}ms " + f"loss:{loss_pct:.0f}%{RESET} " + ) + sys.stdout.flush() + + if i < count: + time.sleep(interval) + + except KeyboardInterrupt: + print(f"\n\n{YELLOW}[!] Monitor interrupted by operator.{RESET}") + + # Final summary + print(f"\n\n{YELLOW}{'━'*40}{RESET}") + print(f"{YELLOW} FINAL SUMMARY{RESET}") + print(f"{YELLOW}{'━'*40}{RESET}") + total = len(latencies) + lost + if latencies: + print(f" Probes sent : {total}") + print(f" Received : {GREEN}{len(latencies)}{RESET}") + print(f" Lost : {RED}{lost} ({(lost/total)*100:.1f}%){RESET}") + print(f" Min latency : {GREEN}{min(latencies):.2f} ms{RESET}") + print(f" Max latency : {RED}{max(latencies):.2f} ms{RESET}") + print(f" Avg latency : {CYAN}{sum(latencies)/len(latencies):.2f} ms{RESET}") + if len(latencies) > 1: + diffs = [abs(latencies[j] - latencies[j-1]) for j in range(1, len(latencies))] + print(f" Jitter : {YELLOW}{sum(diffs)/len(diffs):.2f} ms{RESET}") + else: + print(f" {RED}All probes lost β€” host may be down or blocking TCP on port {port}.{RESET}") diff --git a/modules/session_logger.py b/modules/session_logger.py new file mode 100644 index 0000000..1264fc5 --- /dev/null +++ b/modules/session_logger.py @@ -0,0 +1,156 @@ +import os +import json +import sys +import time +from datetime import datetime +from io import StringIO + +GREEN = "\033[92m" +RED = "\033[91m" +YELLOW = "\033[93m" +CYAN = "\033[96m" +DIM = "\033[2m" +RESET = "\033[0m" + +# ANSI escape stripper for clean file output +import re +ANSI_ESCAPE = re.compile(r'\x1b\[[0-9;]*m') + +def strip_ansi(text: str) -> str: + return ANSI_ESCAPE.sub("", text) + +# ── Global session log buffer ───────────────────────────────────────────────── +_session_log: list[dict] = [] +_session_start: str = datetime.now().strftime("%Y-%m-%d %H:%M:%S") +_capturing = False +_capture_buffer = StringIO() + +class _TeeStream: + """Writes to both the real stdout and a capture buffer simultaneously.""" + def __init__(self, real_stream, buffer: StringIO): + self._real = real_stream + self._buf = buffer + + def write(self, data): + self._real.write(data) + self._buf.write(data) + + def flush(self): + self._real.flush() + self._buf.flush() + + def __getattr__(self, attr): + return getattr(self._real, attr) + + +def start_capture(tool_name: str): + """Begins intercepting stdout so tool output is recorded.""" + global _capturing, _capture_buffer + _capturing = True + _capture_buffer = StringIO() + sys.stdout = _TeeStream(sys.__stdout__, _capture_buffer) + _session_log.append({ + "tool" : tool_name, + "timestamp" : datetime.now().strftime("%H:%M:%S"), + "output" : None, # filled by stop_capture + }) + +def stop_capture(): + """Stops stdout interception and saves captured output to the log.""" + global _capturing + sys.stdout = sys.__stdout__ + if _session_log and _session_log[-1]["output"] is None: + _session_log[-1]["output"] = strip_ansi(_capture_buffer.getvalue()) + _capturing = False + + +def view_session_log(): + """Prints the in-memory session log to the terminal.""" + print(f"\n{CYAN}{'━'*60}{RESET}") + print(f"{CYAN} SESSION LOG β€” Started: {_session_start}{RESET}") + print(f"{CYAN}{'━'*60}{RESET}") + + if not _session_log: + print(f" {YELLOW}[!] No tool outputs captured yet.{RESET}") + print(f" {DIM}Outputs are captured automatically each time you run a tool.{RESET}") + return + + for i, entry in enumerate(_session_log, 1): + print(f"\n{GREEN}[{i}] {entry['tool']}{RESET} β€” {DIM}{entry['timestamp']}{RESET}") + print(f"{DIM}{'─'*50}{RESET}") + lines = (entry["output"] or "").strip().splitlines() + for line in lines[:40]: # cap preview at 40 lines + print(f" {line}") + if len(lines) > 40: + print(f" {DIM}... ({len(lines)-40} more lines in export){RESET}") + + print(f"\n{CYAN}{'━'*60}{RESET}") + print(f" Total recorded tool runs: {GREEN}{len(_session_log)}{RESET}") + + +def export_report(export_dir: str = None): + """ + Exports the full session log as both .txt and .json files. + Defaults to the current working directory. + """ + if not _session_log: + print(f" {YELLOW}[!] Nothing to export β€” session log is empty.{RESET}") + return + + if export_dir is None: + export_dir = os.getcwd() + + ts_tag = datetime.now().strftime("%Y%m%d_%H%M%S") + base = os.path.join(export_dir, f"net_shell_report_{ts_tag}") + txt_path = base + ".txt" + json_path = base + ".json" + + # ── Plain-text report ───────────────────────────────── + with open(txt_path, "w", encoding="utf-8") as f: + f.write(f"NET-Shell Session Report\n") + f.write(f"Session Start : {_session_start}\n") + f.write(f"Export Time : {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n") + f.write(f"{'='*60}\n\n") + for i, entry in enumerate(_session_log, 1): + f.write(f"[{i}] {entry['tool']} β€” {entry['timestamp']}\n") + f.write(f"{'-'*50}\n") + f.write((entry["output"] or "").strip()) + f.write("\n\n") + + # ── JSON report ─────────────────────────────────────── + payload = { + "session_start" : _session_start, + "export_time" : datetime.now().strftime("%Y-%m-%d %H:%M:%S"), + "entries" : _session_log, + } + with open(json_path, "w", encoding="utf-8") as f: + json.dump(payload, f, indent=2) + + print(f"\n {GREEN}[+] TXT report saved β†’ {txt_path}{RESET}") + print(f" {GREEN}[+] JSON report saved β†’ {json_path}{RESET}") + + +def session_menu(): + """Interactive sub-menu for the session logger.""" + print(f"\n{CYAN}{'━'*45}{RESET}") + print(f"{CYAN} SESSION LOGGER & REPORT EXPORTER{RESET}") + print(f"{CYAN}{'━'*45}{RESET}") + print(f" {GREEN}[1]{RESET} View current session log") + print(f" {GREEN}[2]{RESET} Export report (.txt + .json)") + print(f" {RED}[3]{RESET} Clear session log") + print(f" {YELLOW}[B]{RESET} Back") + print(f"{CYAN}{'━'*45}{RESET}") + + try: + choice = input(f"\n{GREEN}logger{RESET}{DIM}@{RESET}{CYAN}net-shell{RESET}{DIM}:~${RESET} ").strip().lower() + except KeyboardInterrupt: + return + + if choice == '1': + view_session_log() + elif choice == '2': + path_input = input(f" Export directory [{os.getcwd()}]: ").strip() + export_report(path_input if path_input else None) + elif choice == '3': + _session_log.clear() + print(f" {YELLOW}[!] Session log cleared.{RESET}") diff --git a/modules/speedtest.py b/modules/speedtest.py new file mode 100644 index 0000000..e332b3b --- /dev/null +++ b/modules/speedtest.py @@ -0,0 +1,117 @@ +import urllib.request +import ssl +import time +import sys + +# Build an SSL context β€” use certifi bundle if available, else unverified +try: + import certifi + _SSL_CTX = ssl.create_default_context(cafile=certifi.where()) +except ImportError: + _SSL_CTX = ssl._create_unverified_context() + +GREEN = "\033[92m" +RED = "\033[91m" +YELLOW = "\033[93m" +CYAN = "\033[96m" +DIM = "\033[2m" +RESET = "\033[0m" + +# Reliable public test endpoints of known size (1 MB binary) +TEST_ENDPOINTS = [ + ("Cloudflare Workers (1MB)", "https://speed.cloudflare.com/__down?bytes=1048576"), + ("GitHub Assets CDN (1MB)", "https://raw.githubusercontent.com/librespeed/speedtest-go/master/LICENSE"), +] + +def _bar(ratio: float, width: int = 30) -> str: + filled = int(ratio * width) + color = GREEN if ratio > 0.6 else (YELLOW if ratio > 0.3 else RED) + return f"{color}{'β–ˆ' * filled}{'β–‘' * (width - filled)}{RESET}" + +def _download_test(url: str, label: str, timeout: int = 15) -> float | None: + """ + Downloads a URL and returns throughput in Mbps. + Streams the response to get real-time progress. + """ + print(f"\n{CYAN}[*] Testing via {label}...{RESET}") + try: + req = urllib.request.Request(url, headers={"User-Agent": "NET-Shell/1.0 SpeedTest"}) + start = time.perf_counter() + total_bytes = 0 + chunk_size = 8192 + + with urllib.request.urlopen(req, timeout=timeout, context=_SSL_CTX) as resp: + content_length = int(resp.headers.get("Content-Length", 0)) + while True: + chunk = resp.read(chunk_size) + if not chunk: + break + total_bytes += len(chunk) + elapsed = time.perf_counter() - start + mbps = (total_bytes * 8) / (elapsed * 1_000_000) if elapsed > 0 else 0 + ratio = (total_bytes / content_length) if content_length else 0 + + bar = _bar(min(ratio, 1.0)) + sys.stdout.write( + f"\r {bar} {total_bytes/1024:.1f} KB " + f"{GREEN}{mbps:.2f} Mbps{RESET} " + ) + sys.stdout.flush() + + elapsed = time.perf_counter() - start + mbps = (total_bytes * 8) / (elapsed * 1_000_000) + print(f"\n {GREEN}[+] Done: {total_bytes/1024:.1f} KB in {elapsed:.2f}s β†’ {mbps:.2f} Mbps{RESET}") + return mbps + + except KeyboardInterrupt: + print(f"\n {YELLOW}[!] Test interrupted.{RESET}") + return None + except Exception as e: + print(f"\n {RED}[!] Failed: {e}{RESET}") + return None + +def run_speed_test(): + """ + Runs a download speed test against multiple CDN endpoints + and prints a consolidated bandwidth report. + """ + print(f"\n{CYAN}{'━'*55}{RESET}") + print(f"{CYAN} BANDWIDTH SPEED ESTIMATOR{RESET}") + print(f"{CYAN}{'━'*55}{RESET}") + print(f"{DIM} Tests download throughput via public CDN endpoints.{RESET}") + + results = [] + for label, url in TEST_ENDPOINTS: + mbps = _download_test(url, label) + if mbps is not None: + results.append((label, mbps)) + + # ── Summary ──────────────────────────────────────────── + print(f"\n{CYAN}{'━'*55}{RESET}") + print(f" SPEED TEST SUMMARY") + print(f"{CYAN}{'━'*55}{RESET}") + + if not results: + print(f" {RED}All tests failed. Check your internet connection.{RESET}") + return + + speeds = [r[1] for r in results] + avg = sum(speeds) / len(speeds) + + for label, mbps in results: + bar = _bar(min(mbps / 100, 1.0), width=20) + print(f" {DIM}{label:<35}{RESET} {bar} {GREEN}{mbps:.2f} Mbps{RESET}") + + print(f"\n Average Download Speed : {CYAN}{avg:.2f} Mbps{RESET}") + + if avg >= 100: + tier = f"{GREEN}Excellent (100+ Mbps){RESET}" + elif avg >= 25: + tier = f"{GREEN}Good (25–100 Mbps){RESET}" + elif avg >= 5: + tier = f"{YELLOW}Fair (5–25 Mbps){RESET}" + else: + tier = f"{RED}Poor (< 5 Mbps){RESET}" + + print(f" Connection Tier : {tier}") + print(f"{CYAN}{'━'*55}{RESET}") diff --git a/modules/traceroute.py b/modules/traceroute.py new file mode 100644 index 0000000..3041a04 --- /dev/null +++ b/modules/traceroute.py @@ -0,0 +1,72 @@ +import socket +import time + +GREEN = "\033[92m" +RED = "\033[91m" +YELLOW = "\033[93m" +CYAN = "\033[96m" +DIM = "\033[2m" +RESET = "\033[0m" + +def traceroute(target, max_hops=30, timeout=2): + """ + Traces the network path to a target by sending UDP probes with + incrementing TTL values and listening for ICMP TTL-exceeded replies. + Works on macOS/Linux without root by using a raw ICMP recv socket. + """ + try: + dest_ip = socket.gethostbyname(target) + except socket.gaierror: + print(f"{RED}[!] Could not resolve host: {target}{RESET}") + return + + print(f"\n{CYAN}Traceroute to {target} ({dest_ip}), max {max_hops} hops:{RESET}") + print(f"{CYAN}{'━'*60}{RESET}") + print(f"{DIM}{'HOP':<5} {'RTT':>8} {'HOST'}{RESET}") + print(f"{CYAN}{'━'*60}{RESET}") + + port = 33434 # Standard traceroute destination port + + try: + for ttl in range(1, max_hops + 1): + recv_sock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP) + send_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) + + recv_sock.settimeout(timeout) + send_sock.setsockopt(socket.IPPROTO_IP, socket.IP_TTL, ttl) + + recv_sock.bind(("", port)) + + start = time.perf_counter() + send_sock.sendto(b"", (dest_ip, port)) + + curr_addr = None + curr_name = None + + try: + _, curr_addr = recv_sock.recvfrom(512) + curr_addr = curr_addr[0] + rtt_ms = (time.perf_counter() - start) * 1000 + + try: + curr_name = socket.gethostbyaddr(curr_addr)[0] + except socket.herror: + curr_name = curr_addr + + print(f"{GREEN}{ttl:<5}{RESET} {rtt_ms:>7.2f}ms {curr_name} ({curr_addr})") + + except socket.timeout: + print(f"{YELLOW}{ttl:<5}{RESET} {'*':>8} Request timed out") + + finally: + send_sock.close() + recv_sock.close() + + if curr_addr == dest_ip: + print(f"\n{GREEN}[+] Destination reached in {ttl} hops.{RESET}") + break + + except PermissionError: + print(f"{RED}[!] Permission denied: traceroute requires elevated privileges (sudo).{RESET}") + except KeyboardInterrupt: + print(f"\n{YELLOW}[!] Traceroute interrupted by operator.{RESET}") diff --git a/modules/waf_fingerprint.py b/modules/waf_fingerprint.py new file mode 100644 index 0000000..01736bf --- /dev/null +++ b/modules/waf_fingerprint.py @@ -0,0 +1,140 @@ +import urllib.request +import urllib.error +import ssl +import time +import re + +# SSL context β€” certifi if available, else unverified +try: + import certifi + _SSL_CTX = ssl.create_default_context(cafile=certifi.where()) +except ImportError: + _SSL_CTX = ssl._create_unverified_context() + +GREEN = "\033[92m" +RED = "\033[91m" +YELLOW = "\033[93m" +CYAN = "\033[96m" +DIM = "\033[2m" +BOLD = "\033[1m" +RESET = "\033[0m" + +# Known WAF signatures: (pattern_in_headers_or_body, waf_name) +WAF_SIGNATURES = [ + (r"cloudflare", "Cloudflare"), + (r"__cfduid|cf-ray", "Cloudflare"), + (r"x-sucuri-id", "Sucuri"), + (r"x-firewall-protection", "Generic Firewall"), + (r"x-waf-event-info", "Barracuda WAF"), + (r"x-amzn-requestid|x-amz-cf-id", "AWS CloudFront"), + (r"x-cdn|x-check-cacheable", "Varnish Cache"), + (r"akamai|akamaighost", "Akamai"), + (r"x-fw-hash", "Wordfence"), + (r"incap_ses|visid_incap", "Imperva Incapsula"), + (r"x-iinfo", "Imperva Incapsula"), + (r"mod_security|modsec", "ModSecurity"), + (r"fortigate|fortiweb", "Fortinet FortiGate"), + (r"f5-trafficshield|bigip","F5 BIG-IP"), + (r"ats/", "Apache Traffic Server"), + (r"naxsi", "NAXSI"), + (r"x-denyall", "DenyAll WAF"), +] + +PROBE_PAYLOADS = [ + ("Normal", "/"), + ("SQLi probe", "/?id=1'OR'1'='1"), + ("XSS probe", "/?q="), + ("Path traversal", "/../../../etc/passwd"), + ("Bad User-Agent", "/"), +] + +def _make_request(url: str, path: str, extra_headers: dict = None) -> tuple: + """Sends an HTTP request and returns (status_code, headers_dict, elapsed_ms).""" + full_url = url.rstrip("/") + path + req = urllib.request.Request(full_url) + req.add_header("User-Agent", "Mozilla/5.0 (X11; Linux x86_64) NET-Shell/1.0") + if extra_headers: + for k, v in extra_headers.items(): + req.add_header(k, v) + start = time.perf_counter() + try: + with urllib.request.urlopen(req, timeout=6, context=_SSL_CTX) as resp: + elapsed = (time.perf_counter() - start) * 1000 + return resp.status, dict(resp.headers), elapsed + except urllib.error.HTTPError as e: + elapsed = (time.perf_counter() - start) * 1000 + return e.code, dict(e.headers), elapsed + except Exception: + return None, {}, (time.perf_counter() - start) * 1000 + +def _detect_waf(headers: dict, body_hint: str = "") -> list: + """Matches response headers against known WAF signatures.""" + detected = [] + combined = " ".join(f"{k.lower()} {v.lower()}" for k, v in headers.items()) + combined += " " + body_hint.lower() + for pattern, name in WAF_SIGNATURES: + if re.search(pattern, combined) and name not in detected: + detected.append(name) + return detected + +def fingerprint(target_url: str): + """ + Sends a series of probe requests to detect WAF/firewall presence, + fingerprint server headers, and identify anomalous blocking behavior. + """ + if not target_url.startswith(("http://", "https://")): + target_url = "http://" + target_url + + print(f"\n{CYAN}{'━'*60}{RESET}") + print(f"{CYAN} WAF / FIREWALL FINGERPRINTER β€” {target_url}{RESET}") + print(f"{CYAN}{'━'*60}{RESET}") + + all_detected = set() + server_banner = None + + print(f"\n{YELLOW}[*] Sending probe requests...{RESET}\n") + print(f"{DIM}{'PROBE':<20} {'CODE':>5} {'RTT':>8} {'VERDICT'}{RESET}") + print(f"{DIM}{'─'*58}{RESET}") + + for label, path in PROBE_PAYLOADS: + extra = {"User-Agent": "() { :; }; echo Content-Type: text/html; echo; echo; /bin/bash -i"} \ + if label == "Bad User-Agent" else None + status, headers, rtt = _make_request(target_url, path, extra) + + # Grab server banner once + if server_banner is None: + server_banner = headers.get("Server", headers.get("server", "Unknown")) + + if status is None: + print(f" {label:<20} {'ERR':>5} {rtt:>7.0f}ms {RED}Connection failed{RESET}") + continue + + detected = _detect_waf(headers) + all_detected.update(detected) + + # Color code by status + if 200 <= status < 300: + sc = f"{GREEN}{status}{RESET}" + verdict = f"{GREEN}Passed through{RESET}" + elif status in (403, 406, 429, 503): + sc = f"{RED}{status}{RESET}" + verdict = f"{RED}Blocked / Rate-limited{RESET}" + if detected: + verdict += f" β€” {YELLOW}{', '.join(detected)}{RESET}" + else: + sc = f"{YELLOW}{status}{RESET}" + verdict = f"{YELLOW}Unusual response{RESET}" + + print(f" {label:<20} {sc:>5} {rtt:>7.0f}ms {verdict}") + + # ── Summary ─────────────────────────────────────────────── + print(f"\n{CYAN}{'━'*60}{RESET}") + print(f"{BOLD} FINGERPRINT REPORT{RESET}") + print(f"{CYAN}{'━'*60}{RESET}") + print(f" {DIM}Server Banner :{RESET} {GREEN}{server_banner}{RESET}") + + if all_detected: + print(f" {DIM}Detected WAF/CDN:{RESET} {RED}{', '.join(all_detected)}{RESET}") + else: + print(f" {DIM}Detected WAF/CDN:{RESET} {GREEN}None detected (may be custom or absent){RESET}") + print(f"{CYAN}{'━'*60}{RESET}") diff --git a/modules/whois_lookup.py b/modules/whois_lookup.py new file mode 100644 index 0000000..5a1940c --- /dev/null +++ b/modules/whois_lookup.py @@ -0,0 +1,103 @@ +import socket +import urllib.request +import ssl +import json + +# SSL context β€” certifi if available, else unverified (common macOS issue) +try: + import certifi + _SSL_CTX = ssl.create_default_context(cafile=certifi.where()) +except ImportError: + _SSL_CTX = ssl._create_unverified_context() + +try: + import whois as python_whois + WHOIS_AVAILABLE = True +except ImportError: + WHOIS_AVAILABLE = False + +GREEN = "\033[92m" +RED = "\033[91m" +YELLOW = "\033[93m" +CYAN = "\033[96m" +DIM = "\033[2m" +RESET = "\033[0m" + +def _resolve_to_ip(target: str) -> str: + """Resolve a domain or raw IP to its IP address string.""" + try: + return socket.gethostbyname(target) + except socket.gaierror: + return None + +def geo_lookup(ip: str): + """ + Fetches geolocation data for an IP using the free ip-api.com JSON endpoint. + No API key required, rate-limited to 45 req/min on free tier. + """ + try: + url = f"http://ip-api.com/json/{ip}?fields=status,message,country,regionName,city,isp,org,as,query" + req = urllib.request.Request(url, headers={"User-Agent": "NET-Shell/1.0"}) + with urllib.request.urlopen(req, timeout=5) as resp: + data = json.loads(resp.read().decode()) + return data + except Exception as e: + return {"status": "fail", "message": str(e)} + +def whois_lookup(target: str): + """ + Performs a WHOIS lookup on a domain and an IP geolocation lookup. + Prints all results in a formatted operator-style table. + """ + print(f"\n{CYAN}{'━'*55}{RESET}") + print(f"{CYAN} WHOIS & GEOLOCATION INTEL β€” {target}{RESET}") + print(f"{CYAN}{'━'*55}{RESET}") + + # ── IP Geolocation ──────────────────────────────────────── + ip = _resolve_to_ip(target) + if ip: + print(f"\n{YELLOW}[GEO] IP Geolocation β†’ {ip}{RESET}") + geo = geo_lookup(ip) + if geo.get("status") == "success": + fields = { + "IP Address" : geo.get("query", "N/A"), + "Country" : geo.get("country", "N/A"), + "Region" : geo.get("regionName", "N/A"), + "City" : geo.get("city", "N/A"), + "ISP" : geo.get("isp", "N/A"), + "Org" : geo.get("org", "N/A"), + "AS" : geo.get("as", "N/A"), + } + for k, v in fields.items(): + print(f" {DIM}{k:<14}{RESET}: {GREEN}{v}{RESET}") + else: + print(f" {RED}[!] Geolocation failed: {geo.get('message', 'Unknown error')}{RESET}") + else: + print(f"{RED}[!] Could not resolve host: {target}{RESET}") + + # ── WHOIS ───────────────────────────────────────────────── + print(f"\n{YELLOW}[WHOIS] Domain Registration Data{RESET}") + if not WHOIS_AVAILABLE: + print(f" {RED}[!] python-whois not installed. Run: pip install python-whois{RESET}") + return + + try: + w = python_whois.whois(target) + fields = { + "Registrar" : w.registrar, + "Created" : w.creation_date, + "Expires" : w.expiration_date, + "Updated" : w.updated_date, + "Name Servers": w.name_servers, + "Status" : w.status, + "Emails" : w.emails, + } + for k, v in fields.items(): + if v is None: + continue + # Handle lists (e.g. multiple name servers) + if isinstance(v, list): + v = ", ".join(str(x) for x in v[:3]) + print(f" {DIM}{k:<14}{RESET}: {GREEN}{v}{RESET}") + except Exception as e: + print(f" {RED}[!] WHOIS query failed: {e}{RESET}") diff --git a/requirements.txt b/requirements.txt index ceb66a1..dc8ac68 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ aiohttp -psutil \ No newline at end of file +psutil +python-whois \ No newline at end of file From 5cfcddb117e0255ff947b4cceb75a4bef7b0b41f Mon Sep 17 00:00:00 2001 From: abhinavmehra2004 Date: Tue, 30 Jun 2026 17:08:30 +0530 Subject: [PATCH 2/2] docs: update README with 7 new modules from v2.0.0 contribution --- readme.md | 56 ++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 43 insertions(+), 13 deletions(-) diff --git a/readme.md b/readme.md index ec0f746..8a4c8ad 100644 --- a/readme.md +++ b/readme.md @@ -1,11 +1,11 @@ # ⚑ NET-Shell: Advanced Network Diagnostic Suite -[![Version](https://img.shields.io/badge/Version-1.0.0-cyan.svg?style=for-the-badge&logo=gitbook)](https://github.com/) +[![Version](https://img.shields.io/badge/Version-2.0.0-cyan.svg?style=for-the-badge&logo=gitbook)](https://github.com/) [![Python](https://img.shields.io/badge/Python-3.8+-yellow.svg?style=for-the-badge&logo=python)](https://www.python.org/) [![License](https://img.shields.io/badge/License-MIT-green.svg?style=for-the-badge)](https://opensource.org/licenses/MIT) [![Status](https://img.shields.io/badge/Status-Operational-brightgreen.svg?style=for-the-badge&logo=checkmarx)](https://github.com/) -> **Operator-Grade Asynchronous Toolkit** for high-performance network analysis, DNS resolution, and infrastructure stress-testing. +> **Operator-Grade Asynchronous Toolkit** for high-performance network analysis, DNS resolution, reconnaissance, and infrastructure stress-testing. --- @@ -17,6 +17,13 @@ | **Host Resolver** | Advanced URL parsing and DNS-to-IP translation. | `socket` | | **Port Scanner** | Multi-threaded TCP handshake verification. | `threading` | | **System Intel** | Deep-dive hardware and OS telemetry. | `psutil` | +| **Traceroute** | ICMP/UDP hop tracing with per-hop RTT and hostname resolution. | `socket` | +| **WHOIS & Geolocation** | Domain/IP registration data and geographic intelligence. | `python-whois`, `ip-api` | +| **Ping Monitor** | Continuous TCP latency probing with live jitter and loss stats. | `socket` | +| **WAF Fingerprinter** | Crafted HTTP probe battery against 15 known WAF/CDN signatures. | `urllib` | +| **Speed Estimator** | Real-world download throughput benchmarking from public CDNs. | `urllib`, `ssl` | +| **Network Interface Monitor** | Live per-interface traffic dashboard with rate and error tracking. | `psutil` | +| **Session Logger** | Transparent stdout capture with TXT/JSON report export. | `io`, `json` | | **Shell UI** | Dynamic ANSI-styled operator interface. | `colorama` | --- @@ -27,7 +34,6 @@ ```bash git clone https://github.com/SoftBridge-Labs/NET-Shell.git cd NET-Shell - ``` ### 2. Auto-Configuration @@ -36,14 +42,12 @@ The toolkit features a self-healing setup script that manages virtual environmen ```bash python setup.py - ``` ### 3. Execution ```bash python main.py - ``` --- @@ -55,6 +59,8 @@ The toolkit operates on a modular architecture, ensuring that network operations 1. **The Request Cycle**: When a "Flood" is initiated, the `asyncio` loop spawns multiple non-blocking tasks. 2. **Real-Time Hook**: As each worker returns a status code, the UI is updated immediately without waiting for the entire batch to finish. 3. **Telemetry**: Latency is calculated per-request to identify "Spikes" and server-side throttling. +4. **Reconnaissance Pipeline**: Traceroute, WHOIS, and WAF modules chain together for full target profiling. +5. **Session Capture**: The Session Logger transparently intercepts stdout for WAF, Speed, and Interface Monitor tools β€” no manual wiring required. --- @@ -63,19 +69,43 @@ The toolkit operates on a modular architecture, ensuring that network operations ```bash πŸ“¦ NET-Shell ┣ πŸ“‚ modules - ┃ ┣ πŸ“œ flood.py # Async Engine - ┃ ┣ πŸ“œ host.py # DNS Logic - ┃ ┣ πŸ“œ scanner.py # TCP Probe - ┃ β”— πŸ“œ menu.py # Shell Core + ┃ ┣ πŸ“œ flood.py # Async HTTP Engine + ┃ ┣ πŸ“œ host.py # DNS Resolution + ┃ ┣ πŸ“œ scanner.py # TCP Port Probe + ┃ ┣ πŸ“œ system.py # System Telemetry + ┃ ┣ πŸ“œ traceroute.py # Hop Path Analysis + ┃ ┣ πŸ“œ whois_lookup.py # WHOIS & Geolocation + ┃ ┣ πŸ“œ ping_monitor.py # Latency Monitor + ┃ ┣ πŸ“œ waf_fingerprint.py # WAF/CDN Detection + ┃ ┣ πŸ“œ speedtest.py # Bandwidth Estimator + ┃ ┣ πŸ“œ net_monitor.py # Interface Dashboard + ┃ ┣ πŸ“œ session_logger.py # Report Exporter + ┃ ┣ πŸ“œ menu.py # Shell Core + ┃ β”— πŸ“œ utils.py # Shared Utilities + ┣ πŸ“‚ ui + ┃ β”— πŸ“œ name.py # Banner / Branding ┣ πŸ“‚ maintain - ┃ β”— πŸ“œ cleaner.py # Cache Purge - ┣ πŸ“œ main.py # Kernel Entry - β”— πŸ“œ setup.py # Dependency Mgmt - + ┃ β”— πŸ“œ cleaner.py # Cache Purge + ┣ πŸ“œ main.py # Kernel Entry + ┣ πŸ“œ setup.py # Dependency Mgmt + ┣ πŸ“œ update.py # Self-Update Script + β”— πŸ“œ requirements.txt # pip Dependencies ``` --- +## πŸ“¦ Dependencies + +| Package | Purpose | +| :--- | :--- | +| `aiohttp` | Asynchronous HTTP flood engine | +| `psutil` | System telemetry and network interface stats | +| `python-whois` | WHOIS domain registration lookups | + +All other modules use the Python standard library (`socket`, `threading`, `asyncio`, `urllib`, `ssl`, `json`). + +--- + ## βš–οΈ Operational Security (OPSEC) **Disclaimer:** This software is intended for **White-Hat testing and educational research only.** Use of this tool for attacking targets without prior authorization is strictly prohibited. The author assumes no liability for misuse.