diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c13f991 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 9783a3b..95d8953 100644 --- a/README.md +++ b/README.md @@ -47,8 +47,8 @@ pkg install nmap python libpcap -y #### 🐍 Instalação das Bibliotecas Python (Comum a todos) ```bash -# Instalar as bibliotecas Python -pip3 install -r requirements.txt +# Instalar as bibliotecas Python em um Ășnico comando +pip3 install scapy==2.5.0 python-nmap==0.7.1 whois==0.9.27 colorama==0.4.6 ``` #### 🐧 Debian/Ubuntu @@ -59,20 +59,34 @@ sudo apt update sudo apt install -y nmap python3-scapy ``` -O arquivo `requirements.txt` contĂ©m: +As dependĂȘncias Python sĂŁo: `scapy==2.5.0`, `python-nmap==0.7.1`, `whois==0.9.27`, `colorama==0.4.6`. -``` -scapy -python-nmap -requests -whois -colorama +### ✅ Instalação RĂĄpida (Passo a Passo) + +```bash +# 1) Instalar dependĂȘncias de sistema (exemplo Debian/Ubuntu) +sudo apt update +sudo apt install -y nmap python3-scapy + +# 2) Instalar dependĂȘncias Python (com versĂ”es fixas) +pip3 install scapy==2.5.0 python-nmap==0.7.1 whois==0.9.27 colorama==0.4.6 + +# 3) Executar a ferramenta +python3 main.py --help ``` ## ⚙ Como Usar O toolkit Ă© executado atravĂ©s do arquivo principal `main.py` com subcomandos. +### ✅ Modo Interativo (UI no Terminal) + +Se preferir uma experiĂȘncia guiada, use o modo interativo com menu no terminal: + +```bash +python3 main.py ui +``` + ### 1. AnĂĄlise de Rede (`net`) #### ARP Scan (Descoberta de Hosts) @@ -81,7 +95,7 @@ O toolkit Ă© executado atravĂ©s do arquivo principal `main.py` com subcomandos. ```bash sudo python3 main.py net arp # Exemplo: -sudo python3 main.py net arp 192.168.1.1/24 +sudo python3 main.py net arp 192.0.2.0/24 ``` #### Port Scan (Varredura de Portas) @@ -134,5 +148,26 @@ sudo python3 main.py sniff -t 30 * **PrivilĂ©gios de Root (sudo):** As funcionalidades de baixo nĂ­vel de rede, como **ARP Scan** e **Sniffer de Rede**, exigem privilĂ©gios de root (`sudo`) para acessar a interface de rede e enviar/receber pacotes brutos. * **Uso Ético:** Esta ferramenta Ă© destinada a fins educacionais e testes de penetração **autorizados**. O uso indevido contra sistemas sem permissĂŁo expressa Ă© ilegal e antiĂ©tico. O desenvolvedor nĂŁo se responsabiliza por qualquer uso indevido desta ferramenta. +* **Validação de entradas:** Os mĂłdulos de varredura validam alvo e faixa de portas para reduzir erros e evitar entradas invĂĄlidas. Garanta que os alvos estejam corretos antes de executar os scans. * **Interface:** A aplicação utiliza a biblioteca `colorama` para uma melhor experiĂȘncia visual no terminal. +## 🧰 Solução de Problemas (Troubleshooting) + +Se vocĂȘ encontrar falhas na execução, revise os pontos abaixo: + +* **Erro ao usar `scapy` ou `nmap`:** instale as dependĂȘncias de sistema (`nmap` e `python3-scapy` ou equivalentes) conforme as instruçÔes de Fedora/Ubuntu/Termux acima. +* **PermissionError em `net arp` ou `sniff`:** execute com `sudo`, pois essas operaçÔes precisam de acesso Ă  interface de rede. +* **Ambiente virtual (venv):** se estiver usando venv, pode ser necessĂĄrio executar o Python do venv com `sudo` para que o `scapy` tenha acesso Ă s interfaces de rede. + +## 🔒 ConsideraçÔes de Segurança e Engenharia + +Para transparĂȘncia e auditoria: + +* **Execução de comandos:** o toolkit nĂŁo utiliza `eval`, `exec` ou `os.system`, evitando superfĂ­cies clĂĄssicas de injeção de comandos. +* **Validação de entrada:** hĂĄ validaçÔes de alvo, faixa de portas, rede e caminhos de arquivo, reduzindo erros de execução e entradas malformadas. +* **ConcorrĂȘncia:** as varreduras atuais usam fluxo sequencial; para ambientes grandes, recomenda-se evoluir para `asyncio`/`threading` conforme o caso. +* **Logs e auditoria:** se vocĂȘ precisa de rastreabilidade, considere adicionar logging estruturado (ex.: mĂłdulo `logging`) com nĂ­veis apropriados e sem dados sensĂ­veis. + +## 📄 Licença + +Este projeto Ă© distribuĂ­do sob a licença MIT. Consulte o arquivo `LICENSE` para detalhes. diff --git a/info_analysis.py b/info_analysis.py index b283816..12e9c24 100644 --- a/info_analysis.py +++ b/info_analysis.py @@ -1,9 +1,26 @@ import hashlib import os +import socket +import re import whois -import requests from colorama import Fore, Style +DOMAIN_LABEL_PATTERN = re.compile(r"^[A-Za-z0-9-]{1,63}$") + +def _is_valid_domain(domain): + if not domain or any(char.isspace() for char in domain): + return False + if len(domain) > 253: + return False + parts = domain.rstrip(".").split(".") + return all( + part + and DOMAIN_LABEL_PATTERN.match(part) + and not part.startswith("-") + and not part.endswith("-") + for part in parts + ) + def calculate_file_hash(file_path, algorithm='sha256'): """ Calcula o hash de um arquivo usando o algoritmo especificado. @@ -13,6 +30,12 @@ def calculate_file_hash(file_path, algorithm='sha256'): if not os.path.exists(file_path): print(f"{Fore.RED}Erro: Arquivo nĂŁo encontrado em {file_path}{Style.RESET_ALL}") return None + if not os.path.isfile(file_path): + print(f"{Fore.RED}Erro: O caminho informado nĂŁo Ă© um arquivo vĂĄlido.{Style.RESET_ALL}") + return None + if os.path.islink(file_path): + print(f"{Fore.RED}Erro: Links simbĂłlicos nĂŁo sĂŁo permitidos para cĂĄlculo de hash.{Style.RESET_ALL}") + return None hash_func = hashlib.new(algorithm) with open(file_path, 'rb') as f: @@ -36,6 +59,9 @@ def whois_lookup(domain): """ Realiza uma consulta Whois para um domĂ­nio. """ + if not _is_valid_domain(domain): + print(f"{Fore.RED}Erro: DomĂ­nio invĂĄlido para consulta Whois.{Style.RESET_ALL}") + return None print(f"{Fore.CYAN}Realizando consulta Whois para {domain}...{Style.RESET_ALL}") try: w = whois.whois(domain) @@ -50,6 +76,9 @@ def dns_lookup(domain): """ Realiza uma consulta DNS bĂĄsica para obter o endereço IP. """ + if not _is_valid_domain(domain): + print(f"{Fore.RED}Erro: DomĂ­nio invĂĄlido para consulta DNS.{Style.RESET_ALL}") + return None print(f"{Fore.CYAN}Realizando consulta DNS para {domain}...{Style.RESET_ALL}") try: ip_address = socket.gethostbyname(domain) diff --git a/main.py b/main.py index bc1aae8..5a083d5 100644 --- a/main.py +++ b/main.py @@ -1,6 +1,7 @@ import argparse import sys import os +import whois from colorama import Fore, Style, init # Inicializa colorama para funcionar em diferentes terminais @@ -76,6 +77,68 @@ def display_results(title, data): print(f"{Fore.YELLOW}A operação nĂŁo retornou dados ou falhou.{Style.RESET_ALL}") +def _prompt_non_empty(prompt_text): + while True: + value = input(prompt_text).strip() + if value: + return value + print(f"{Fore.YELLOW}Entrada vazia. Tente novamente.{Style.RESET_ALL}") + + +def run_interactive_menu(): + """Executa um menu interativo para uso guiado do toolkit.""" + print(f"{Fore.GREEN}Modo interativo iniciado. Selecione uma opção:{Style.RESET_ALL}") + while True: + print( + f""" +{Fore.BLUE}1){Style.RESET_ALL} ARP Scan +{Fore.BLUE}2){Style.RESET_ALL} Port Scan +{Fore.BLUE}3){Style.RESET_ALL} Calcular Hash de Arquivo +{Fore.BLUE}4){Style.RESET_ALL} Whois Lookup +{Fore.BLUE}5){Style.RESET_ALL} DNS Lookup +{Fore.BLUE}6){Style.RESET_ALL} Sniffer de Pacotes +{Fore.BLUE}0){Style.RESET_ALL} Sair +""" + ) + choice = input("Opção: ").strip() + + if choice == "1": + ip_range = _prompt_non_empty("Faixa de IP (ex: 192.0.2.0/24): ") + results = arp_scan(ip_range) + display_results(f"Resultados do ARP Scan em {ip_range}", results) + elif choice == "2": + target = _prompt_non_empty("Alvo (IP ou domĂ­nio): ") + ports = input("Portas (ex: 22,80,443 ou 1-100) [1-1024]: ").strip() or "1-1024" + results = port_scan(target, ports) + display_results(f"Resultados do Port Scan em {target}", results) + elif choice == "3": + file_path = _prompt_non_empty("Caminho para o arquivo: ") + algorithm = input("Algoritmo [sha256]: ").strip() or "sha256" + file_hash = calculate_file_hash(file_path, algorithm) + display_results(f"Hash {algorithm.upper()} do Arquivo", file_hash) + elif choice == "4": + domain = _prompt_non_empty("DomĂ­nio (ex: example.com): ") + whois_info = whois_lookup(domain) + display_results(f"Whois Lookup para {domain}", whois_info) + elif choice == "5": + domain = _prompt_non_empty("DomĂ­nio (ex: example.com): ") + ip = dns_lookup(domain) + display_results(f"DNS Lookup para {domain}", ip) + elif choice == "6": + print(f"{Fore.YELLOW}Atenção: O Sniffer de Pacotes requer privilĂ©gios de root (sudo).{Style.RESET_ALL}") + interface = input("Interface (opcional): ").strip() or None + count_input = input("NĂșmero de pacotes (0 para ilimitado) [0]: ").strip() + timeout_input = input("Tempo limite em segundos (opcional): ").strip() + count = int(count_input) if count_input.isdigit() else 0 + timeout = int(timeout_input) if timeout_input.isdigit() else None + start_sniffer(interface, count, timeout) + elif choice == "0": + print(f"{Fore.GREEN}Encerrando modo interativo.{Style.RESET_ALL}") + break + else: + print(f"{Fore.YELLOW}Opção invĂĄlida. Escolha novamente.{Style.RESET_ALL}") + + def main(): print_banner() @@ -88,7 +151,7 @@ def main(): # ARP Scan arp_parser = net_subparsers.add_parser('arp', help='Realiza um ARP Scan para descoberta de hosts (Requer sudo)') - arp_parser.add_argument('ip_range', help='Faixa de IP (ex: 192.168.1.1/24)') + arp_parser.add_argument('ip_range', help='Faixa de IP (ex: 192.0.2.0/24)') # Port Scan port_parser = net_subparsers.add_parser('port', help='Realiza um Port Scan TCP (usando nmap)') @@ -118,6 +181,9 @@ def main(): sniff_parser.add_argument('-c', '--count', type=int, default=0, help='NĂșmero de pacotes a capturar (0 para ilimitado)') sniff_parser.add_argument('-t', '--timeout', type=int, default=None, help='Tempo mĂĄximo de captura em segundos') + # --- Subparser para Interface --- + subparsers.add_parser('ui', help='Inicia o modo interativo com menu guiado') + args = parser.parse_args() @@ -144,6 +210,9 @@ def main(): print(f"{Fore.YELLOW}Atenção: O Sniffer de Pacotes requer privilĂ©gios de root (sudo) para funcionar corretamente.{Style.RESET_ALL}") start_sniffer(args.interface, args.count, args.timeout) + elif args.command == 'ui': + run_interactive_menu() + elif args.command is None: parser.print_help() diff --git a/network_scanner.py b/network_scanner.py index 9e7496d..b3ad3d2 100644 --- a/network_scanner.py +++ b/network_scanner.py @@ -1,13 +1,55 @@ +import ipaddress import nmap +import re import socket from scapy.all import ARP, Ether, srp from colorama import Fore, Style +PORTS_PATTERN = re.compile(r"^\d{1,5}(-\d{1,5})?(,\d{1,5}(-\d{1,5})?)*$") + +def _is_valid_ports(ports): + if not ports or not PORTS_PATTERN.match(ports): + return False + for part in ports.split(","): + if "-" in part: + start, end = part.split("-", 1) + if not (start.isdigit() and end.isdigit()): + return False + start_i, end_i = int(start), int(end) + if start_i < 1 or end_i > 65535 or start_i > end_i: + return False + else: + if not part.isdigit(): + return False + value = int(part) + if value < 1 or value > 65535: + return False + return True + +def _is_valid_target(target): + if not target or any(char.isspace() for char in target): + return False + try: + ipaddress.ip_address(target) + return True + except ValueError: + pass + if len(target) > 253: + return False + label_regex = re.compile(r"^[A-Za-z0-9-]{1,63}$") + parts = target.rstrip(".").split(".") + return all(part and label_regex.match(part) and not part.startswith("-") and not part.endswith("-") for part in parts) + def arp_scan(ip_range): """ Realiza um ARP Scan para descobrir hosts ativos na rede local. Requer privilĂ©gios de root (sudo) para funcionar corretamente. """ + try: + ipaddress.ip_network(ip_range, strict=False) + except ValueError: + print(f"{Fore.RED}Erro: Faixa de IP invĂĄlida para ARP Scan (ex: 192.0.2.0/24).{Style.RESET_ALL}") + return [] print(f"{Fore.CYAN}Iniciando ARP Scan em {ip_range}...{Style.RESET_ALL}") try: # Cria o pacote ARP @@ -38,6 +80,12 @@ def port_scan(target_ip, ports='1-1024'): """ Realiza um Port Scan TCP usando nmap. """ + if not _is_valid_target(target_ip): + print(f"{Fore.RED}Erro: Alvo invĂĄlido para o Port Scan. Use um IP ou domĂ­nio vĂĄlido.{Style.RESET_ALL}") + return {} + if not _is_valid_ports(ports): + print(f"{Fore.RED}Erro: Faixa de portas invĂĄlida. Use nĂșmeros de 1-65535 separados por vĂ­rgulas ou intervalos (ex: 22,80,443 ou 1-100).{Style.RESET_ALL}") + return {} print(f"{Fore.CYAN}Iniciando Port Scan em {target_ip} (Portas: {ports})...{Style.RESET_ALL}") try: nm = nmap.PortScanner() @@ -74,7 +122,7 @@ def port_scan(target_ip, ports='1-1024'): if __name__ == '__main__': # Exemplo de uso (requer sudo para arp_scan) - # hosts = arp_scan("192.168.1.1/24") + # hosts = arp_scan("192.0.2.0/24") # print(hosts) # Exemplo de uso (nĂŁo requer sudo) diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index d196799..0000000 --- a/requirements.txt +++ /dev/null @@ -1,5 +0,0 @@ -scapy -python-nmap -requests -whois -colorama