forensic-cli 1.2.13__tar.gz → 1.4.0__tar.gz

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.
Files changed (53) hide show
  1. {forensic_cli-1.2.13/src/forensic_cli.egg-info → forensic_cli-1.4.0}/PKG-INFO +1 -1
  2. {forensic_cli-1.2.13 → forensic_cli-1.4.0}/pyproject.toml +1 -1
  3. {forensic_cli-1.2.13 → forensic_cli-1.4.0}/setup.py +1 -1
  4. {forensic_cli-1.2.13 → forensic_cli-1.4.0}/src/cli/commands/network.py +70 -9
  5. {forensic_cli-1.2.13 → forensic_cli-1.4.0}/src/core/browser/common_words.py +0 -1
  6. forensic_cli-1.4.0/src/core/network/network_map.py +126 -0
  7. forensic_cli-1.4.0/src/core/network/port_scanner.py +138 -0
  8. {forensic_cli-1.2.13 → forensic_cli-1.4.0/src/forensic_cli.egg-info}/PKG-INFO +1 -1
  9. forensic_cli-1.2.13/src/core/network/network_map.py +0 -120
  10. forensic_cli-1.2.13/src/core/network/port_scanner.py +0 -93
  11. {forensic_cli-1.2.13 → forensic_cli-1.4.0}/LICENSE +0 -0
  12. {forensic_cli-1.2.13 → forensic_cli-1.4.0}/LICENSE-GPL +0 -0
  13. {forensic_cli-1.2.13 → forensic_cli-1.4.0}/README.md +0 -0
  14. {forensic_cli-1.2.13 → forensic_cli-1.4.0}/setup.cfg +0 -0
  15. {forensic_cli-1.2.13 → forensic_cli-1.4.0}/src/cli/__init__.py +0 -0
  16. {forensic_cli-1.2.13 → forensic_cli-1.4.0}/src/cli/commands/__init__.py +0 -0
  17. {forensic_cli-1.2.13 → forensic_cli-1.4.0}/src/cli/commands/browser.py +0 -0
  18. {forensic_cli-1.2.13 → forensic_cli-1.4.0}/src/cli/commands/describe.py +0 -0
  19. {forensic_cli-1.2.13 → forensic_cli-1.4.0}/src/cli/commands/email.py +0 -0
  20. {forensic_cli-1.2.13 → forensic_cli-1.4.0}/src/cli/commands/utils.py +0 -0
  21. {forensic_cli-1.2.13 → forensic_cli-1.4.0}/src/cli/main.py +0 -0
  22. {forensic_cli-1.2.13 → forensic_cli-1.4.0}/src/core/__init__.py +0 -0
  23. {forensic_cli-1.2.13 → forensic_cli-1.4.0}/src/core/browser/browser_history.py +0 -0
  24. {forensic_cli-1.2.13 → forensic_cli-1.4.0}/src/core/browser/downloads_history.py +0 -0
  25. {forensic_cli-1.2.13 → forensic_cli-1.4.0}/src/core/browser/fav_screen.py +0 -0
  26. {forensic_cli-1.2.13 → forensic_cli-1.4.0}/src/core/browser/logins.py +0 -0
  27. {forensic_cli-1.2.13 → forensic_cli-1.4.0}/src/core/browser/unusual_patterns.py +0 -0
  28. {forensic_cli-1.2.13 → forensic_cli-1.4.0}/src/core/data/__init__.py +0 -0
  29. {forensic_cli-1.2.13 → forensic_cli-1.4.0}/src/core/data/data_recovery.py +0 -0
  30. {forensic_cli-1.2.13 → forensic_cli-1.4.0}/src/core/db/db.py +0 -0
  31. {forensic_cli-1.2.13 → forensic_cli-1.4.0}/src/core/email/email_parser.py +0 -0
  32. {forensic_cli-1.2.13 → forensic_cli-1.4.0}/src/core/email/header_analysis.py +0 -0
  33. {forensic_cli-1.2.13 → forensic_cli-1.4.0}/src/core/models/orm.py +0 -0
  34. {forensic_cli-1.2.13 → forensic_cli-1.4.0}/src/core/models/schemas.py +0 -0
  35. {forensic_cli-1.2.13 → forensic_cli-1.4.0}/src/core/network/__init__.py +0 -0
  36. {forensic_cli-1.2.13 → forensic_cli-1.4.0}/src/core/network/arp_scan.py +0 -0
  37. {forensic_cli-1.2.13 → forensic_cli-1.4.0}/src/core/network/dns_recon.py +0 -0
  38. {forensic_cli-1.2.13 → forensic_cli-1.4.0}/src/core/network/fingerprinting.py +0 -0
  39. {forensic_cli-1.2.13 → forensic_cli-1.4.0}/src/core/network/ip_info.py +0 -0
  40. {forensic_cli-1.2.13 → forensic_cli-1.4.0}/src/core/network/ping_sweep.py +0 -0
  41. {forensic_cli-1.2.13 → forensic_cli-1.4.0}/src/core/network/smb_scan.py +0 -0
  42. {forensic_cli-1.2.13 → forensic_cli-1.4.0}/src/core/network/snmp_scan.py +0 -0
  43. {forensic_cli-1.2.13 → forensic_cli-1.4.0}/src/core/network/traceroute.py +0 -0
  44. {forensic_cli-1.2.13 → forensic_cli-1.4.0}/src/core/network/utils.py +0 -0
  45. {forensic_cli-1.2.13 → forensic_cli-1.4.0}/src/core/registry.py +0 -0
  46. {forensic_cli-1.2.13 → forensic_cli-1.4.0}/src/core/utils/__init__.py +0 -0
  47. {forensic_cli-1.2.13 → forensic_cli-1.4.0}/src/core/utils/gerar_amostras.py +0 -0
  48. {forensic_cli-1.2.13 → forensic_cli-1.4.0}/src/forensic_cli.egg-info/SOURCES.txt +0 -0
  49. {forensic_cli-1.2.13 → forensic_cli-1.4.0}/src/forensic_cli.egg-info/dependency_links.txt +0 -0
  50. {forensic_cli-1.2.13 → forensic_cli-1.4.0}/src/forensic_cli.egg-info/entry_points.txt +0 -0
  51. {forensic_cli-1.2.13 → forensic_cli-1.4.0}/src/forensic_cli.egg-info/requires.txt +0 -0
  52. {forensic_cli-1.2.13 → forensic_cli-1.4.0}/src/forensic_cli.egg-info/top_level.txt +0 -0
  53. {forensic_cli-1.2.13 → forensic_cli-1.4.0}/tests/test_file_inspector.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: forensic-cli
