diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..7b494bb --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Paulo Masson (PaulMass) + +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/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..15e32e4 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,96 @@ +# Segurança do Toolkit de Pentest + +## ⚠️ Avisos Legais e de Segurança + +### Uso Responsável +Este toolkit é projetado para **testes de segurança autorizados** em ambientes controlados. O uso não autorizado para acessar sistemas, redes ou dados sem permissão explícita é **ilegal** e viola: + +- Leis de privacidade e proteção de dados +- Leis de acesso não autorizado a sistemas computacionais +- Termos de serviço de provedores de internet + +### Requisitos Legais +- **Sempre obtenha autorização escrita** antes de testar qualquer sistema +- **Teste apenas sistemas que você possui ou tem permissão para testar** +- **Respeite todas as leis locais, estaduais e federais** + +## 🔒 Melhores Práticas de Segurança + +### Ao Usar Este Toolkit + +1. **Ambiente Controlado**: Execute testes apenas em redes isoladas ou ambientes de laboratório +2. **Permissões Mínimas**: Use o princípio do menor privilégio - execute com privilégios mínimos necessários +3. **Registro de Atividades**: Mantenha registros de todas as atividades de teste para auditoria +4. **Proteção de Dados**: Não armazene ou compartilhe dados sensíveis capturados durante os testes + +### Proteções Implementadas + +Este toolkit inclui as seguintes proteções: + +- **Validação de Entrada**: Todos os parâmetros são validados antes do processamento +- **Limitação de Dados**: Exibição limitada de dados capturados para evitar exposição acidental +- **Avisos de Segurança**: Mensagens claras sobre requisitos de permissão e implicações legais +- **Tratamento de Erros**: Manipulação robusta de erros para evitar vazamento de informações + +### Vulnerabilidades Conhecidas e Mitigações + +| Componente | Risco | Mitigação | +|------------|-------|------------| +| ARP Scan | Requer privilégios de root | Aviso claro sobre requisitos de permissão | +| Port Scan | Pode ser detectado como ataque | Limitação de taxa (--max-rate) | +| Sniffer | Captura dados sensíveis | Limitação de exibição de dados | +| Whois/DNS | Consulta serviços externos | Validação de domínio | + +## 🛡️ Configurações Recomendadas + +### Para Ambientes de Produção + +1. **Desative funcionalidades não necessárias** +2. **Implemente autenticação** para acesso ao toolkit +3. **Use redes isoladas** para testes +4. **Monitore atividades** com ferramentas de logging + +### Para Desenvolvimento + +1. **Teste em ambiente sandbox** +2. **Use dados fictícios** para testes +3. **Revise o código** regularmente para vulnerabilidades + +## 📋 Checklist de Segurança + +Antes de usar este toolkit: + +- [ ] Tenho autorização escrita para testar o alvo +- [ ] O alvo está em um ambiente controlado +- [ ] Estou ciente das leis aplicáveis +- [ ] Tenho permissão para capturar e analisar o tráfego +- [ ] Os dados capturados serão protegidos adequadamente +- [ ] Os resultados serão usados apenas para fins legítimos + +## 🚨 O Que NÃO Fazer + +- ❌ NÃO teste sistemas sem autorização +- ❌ NÃO capture tráfego em redes públicas +- ❌ NÃO armazene dados sensíveis sem proteção +- ❌ NÃO compartilhe resultados com terceiros não autorizados +- ❌ NÃO use este toolkit para atividades maliciosas + +## 📞 Reportando Problemas de Segurança + +Se você encontrar vulnerabilidades neste toolkit, por favor: + +1. **NÃO divulgue publicamente** até que uma correção esteja disponível +2. Contate o mantenedor do projeto de forma privada +3. Forneça detalhes suficientes para reproduzir o problema +4. Dê tempo razoável para correção antes da divulgação + +## 📜 Licença e Responsabilidade + +Este software é fornecido "COMO ESTÁ", sem garantias de qualquer tipo. Os autores não se responsabilizam por: + +- Uso indevido do software +- Danos causados pelo uso do software +- Violações de leis resultantes do uso do software +- Qualquer consequência do uso não autorizado + +O usuário assume toda a responsabilidade pelo uso deste toolkit. diff --git a/info_analysis.py b/info_analysis.py index b283816..7fe9cfb 100644 --- a/info_analysis.py +++ b/info_analysis.py @@ -1,19 +1,98 @@ import hashlib import os +import socket import whois import requests from colorama import Fore, Style +from urllib.parse import urlparse + + +def validate_file_path(file_path): + """ + Valida se o caminho do arquivo é seguro e existe. + + Args: + file_path (str): Caminho do arquivo + + Returns: + tuple: (bool, str) - (True, path) se válido, (False, mensagem_de_erro) caso contrário + """ + # Verificar se o caminho é absoluto e tentará acessar fora da árvore de diretórios + if os.path.isabs(file_path): + # Permitir apenas arquivos no diretório de trabalho atual ou subdiretórios + current_dir = os.path.abspath(os.getcwd()) + file_dir = os.path.abspath(os.path.dirname(file_path)) + if not file_dir.startswith(current_dir): + return False, f"Caminho absoluto fora do diretório de trabalho não é permitido: {file_path}" + + # Verificar se o arquivo existe + if not os.path.exists(file_path): + return False, f"Arquivo não encontrado: {file_path}" + + # Verificar se é um arquivo (não um diretório) + if not os.path.isfile(file_path): + return False, f"Caminho não é um arquivo: {file_path}" + + # Verificar permissões de leitura + if not os.access(file_path, os.R_OK): + return False, f"Sem permissão de leitura para o arquivo: {file_path}" + + return True, file_path + + +def validate_domain(domain): + """ + Valida se o domínio é seguro para consulta. + + Args: + domain (str): Domínio a ser validado + + Returns: + bool: True se válido, False caso contrário + """ + # Remover protocolos se presentes + domain = domain.replace('http://', '').replace('https://', '').replace('www.', '') + + # Padrao para domínio válido + domain_pattern = r'^[a-zA-Z0-9\-]+(\.[a-zA-Z0-9\-]+)*$' + + if not domain_pattern.match(domain): + return False + + # Verificar se o domínio não é um IP (para whois) + ip_pattern = r'^(\d{1,3}\.){3}\d{1,3}$' + if ip_pattern.match(domain): + return False + + return True + def calculate_file_hash(file_path, algorithm='sha256'): """ Calcula o hash de um arquivo usando o algoritmo especificado. + + Args: + file_path (str): Caminho para o arquivo + algorithm (str): Algoritmo de hash (md5, sha1, sha256, sha512) + + Returns: + str: Hash do arquivo ou None em caso de erro """ print(f"{Fore.CYAN}Calculando o hash {algorithm.upper()} para {file_path}...{Style.RESET_ALL}") + + # Validar algoritmo + valid_algorithms = ['md5', 'sha1', 'sha256', 'sha512'] + if algorithm.lower() not in valid_algorithms: + print(f"{Fore.RED}Erro: Algoritmo não suportado. Use: {', '.join(valid_algorithms)}{Style.RESET_ALL}") + return None + + # Validar caminho do arquivo + is_valid, message = validate_file_path(file_path) + if not is_valid: + print(f"{Fore.RED}{message}{Style.RESET_ALL}") + return None + try: - if not os.path.exists(file_path): - print(f"{Fore.RED}Erro: Arquivo não encontrado em {file_path}{Style.RESET_ALL}") - return None - hash_func = hashlib.new(algorithm) with open(file_path, 'rb') as f: # Lê o arquivo em blocos para lidar com arquivos grandes @@ -28,15 +107,29 @@ def calculate_file_hash(file_path, algorithm='sha256'): print(f"{Fore.RED}Ocorreu um erro ao calcular o hash: {e}{Style.RESET_ALL}") return None + # Nota: A extração de metadados de arquivos (como PDF, DOCX, Imagens) geralmente requer bibliotecas específicas (ex: PIL, exifread, python-docx, PyPDF2). # Para manter o escopo inicial e evitar muitas dependências, focaremos no hash e Whois. # Uma função de metadados básica pode ser adicionada posteriormente. + def whois_lookup(domain): """ Realiza uma consulta Whois para um domínio. + + Args: + domain (str): Domínio alvo (ex: google.com) + + Returns: + whois.parser.WhoisEntry: Objeto com informações Whois ou None em caso de erro """ print(f"{Fore.CYAN}Realizando consulta Whois para {domain}...{Style.RESET_ALL}") + + # Validar domínio + if not validate_domain(domain): + print(f"{Fore.RED}Erro: Domínio inválido para consulta Whois: {domain}{Style.RESET_ALL}") + return None + try: w = whois.whois(domain) print(f"{Fore.GREEN}Consulta Whois concluída.{Style.RESET_ALL}") @@ -46,11 +139,24 @@ def whois_lookup(domain): print(f"{Fore.RED}Ocorreu um erro durante a consulta Whois: {e}{Style.RESET_ALL}") return None + def dns_lookup(domain): """ Realiza uma consulta DNS básica para obter o endereço IP. + + Args: + domain (str): Domínio alvo (ex: github.com) + + Returns: + str: Endereço IP ou None em caso de erro """ print(f"{Fore.CYAN}Realizando consulta DNS para {domain}...{Style.RESET_ALL}") + + # Validar domínio + if not validate_domain(domain): + print(f"{Fore.RED}Erro: Domínio inválido para consulta DNS: {domain}{Style.RESET_ALL}") + return None + try: ip_address = socket.gethostbyname(domain) print(f"{Fore.GREEN}Consulta DNS concluída. IP: {ip_address}{Style.RESET_ALL}") @@ -62,6 +168,7 @@ def dns_lookup(domain): print(f"{Fore.RED}Ocorreu um erro durante a consulta DNS: {e}{Style.RESET_ALL}") return None + if __name__ == '__main__': # Exemplo de uso # Criar um arquivo de teste diff --git a/main.py b/main.py index bc1aae8..2ace502 100644 --- a/main.py +++ b/main.py @@ -23,10 +23,10 @@ def print_banner(): banner = f""" {Fore.RED} ____ _ _ ____ _____ _____ _____ _ _ _____ _____ - | _ \| \ | | _ \| ____| ____|_ _| \ | | ____|_ _| - | |_) | \| | |_) | _| | _| | | | \| | _| | | - | __/| |\ | _ <| |___| |___ | | | |\ | |___ | | - |_| |_| \_|_| \_\_____|_____| |_| |_| \_|_____| |_| + | _ \\ | \\ | | _ \\| ____| ____|_ _| \\ | | ____|_ _| + | |_) | \\| | |_) | _| | _| | | | \\| | _| | | + | __/| |\\ | _ <| |___| |___ | | | |\\ | |___ | | + |_| |_| \\_|_| \\_\\_____|_____| |_| |_| \\_|_____| |_| {Style.RESET_ALL} {Fore.GREEN} Toolkit de Pentest em Python - Manus AI{Style.RESET_ALL} """ diff --git a/network_scanner.py b/network_scanner.py index 9e7496d..f4b7981 100644 --- a/network_scanner.py +++ b/network_scanner.py @@ -1,14 +1,102 @@ import nmap import socket +import re from scapy.all import ARP, Ether, srp from colorama import Fore, Style + +def validate_ip_range(ip_range): + """ + Valida o formato de uma faixa de IP ou IP único. + + Args: + ip_range (str): Faixa de IP no formato CIDR (ex: 192.168.1.1/24) ou IP único + + Returns: + bool: True se válido, False caso contrário + """ + # Padrao para IP único + ip_pattern = r'^(\d{1,3}\.){3}\d{1,3}$' + # Padrao para CIDR + cidr_pattern = r'^(\d{1,3}\.){3}\d{1,3}/\d{1,2}$' + + if re.match(ip_pattern, ip_range): + # Validar que cada octeto está entre 0-255 + octets = ip_range.split('.') + return all(0 <= int(octet) <= 255 for octet in octets) + elif re.match(cidr_pattern, ip_range): + network, prefix = ip_range.split('/') + octets = network.split('.') + if not all(0 <= int(octet) <= 255 for octet in octets): + return False + # Validar prefixo (0-32) + return 0 <= int(prefix) <= 32 + return False + + +def validate_ip_or_domain(target): + """ + Valida se o alvo é um IP válido ou um domínio. + + Args: + target (str): IP ou domínio + + Returns: + bool: True se válido, False caso contrário + """ + # Padrao para IP + ip_pattern = r'^(\d{1,3}\.){3}\d{1,3}$' + # Padrao para domínio (simplificado) + domain_pattern = r'^[a-zA-Z0-9\-]+(\.[a-zA-Z0-9\-]+)*$' + + if re.match(ip_pattern, target): + octets = target.split('.') + return all(0 <= int(octet) <= 255 for octet in octets) + return bool(re.match(domain_pattern, target)) + + +def validate_ports(ports): + """ + Valida o formato das portas. + + Args: + ports (str): Portas no formato '22,80,443' ou '1-1024' + + Returns: + bool: True se válido, False caso contrário + """ + # Padrao para portas individuais separadas por virgula + individual_pattern = r'^(\d{1,5},)*\d{1,5}$' + # Padrao para faixa de portas + range_pattern = r'^\d{1,5}-\d{1,5}$' + + if re.match(individual_pattern, ports): + port_list = ports.split(',') + return all(0 <= int(p) <= 65535 for p in port_list) + elif re.match(range_pattern, ports): + start, end = ports.split('-') + return 0 <= int(start) <= int(end) <= 65535 + return False + + 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. + + Args: + ip_range (str): Faixa de IP no formato CIDR (ex: 192.168.1.1/24) + + Returns: + list: Lista de dicionários com 'ip' e 'mac' dos hosts encontrados """ print(f"{Fore.CYAN}Iniciando ARP Scan em {ip_range}...{Style.RESET_ALL}") + + # Validação de entrada + if not validate_ip_range(ip_range): + print(f"{Fore.RED}Erro: Formato de faixa de IP inválido. Use CIDR (ex: 192.168.1.1/24).{Style.RESET_ALL}") + return [] + try: # Cria o pacote ARP arp_request = ARP(pdst=ip_range) @@ -34,15 +122,34 @@ def arp_scan(ip_range): print(f"{Fore.RED}Ocorreu um erro durante o ARP Scan: {e}{Style.RESET_ALL}") return [] + def port_scan(target_ip, ports='1-1024'): """ Realiza um Port Scan TCP usando nmap. + + Args: + target_ip (str): IP ou domínio alvo + ports (str): Portas a escanear (ex: '22,80,443' ou '1-100') + + Returns: + dict: Dicionário com resultados do scan """ print(f"{Fore.CYAN}Iniciando Port Scan em {target_ip} (Portas: {ports})...{Style.RESET_ALL}") + + # Validação de entrada + if not validate_ip_or_domain(target_ip): + print(f"{Fore.RED}Erro: Alvo inválido. Use um IP válido ou domínio.{Style.RESET_ALL}") + return {} + + if not validate_ports(ports): + print(f"{Fore.RED}Erro: Formato de portas inválido. Use '22,80,443' ou '1-100'.{Style.RESET_ALL}") + return {} + try: nm = nmap.PortScanner() # Argumentos: -sV para detecção de versão, -T4 para velocidade, -p para portas - nm.scan(target_ip, ports, arguments='-sV -T4') + # Adicionado --max-rate para limitar taxa e evitar detecção + nm.scan(target_ip, ports, arguments='-sV -T4 --max-rate 100') scan_result = {} for host in nm.all_hosts(): @@ -72,6 +179,7 @@ def port_scan(target_ip, ports='1-1024'): print(f"{Fore.RED}Ocorreu um erro durante o Port Scan: {e}{Style.RESET_ALL}") return {} + if __name__ == '__main__': # Exemplo de uso (requer sudo para arp_scan) # hosts = arp_scan("192.168.1.1/24") diff --git a/sniffer.py b/sniffer.py index ae9cf83..d5fd563 100644 --- a/sniffer.py +++ b/sniffer.py @@ -1,6 +1,65 @@ -from scapy.all import sniff, IP, TCP, UDP, ICMP, Raw +from scapy.all import sniff, IP, TCP, UDP, ICMP, Raw, get_if_list from colorama import Fore, Style import time +import re + + +def get_available_interfaces(): + """ + Retorna uma lista de interfaces de rede disponíveis. + + Returns: + list: Lista de nomes de interfaces + """ + try: + interfaces = get_if_list() + return interfaces + except Exception: + return [] + + +def validate_interface(interface): + """ + Valida se a interface de rede existe. + + Args: + interface (str): Nome da interface + + Returns: + bool: True se válida, False caso contrário + """ + if interface is None: + return True # None significa usar a interface padrão + + available = get_available_interfaces() + return interface in available + + +def validate_count(count): + """ + Valida o número de pacotes a capturar. + + Args: + count (int): Número de pacotes + + Returns: + bool: True se válido, False caso contrário + """ + return isinstance(count, int) and count >= 0 + + +def validate_timeout(timeout): + """ + Valida o tempo limite de captura. + + Args: + timeout (int): Tempo em segundos + + Returns: + bool: True se válido, False caso contrário + """ + return timeout is None or (isinstance(timeout, int) and timeout > 0) + def packet_callback(packet): """ @@ -29,31 +88,58 @@ def packet_callback(packet): icmp_layer = packet[ICMP] print(f" {Fore.GREEN}ICMP:{Style.RESET_ALL} Tipo: {icmp_layer.type}, Código: {icmp_layer.code}") - # Dados Brutos + # Dados Brutos (limitado para evitar exposição de dados sensíveis) if Raw in packet: raw_data = packet[Raw].load try: # Tenta decodificar como texto (útil para HTTP) data_str = raw_data.decode('utf-8', errors='ignore') - # Limita a exibição para não poluir - print(f" {Fore.RED}Dados:{Style.RESET_ALL} {data_str[:50]}...") + # Limita a exibição para não poluir e evitar exposição de dados sensíveis + print(f" {Fore.RED}Dados:{Style.RESET_ALL} {data_str[:100]}...") except: # Se não for texto, exibe em hexadecimal print(f" {Fore.RED}Dados:{Style.RESET_ALL} {raw_data[:20].hex()}...") + def start_sniffer(interface=None, count=0, timeout=None): """ Inicia o sniffer de pacotes. Requer privilégios de root (sudo) e a interface correta. + + Args: + interface (str): Interface de rede para capturar (ex: eth0) + count (int): Número de pacotes a capturar (0 para ilimitado) + timeout (int): Tempo máximo de captura em segundos """ print(f"{Fore.BLUE}Iniciando Sniffer de Pacotes...{Style.RESET_ALL}") + + # Validação de entrada + if interface is not None and not validate_interface(interface): + available = get_available_interfaces() + print(f"{Fore.RED}Erro: Interface '{interface}' não encontrada.{Style.RESET_ALL}") + if available: + print(f"{Fore.YELLOW}Interfaces disponíveis: {', '.join(available)}{Style.RESET_ALL}") + return + + if not validate_count(count): + print(f"{Fore.RED}Erro: Contagem de pacotes inválida. Use um número inteiro >= 0.{Style.RESET_ALL}") + return + + if not validate_timeout(timeout): + print(f"{Fore.RED}Erro: Tempo limite inválido. Use um número inteiro > 0 ou None.{Style.RESET_ALL}") + return + if interface: print(f"{Fore.BLUE}Interface:{Style.RESET_ALL} {interface}") if count > 0: print(f"{Fore.BLUE}Limite de pacotes:{Style.RESET_ALL} {count}") if timeout: print(f"{Fore.BLUE}Tempo limite:{Style.RESET_ALL} {timeout} segundos") - + + # Aviso de segurança + print(f"{Fore.YELLOW}Atenção: O Sniffer de Pacotes requer privilégios de root (sudo) para funcionar corretamente.{Style.RESET_ALL}") + print(f"{Fore.YELLOW}Aviso: Capturar pacotes em redes sem autorização pode violar leis de privacidade.{Style.RESET_ALL}") + try: # Filtro: 'ip' para capturar apenas pacotes IP (exclui ARP, etc. a menos que especificado) # store=0 para não armazenar pacotes na memória (melhor para longas sessões) @@ -64,6 +150,7 @@ def start_sniffer(interface=None, count=0, timeout=None): except Exception as e: print(f"\n{Fore.RED}Ocorreu um erro durante a captura de pacotes: {e}{Style.RESET_ALL}") + if __name__ == '__main__': # Exemplo de uso (requer sudo) # start_sniffer(interface="eth0", count=10)