Introdução
No artigo de hoje, vamos desenvolver um Port Scan utilizando Python 3, mas, antes de mais nada, é fundamental compreender o que é um port scan e como ele funciona.
Entendendo um Port Scan
O que é um port scan?
Basicamente, trata-se de um programa que envia pacotes de dados para uma determinada porta em um ativo específico. O port scan opera na camada de transporte do protocolo TCP. Ele envia um pacote com a flag SYN para uma porta no servidor e, caso o servidor responda com SYN-ACK, entende-se que a porta está aberta.
Se o retorno for uma flag RST, a porta está fechada. Outra resposta possível é o ICMP Destination Unreachable, que indica que a porta está filtrada, ou seja, acessível apenas internamente ou por redes autorizadas.
Existem duas abordagens comuns para se realizar um port scan:
TCP — baseado em conexão (three-way handshake)
UDP — sem garantia de entrega dos pacotes
Veja a representação visual abaixo:
- TCP — baseado em conexão (3-way handshake)
- UDP — Não garante a entrega Veja abaixo isso de forma mais visual:
[Você] --------- SYN ----------> [Servidor Porta 80]
[Você] <------ SYN-ACK ---------- [Servidor Porta 80] (Porta aberta!)
[Você] -------- RST ------------> [Servidor Porta 80] (Fecha conexão)
Agora que compreendemos melhor o funcionamento do port scan, vamos ao nosso código. Para facilitar o entendimento, o código será apresentado em blocos e, ao final, será exibido de forma completa.
Nosso Port Scan
import socket
import sys
from datetime import datetime
from colorama import init, Fore
init(autoreset=True)
Este primeiro bloco importa as bibliotecas necessárias e inicializa o Colorama, que permite o uso de cores no terminal.
def scan_port(ip, port):
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(0.5)
result = sock.connect_ex((ip, port))
sock.close()
if result == 0:
print(Fore.GREEN + f"[+] Porta {port} aberta")
else:
print(Fore.RED + f"[-] Porta {port} fechada")
except Exception as e:
print(Fore.RED + f"[!] Erro ao escanear a porta {port}: {e}")
Esta é nossa primeira função, chamada scan_port
. Ela recebe como parâmetros o IP e a porta. O objetivo é tentar uma conexão com a porta especificada no host, retornando se ela está aberta ou fechada.
Na função, é criado um socket
para realizar a conexão. O bloco try
é utilizado para tratamento de exceções, retornando uma mensagem de erro caso a verificação falhe.
def main():
print("-" * 50)
print("Port Scanner Básico")
print("-" * 50)
target = input("Digite o IP ou domínio a ser escaneado: ")
ports_input = input("Digite as portas separadas por vírgula (ou pressione ENTER para todas as portas): ")
if ports_input.strip() == "":
ports = range(1, 65536) # Escaneia todas as portas
else:
try:
ports = [int(port.strip()) for port in ports_input.split(",")]
except ValueError:
print(Fore.RED + "[!] Formato inválido de porta(s). Use apenas números separados por vírgula.")
sys.exit(1)
print(f"\nIniciando o scan em {target}...\n")
start_time = datetime.now()
Esta função representa o ponto de entrada principal da aplicação. Ela exibe um banner no terminal e solicita ao usuário que informe o alvo (target), podendo ser um endereço IP ou domínio.
Também solicita que o usuário insira as portas a serem escaneadas, separadas por vírgula. Caso nenhuma porta seja informada, o código executará a varredura em todas as 65535 portas TCP.
As entradas do usuário são convertidas para inteiros. Se houver algum valor inválido, uma mensagem de erro é exibida e a execução é encerrada com sys.exit(1)
.
# Faz o scan em cada porta
for port in ports:
scan_port(target, port)
end_time = datetime.now()
total_time = end_time - start_time
print("\nScan finalizado!")
print(f"Tempo total: {total_time}")
if __name__ == "__main__":
main()
Por fim, o laço for
percorre cada porta informada, invocando a função scan_port
para realizar a tentativa de conexão.
Ao final do processo, o tempo total de execução do scan é exibido.
As saídas do script são coloridas: verde para portas abertas e vermelho para portas fechadas ou erros.