3
- Version: 1.2.13
3
+ Version: 1.4.0
4
4
  Summary: DevKit Forense - CLI para análise de navegadores e rede
5
5
  Home-page: https://github.com/ErickG123/devkit_forense
6
6
  Author: Erick Gabriel dos Santos Alves
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "forensic-cli"
7
- version = "1.2.13"
7
+ version = "1.4.0"
8
8
  description = "DevKit Forense - CLI para análise de navegadores e rede"
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name="forensic_cli",
5
- version="1.2.13",
5
+ version="1.4.0",
6
6
  author="Erick Gabriel dos Santos Alves",
7
7
  author_email="erickgabrielalves0@gmail.com",
8
8
  description="Toolkit modular para análise de evidências digitais",
@@ -1,9 +1,10 @@
1
1
  import typer
2
2
  from rich import print
3
3
  from rich.text import Text
4
+ from rich.table import Table
4
5
 
5
6
  from core.network.network_map import run as run_network_map
6
- from core.network.port_scanner import scan_host
7
+ from core.network.port_scanner import scan_host, parse_ports
7
8
  from core.network.ping_sweep import parse_network, ping_host
8
9
  from core.network.fingerprinting import detect_os
9
10
  from core.network.traceroute import traceroute_host
@@ -15,20 +16,80 @@ from core.network.snmp_scan import snmp_scan
15
16
 
16
17
  network_app = typer.Typer(help="Conjunto de ferramentas para análise e exploração de redes")
17
18
 
18
- @network_app.command("map", help="Mapeia dispositivos ativos na rede e salva os resultados em JSON/CSV")
19
+ @network_app.command("map", help="Mapeia dispositivos ativos na rede e salva os resultados")
19
20
  def map(
20
- network: str = typer.Option(..., help="Range de IPs da rede. Exemplo: 192.168.1.1-254"),
21
- output_dir: str = typer.Option("./output", help="Diretório para salvar os resultados"),
21
+ network: str = typer.Option(..., "--network", "-n", help="Range de IPs da rede. Ex: 192.168.1.1-254"),
22
+
23
+ ports: str = typer.Option(
24
+ "21,22,80,443,445,8080",
25
+ "--ports",
26
+ "-p",
27
+ help="Portas para escanear em cada host."
28
+ ),
29
+
30
+ output_dir: str = typer.Option(
31
+ "./output",
32
+ "--output",
33
+ "-o",
34
+ help="Diretório para salvar os resultados"
35
+ ),
22
36
  ):
23
- result = run_network_map(network, output_dir)
24
- typer.echo(f"Resultados salvos em: {result['json_file']} e {result['csv_file']}")
37
+ typer.echo(f"[+] Iniciando mapeamento da rede {network} nas portas [{ports}]...")
38
+
39
+ result = run_network_map(network, ports, output_dir)
40
+
41
+ if result and result.get('json_file'):
42
+ typer.echo(f"Resultados salvos em: {result.get('json_file')} e {result.get('csv_file')}")
25
43
 
26
44
  @network_app.command("scan", help="Realiza um scan de portas em um host específico")
27
45
  def scan(
28
- ip: str = typer.Option(..., help="Endereço IP do host. Exemplo: 192.168.0.10")
46
+ target: str = typer.Option(..., "--target", "-t", help="Alvo do scan (IP ou hostname)"),
47
+ ports: str = typer.Option("21,22,53,80,443,445,3306,8080", "--ports", "-p", help="Portas para escanear. Ex: '22,80,100-200'")
29
48
  ):
30
- result = scan_host(ip)
31
- print(result)
49
+ try:
50
+ ports_to_scan = parse_ports(ports)
51
+ num_ports = len(ports_to_scan)
52
+
53
+ if num_ports == 0:
54
+ print("[yellow]Nenhuma porta especificada para o scan.[/yellow]")
55
+ raise typer.Exit()
56
+
57
+ typer.echo(f"[+] Iniciando scan em {num_ports} porta(s) de {target}...")
58
+
59
+ results = []
60
+ with typer.progressbar(length=num_ports, label="Scanning ports") as progress:
61
+ def update_progress():
62
+ progress.update(1)
63
+
64
+ results = scan_host(
65
+ target=target,
66
+ ports=ports,
67
+ callback=update_progress
68
+ )
69
+
70
+ table = Table(title=f"Resultados do Scan para {target}")
71
+ table.add_column("Porta", style="cyan")
72
+ table.add_column("Protocolo", style="magenta")
73
+ table.add_column("Status", style="green")
74
+ table.add_column("Serviço", style="yellow")
75
+ table.add_column("Banner / Detalhes", style="white", overflow="fold")
76
+
77
+ if not results:
78
+ print(f"[yellow]Nenhuma porta aberta encontrada para '{target}' no range '{ports}'.[/yellow]")
79
+ return
80
+
81
+ for res in results:
82
+ table.add_row(
83
+ str(res["port"]),
84
+ res["protocol"],
85
+ res["status"],
86
+ res["service"],
87
+ res["banner"]
88
+ )
89
+ print(table)
90
+
91
+ except ValueError as e:
92
+ print(f"[bold red]Erro: {e}[/bold red]")
32
93
 
33
94
  @network_app.command("sweep", help="Verifica hosts ativos em um range de IPs via ping")
34
95
  def sweep(
@@ -1,4 +1,3 @@
1
- # core/browser/words_extractor.py
2
1
  import sqlite3
3
2
  import shutil
4
3
  import tempfile
@@ -0,0 +1,126 @@
1
+ import os
2
+ import json
3
+ import csv
4
+ from datetime import datetime
5
+ from concurrent.futures import ThreadPoolExecutor, as_completed
6
+ import typer
7
+
8
+ from .ping_sweep import ping_sweep
9
+ from .arp_scan import arp_scan
10
+ from .port_scanner import scan_host
11
+ from .utils import get_mac, get_vendor, get_hostname
12
+ from .fingerprinting import detect_os
13
+ from .traceroute import traceroute_host
14
+ from .snmp_scan import snmp_scan
15
+ from .dns_recon import dns_recon
16
+ from .smb_scan import smb_scan
17
+ from .ip_info import ip_info_lookup
18
+
19
+ def _process_host(host: str, ports_to_scan: str) -> dict:
20
+ open_ports = scan_host(host, ports=ports_to_scan)
21
+
22
+ try:
23
+ mac = get_mac(host)
24
+ vendor = get_vendor(mac) if mac else None
25
+ except Exception:
26
+ mac = None
27
+ vendor = None
28
+
29
+ hostname = get_hostname(host)
30
+
31
+ os_info = detect_os(host, ports=open_ports, mac_vendor=vendor)
32
+ hops = traceroute_host(host)
33
+ snmp_info = snmp_scan(host)
34
+ dns_info = dns_recon([host])
35
+ smb_info = smb_scan([host])
36
+ ip_info = ip_info_lookup(host)
37
+
38
+ return {
39
+ "host": host,
40
+ "hostname": hostname,
41
+ "mac": mac,
42
+ "vendor": vendor,
43
+ "open_ports": open_ports,
44
+ "os_info": os_info,
45
+ "traceroute": hops,
46
+ "snmp": snmp_info,
47
+ "dns": dns_info[0] if dns_info else {},
48
+ "smb": smb_info[0] if smb_info else {},
49
+ "ip_info": ip_info
50
+ }
51
+
52
+ def build_network_map(network: str, ports: str, workers: int = 50):
53
+ hosts = ping_sweep(network)
54
+ if not hosts:
55
+ typer.echo("[+] Ping sweep não encontrou hosts, tentando ARP scan...")
56
+ hosts = arp_scan(network)
57
+
58
+ if not hosts:
59
+ typer.echo("[!] Nenhum host ativo encontrado na rede especificada.")
60
+ return []
61
+
62
+ results = []
63
+ typer.echo(f"[+] {len(hosts)} host(s) ativo(s) encontrado(s). Iniciando análise detalhada...")
64
+
65
+ with ThreadPoolExecutor(max_workers=workers) as executor:
66
+ future_to_host = {executor.submit(_process_host, host, ports): host for host in hosts}
67
+
68
+ with typer.progressbar(as_completed(future_to_host), length=len(hosts), label="Mapeando Rede") as progress:
69
+ for future in progress:
70
+ try:
71
+ host_data = future.result()
72
+ results.append(host_data)
73
+ except Exception as exc:
74
+ host = future_to_host[future]
75
+ typer.echo(f"\n[!] Erro ao processar o host {host}: {exc}")
76
+
77
+ return results
78
+
79
+ def save_network_map(results: list, output_dir: str, prefix="network_map"):
80
+ os.makedirs(output_dir, exist_ok=True)
81
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
82
+
83
+ json_file = os.path.join(output_dir, f"{prefix}_{timestamp}.json")
84
+ csv_file = os.path.join(output_dir, f"{prefix}_{timestamp}.csv")
85
+
86
+ with open(json_file, "w", encoding="utf-8") as f:
87
+ json.dump(results, f, indent=4, ensure_ascii=False)
88
+
89
+ flat_results = []
90
+ for row in results:
91
+ flat_row = {
92
+ "host": row.get("host"),
93
+ "hostname": row.get("hostname"),
94
+ "mac": row.get("mac"),
95
+ "vendor": row.get("vendor"),
96
+ "os_type": row.get("os_info", {}).get("os"),
97
+ "host_type": row.get("os_info", {}).get("host_type"),
98
+ "open_ports_count": len(row.get("open_ports", [])),
99
+ "traceroute_hops": len(row.get("traceroute", [])),
100
+ "snmp_sys_name": row.get("snmp", {}).get("sys_name"),
101
+ "dns_hostname": row.get("dns", {}).get("hostname"),
102
+ "smb_shares_count": len(row.get("smb", {}).get("shares", [])),
103
+ "asn": row.get("ip_info", {}).get("asn"),
104
+ "network_name": row.get("ip_info", {}).get("network_name"),
105
+ }
106
+ flat_results.append(flat_row)
107
+
108
+ if not flat_results:
109
+ return json_file, None
110
+
111
+ with open(csv_file, "w", newline="", encoding="utf-8") as f:
112
+ fieldnames = flat_results[0].keys()
113
+ writer = csv.DictWriter(f, fieldnames=fieldnames)
114
+ writer.writeheader()
115
+ writer.writerows(flat_results)
116
+
117
+ return json_file, csv_file
118
+
119
+ def run(network: str, ports: str, output_dir: str, prefix="network_map"):
120
+ results = build_network_map(network, ports)
121
+
122
+ if results:
123
+ json_file, csv_file = save_network_map(results, output_dir, prefix)
124
+ return {"results": results, "json_file": json_file, "csv_file": csv_file}
125
+ else:
126
+ return {}
@@ -0,0 +1,138 @@
1
+ import socket
2
+ import ssl
3
+ from concurrent.futures import ThreadPoolExecutor, as_completed
4
+ from typing import Callable, Dict, List, Optional, Set
5
+
6
+ TCP_PROBES: Dict[int, bytes] = {
7
+ 80: b"HEAD / HTTP/1.1\r\nHost: target\r\n\r\n",
8
+ 21: b"FEAT\r\n",
9
+ }
10
+
11
+ PORT_ALERTS: Dict[int, str] = {
12
+ 21: "FTP", 22: "SSH", 23: "Telnet", 25: "SMTP", 53: "DNS",
13
+ 80: "HTTP", 110: "POP3", 137: "NetBIOS-NS", 139: "NetBIOS", 143: "IMAP",
14
+ 443: "HTTPS", 445: "SMB", 993: "IMAPS", 995: "POP3S",
15
+ 1900: "UPnP", 3306: "MySQL", 3389: "RDP", 5432: "PostgreSQL",
16
+ 5900: "VNC", 8080: "HTTP-Proxy",
17
+ }
18
+
19
+ def _clean_banner(banner_bytes: bytes) -> str:
20
+ try:
21
+ banner_str = banner_bytes.decode(errors='ignore')
22
+ printable_banner = "".join(char for char in banner_str if char.isprintable() or char.isspace())
23
+ return printable_banner.strip()
24
+ except Exception:
25
+ return "Falha ao decodificar banner"
26
+
27
+ def parse_ports(port_str: str) -> List[int]:
28
+ ports: Set[int] = set()
29
+ try:
30
+ parts = port_str.split(',')
31
+ for part in parts:
32
+ if '-' in part:
33
+ start, end = map(int, part.split('-'))
34
+ ports.update(range(start, end + 1))
35
+ else:
36
+ ports.add(int(part))
37
+ except ValueError:
38
+ raise ValueError("Formato de porta inválido. Use '80', '22,80,443' ou '1-1024'.")
39
+
40
+ return sorted(list(ports))
41
+
42
+ def _scan_tcp(ip: str, port: int, timeout: float) -> Optional[Dict]:
43
+ try:
44
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
45
+ s.settimeout(timeout)
46
+ if s.connect_ex((ip, port)) == 0:
47
+ banner = ""
48
+ if port == 443:
49
+ try:
50
+ context = ssl.create_default_context()
51
+ with context.wrap_socket(s, server_hostname=ip) as ss:
52
+ cert = ss.getpeercert()
53
+ subject = dict(x[0] for x in cert.get('subject', []))
54
+ banner = f"SSL Cert for: {subject.get('commonName', 'N/A')}"
55
+ except Exception:
56
+ banner = "SSL Handshake Error"
57
+ else:
58
+ try:
59
+ if port in TCP_PROBES:
60
+ s.send(TCP_PROBES[port])
61
+ raw_banner = s.recv(1024)
62
+ cleaned_banner = _clean_banner(raw_banner)
63
+ if port in [80, 8080] and cleaned_banner:
64
+ banner = cleaned_banner.splitlines()[0]
65
+ else:
66
+ banner = cleaned_banner
67
+ except (socket.timeout, ConnectionResetError):
68
+ pass
69
+
70
+ return {
71
+ "port": port, "protocol": "TCP", "status": "open",
72
+ "banner": banner or "sem banner",
73
+ "service": PORT_ALERTS.get(port, "unknown")
74
+ }
75
+ except (socket.timeout, OSError):
76
+ pass
77
+
78
+ return None
79
+
80
+ def _scan_udp(ip: str, port: int, timeout: float) -> Optional[Dict]:
81
+ try:
82
+ with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
83
+ s.settimeout(timeout)
84
+ s.sendto(b"", (ip, port))
85
+ try:
86
+ s.recvfrom(1024)
87
+ return {
88
+ "port": port, "protocol": "UDP", "status": "open",
89
+ "banner": "response received",
90
+ "service": PORT_ALERTS.get(port, "unknown")
91
+ }
92
+ except socket.timeout:
93
+ return {
94
+ "port": port, "protocol": "UDP", "status": "open|filtered",
95
+ "banner": "no response",
96
+ "service": PORT_ALERTS.get(port, "unknown")
97
+ }
98
+ except (OSError, socket.timeout):
99
+ pass
100
+
101
+ return None
102
+
103
+ def scan_host(
104
+ target: str,
105
+ ports: str,
106
+ udp: bool = False,
107
+ workers: int = 100,
108
+ timeout: float = 1.0,
109
+ callback: Optional[Callable] = None
110
+ ) -> List[Dict]:
111
+ try:
112
+ ip = socket.gethostbyname(target)
113
+ except socket.gaierror:
114
+ raise ValueError(f"Hostname '{target}' não pôde ser resolvido.")
115
+
116
+ tcp_ports_to_scan = parse_ports(ports)
117
+ udp_ports_to_scan = parse_ports("53,67,68,161,500") if udp else []
118
+
119
+ total_ports = len(tcp_ports_to_scan) + len(udp_ports_to_scan)
120
+ if total_ports == 0:
121
+ return []
122
+
123
+ results: List[Dict] = []
124
+ with ThreadPoolExecutor(max_workers=workers) as executor:
125
+ futures = [executor.submit(_scan_tcp, ip, port, timeout) for port in tcp_ports_to_scan]
126
+ futures += [executor.submit(_scan_udp, ip, port, timeout) for port in udp_ports_to_scan]
127
+
128
+ for future in as_completed(futures):
129
+ res = future.result()
130
+ if res:
131
+ results.append(res)
132
+
133
+ if callback:
134
+ callback()
135
+
136
+ results.sort(key=lambda x: x["port"])
137
+
138
+ return results
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: forensic-cli
3
- Version: 1.2.13
3
+ Version: 1.4.0
4
4
  Summary: DevKit Forense - CLI para análise de navegadores e rede
5
5
  Home-page: https://github.com/ErickG123/devkit_forense
6
6
  Author: Erick Gabriel dos Santos Alves
@@ -1,120 +0,0 @@
1
- import os
2
- import json
3
- import csv
4
- from datetime import datetime
5
- import threading
6
- import typer
7
-
8
- from core.network.ping_sweep import ping_sweep
9
- from core.network.arp_scan import arp_scan
10
- from core.network.port_scanner import scan_host
11
- from core.network.utils import get_mac, get_vendor, get_hostname
12
- from core.network.fingerprinting import detect_os
13
- from core.network.traceroute import traceroute_host
14
- from core.network.snmp_scan import snmp_scan
15
- from core.network.dns_recon import dns_recon
16
- from core.network.smb_scan import smb_scan
17
- from core.network.ip_info import ip_info_lookup
18
-
19
- lock = threading.Lock()
20
-
21
- def build_network_map(network):
22
- hosts = ping_sweep(network)
23
- if not hosts:
24
- hosts = arp_scan(network)
25
-
26
- results = []
27
-
28
- typer.echo(f"[+] Iniciando varredura da rede ({len(hosts)} hosts)...")
29
-
30
- with typer.progressbar(hosts, label="Network Map") as progress:
31
- for host in hosts:
32
- open_ports = scan_host(host)
33
-
34
- try:
35
- mac = get_mac(host)
36
- vendor = get_vendor(mac) if mac else None
37
- except Exception:
38
- mac = None
39
- vendor = None
40
-
41
- hostname = get_hostname(host)
42
- os_info = detect_os(host, ports=open_ports, mac_vendor=vendor)
43
-
44
- hops = traceroute_host(host)
45
-
46
- snmp_info = snmp_scan(host)
47
-
48
- dns_info = dns_recon([host])
49
-
50
- smb_info = smb_scan([host])
51
-
52
- ip_info = ip_info_lookup(host)
53
-
54
- host_data = {
55
- "host": host,
56
- "hostname": hostname,
57
- "mac": mac,
58
- "vendor": vendor,
59
- "open_ports": open_ports,
60
- "os_info": os_info,
61
- "traceroute": hops,
62
- "snmp": snmp_info,
63
- "dns": dns_info[0] if dns_info else {},
64
- "smb": smb_info[0] if smb_info else {},
65
- "ip_info": ip_info
66
- }
67
-
68
- with lock:
69
- results.append(host_data)
70
-
71
- progress.update(1)
72
-
73
- return results
74
-
75
- def save_network_map(results, output_dir: str, prefix="network_map"):
76
- os.makedirs(output_dir, exist_ok=True)
77
- timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
78
-
79
- json_file = os.path.join(output_dir, f"{prefix}_{timestamp}.json")
80
- csv_file = os.path.join(output_dir, f"{prefix}_{timestamp}.csv")
81
-
82
- with open(json_file, "w", encoding="utf-8") as f:
83
- json.dump(results, f, indent=4, ensure_ascii=False)
84
-
85
- with open(csv_file, "w", newline="", encoding="utf-8") as f:
86
- fieldnames = ["host", "hostname", "mac", "vendor", "os_type", "host_type",
87
- "open_ports_count", "traceroute_hops", "snmp_sys_name", "dns_hostname",
88
- "smb_shares_count", "vulnerabilities_count", "asn", "network_name"]
89
- writer = csv.DictWriter(f, fieldnames=fieldnames)
90
- writer.writeheader()
91
-
92
- for row in results:
93
- writer.writerow({
94
- "host": row["host"],
95
- "hostname": row["hostname"],
96
- "mac": row["mac"],
97
- "vendor": row["vendor"],
98
- "os_type": row["os_info"].get("os"),
99
- "host_type": row["os_info"].get("host_type"),
100
- "open_ports_count": len(row["open_ports"]),
101
- "traceroute_hops": len(row["traceroute"]),
102
- "snmp_sys_name": row["snmp"].get("sys_name"),
103
- "dns_hostname": row["dns"].get("hostname"),
104
- "smb_shares_count": len(row["smb"].get("shares", [])),
105
- "asn": row["ip_info"].get("asn"),
106
- "network_name": row["ip_info"].get("network_name"),
107
- })
108
-
109
- return json_file, csv_file
110
-
111
- def run(network: str, output_dir: str, prefix="network_map"):
112
- results = build_network_map(network)
113
- json_file, csv_file = save_network_map(results, output_dir, prefix)
114
-
115
- typer.echo(f"[+] Network map salvo em: {json_file} e {csv_file}")
116
- return {
117
- "results": results,
118
- "json_file": json_file,
119
- "csv_file": csv_file,
120
- }
@@ -1,93 +0,0 @@
1
- import socket
2
- from concurrent.futures import ThreadPoolExecutor, as_completed
3
- import threading
4
- import typer
5
-
6
- COMMON_TCP_PORTS = [
7
- 21, 22, 23, 25, 53, 80, 110, 139, 143, 443, 445,
8
- 3389, 3306, 8080, 8443, 5900, 135, 995, 993, 1723
9
- ]
10
- COMMON_UDP_PORTS = [53, 67, 68, 69, 123, 161, 162, 500, 514]
11
-
12
- PORT_ALERTS = {
13
- 21: "FTP",
14
- 22: "SSH",
15
- 23: "Telnet",
16
- 25: "SMTP",
17
- 53: "DNS",
18
- 80: "HTTP",
19
- 443: "HTTPS",
20
- 445: "SMB",
21
- 3306: "MySQL",
22
- 3389: "RDP"
23
- }
24
-
25
- lock = threading.Lock()
26
-
27
- def scan_tcp(ip, port):
28
- result = None
29
- try:
30
- with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
31
- s.settimeout(1)
32
- if s.connect_ex((ip, port)) == 0:
33
- banner = ""
34
- try:
35
- if port in [21, 22, 25, 80, 443, 3306]:
36
- s.send(b"HEAD / HTTP/1.0\r\n\r\n")
37
- banner = s.recv(1024).decode(errors="ignore").strip()
38
- except (socket.timeout, ConnectionResetError):
39
- pass
40
-
41
- result = {
42
- "port": port,
43
- "protocol": "TCP",
44
- "status": "open",
45
- "banner": banner if banner else "sem banner",
46
- "alert": PORT_ALERTS.get(port)
47
- }
48
- except (socket.timeout, OSError):
49
- pass
50
- return result
51
-
52
- def scan_udp(ip, port):
53
- result = None
54
- try:
55
- with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
56
- s.settimeout(1)
57
- s.sendto(b"", (ip, port))
58
- try:
59
- data, _ = s.recvfrom(1024)
60
- banner = data.decode(errors="ignore").strip()
61
- status = "open"
62
- except socket.timeout:
63
- banner = "sem resposta"
64
- status = "open|filtered"
65
- result = {
66
- "port": port,
67
- "protocol": "UDP",
68
- "status": status,
69
- "banner": banner,
70
- "alert": PORT_ALERTS.get(port)
71
- }
72
- except (OSError, socket.timeout):
73
- pass
74
- return result
75
-
76
- def scan_host(ip, tcp_ports=None, udp_ports=None):
77
- tcp_ports = tcp_ports or COMMON_TCP_PORTS
78
- udp_ports = udp_ports or COMMON_UDP_PORTS
79
- all_ports = tcp_ports + udp_ports
80
- results = []
81
-
82
- with typer.progressbar(all_ports, label=f"Scanning {ip}") as progress:
83
- with ThreadPoolExecutor(max_workers=100) as executor:
84
- futures = {executor.submit(scan_tcp if port in tcp_ports else scan_udp, ip, port): port for port in all_ports}
85
-
86
- for future in as_completed(futures):
87
- res = future.result()
88
- if res:
89
- with lock:
90
- results.append(res)
91
- progress.update(1)
92
-
93
- return results
File without changes
File without changes
File without changes
File without changes