Python é a língua franca da segurança ofensiva. A maioria das ferramentas modernas é escrita nela, exploits públicos vêm em Python, e quando nenhuma ferramenta existente faz exatamente o que você precisa, você escreve a sua — em minutos. Nesta aula você não vai aprender Python do zero (assumo que você consegue ler lógica básica; se não, o curso tem referências), mas vai aprendê-lo pela ótica ofensiva, construindo algo real: um scanner de portas TCP funcional, usando sockets. Ao final, você entenderá por dentro o que o Nmap faz por fora — e esse é exatamente o princípio "o manual antes da ferramenta".
O socket é o handshake em código
O conceito central é o socket: a abstração de programação que representa um "ponto de conexão" de rede. Criar um socket e tentar conectá-lo a um IP e porta é, na prática, iniciar aquele three-way handshake TCP da aula Protocolos em ação: handshake TCP, DNS e inspeção real. Se a conexão tem sucesso, a porta está aberta; se falha ou expira, está fechada ou filtrada. Toda a varredura de portas se reduz a essa ideia. Veja o núcleo mínimo, testando uma porta:
import socket
alvo = "127.0.0.1"
porta = 80
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # AF_INET=IPv4, SOCK_STREAM=TCP
s.settimeout(1) # não espera para sempre
resultado = s.connect_ex((alvo, porta)) # tenta conectar
if resultado == 0:
print(f"[+] Porta {porta} ABERTA")
else:
print(f"[-] Porta {porta} fechada/filtrada")
s.close()
O método-chave é o connect_ex: ele tenta o handshake e devolve 0 se a conexão foi estabelecida (porta aberta), ou um código de erro caso contrário. Usamos connect_ex em vez de connect justamente porque ele retorna um código em vez de lançar exceção, o que é mais limpo para varredura. O settimeout(1) é essencial: sem ele, uma porta filtrada faria o script travar esperando indefinidamente.
De uma porta para mil
Agora transformamos isso num scanner de verdade, varrendo um intervalo de portas com um laço. Esta é a versão que você vai efetivamente usar em laboratório:
import socket
def scan_porta(alvo, porta):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(1)
resultado = s.connect_ex((alvo, porta))
s.close()
return resultado == 0 # True se aberta
alvo = input("Alvo: ")
print(f"Escaneando {alvo}...\n")
for porta in range(1, 1025): # portas bem-conhecidas: 1 a 1024
if scan_porta(alvo, porta):
print(f"[+] Porta {porta} ABERTA")
print("\nScan concluído.")
Rode isso contra uma máquina do seu laboratório (jamais contra alvos sem autorização — a regra de ouro de sempre) e ele lista as portas abertas, exatamente como um scanner comercial faria, só que você entende cada linha. Um exercício revelador: rode-o contra sua própria máquina Metasploitable e compare o resultado com o do Nmap; eles devem coincidir, e ver isso funcionar fecha o conceito.
Do funciona ao funciona rápido
Há um problema óbvio: esse scanner é lento, porque testa uma porta de cada vez, esperando o timeout de cada uma em sequência. A solução profissional é o paralelismo — testar muitas portas simultaneamente com threads. Não é preciso dominar concorrência agora, mas veja como a ideia se materializa, porque é o que torna a ferramenta utilizável:
import socket
from concurrent.futures import ThreadPoolExecutor
alvo = "127.0.0.1"
abertas = []
def scan_porta(porta):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(1)
if s.connect_ex((alvo, porta)) == 0:
abertas.append(porta)
s.close()
# dispara até 100 verificações em paralelo
with ThreadPoolExecutor(max_workers=100) as executor:
executor.map(scan_porta, range(1, 1025))
print("Portas abertas:", sorted(abertas))
Com ThreadPoolExecutor, o mesmo scan que levaria minutos roda em segundos. A diferença de velocidade é dramática, e essa passagem do "funciona" para o "funciona rápido" é uma lição de engenharia que se repete em toda ferramenta ofensiva.
O banner entrega software e versão
Um refinamento que aproxima seu scanner do profissional: além de saber que a porta está aberta, descobrir qual serviço responde nela, capturando o banner — o texto de identificação que muitos serviços enviam ao conectar. É o embrião da enumeração do Bloco VI:
import socket
def pegar_banner(alvo, porta):
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(2)
s.connect((alvo, porta))
banner = s.recv(1024).decode(errors="ignore").strip() # lê o que o serviço envia
s.close()
return banner
except:
return None
banner = pegar_banner("127.0.0.1", 22)
if banner:
print(f"Serviço na porta 22: {banner}") # ex.: "SSH-2.0-OpenSSH_8.2p1..."
Aquele retorno tipo SSH-2.0-OpenSSH_8.2p1 não é detalhe: a versão do serviço é o que você cruza com bases de vulnerabilidades para descobrir se há um exploit conhecido (aula CVE, CVSS e CWE: a linguagem das vulnerabilidades). Um banner entregou, de graça, o software e a versão exata do alvo.
A ferramenta que parecia mágica
O que espero que você leve desta aula vai além do scanner. É a percepção de que as ferramentas que parecem mágicas são, por dentro, lógica que você é capaz de escrever. Nmap é este scanner com anos de refinamento, milhares de casos tratados e otimizações; mas o coração — abrir sockets, testar conexões, ler banners — é o que você acabou de construir. Essa desmistificação é libertadora: quando uma ferramenta pronta falhar ou não existir para o seu caso, você não trava, você codifica a solução. Do lado defensivo, entender scanning por dentro é entender o que aparece nos logs quando alguém varre sua rede — conexões parciais em rajada, múltiplas portas de uma mesma origem —, o que alimenta as detecções do Blue Team.
Fontes e leituras recomendadas
- Black Hat Python — Justin Seitz (o livro de referência para Python ofensivo)
- Violent Python — TJ O'Connor (projetos práticos de segurança em Python)
- Documentação oficial do módulo
socket(docs.python.org) - Automate the Boring Stuff with Python — Al Sweigart (para reforçar a base da linguagem, gratuito)
Exercícios
-
O que é um socket, e como tentar conectá-lo a uma porta se relaciona com o handshake TCP da aula Protocolos em ação: handshake TCP, DNS e inspeção real?
✓ Resposta:Um socket é a abstração que representa um ponto de conexão de rede. Tentar conectá-lo a um IP e porta inicia, na prática, o three-way handshake do TCP; se o handshake se completa, a porta está aberta.
-
Por que o código usa
connect_exem vez deconnect, e o que o retorno0significa?✓ Resposta:connect_exretorna um código de erro em vez de lançar uma exceção, o que é mais limpo para varredura. O retorno0significa que a conexão foi estabelecida com sucesso — porta aberta. -
Qual o papel do
settimeout, e o que aconteceria ao scanner sem ele?✓ Resposta:O
settimeoutlimita quanto tempo o socket espera por resposta. Sem ele, o scanner travaria indefinidamente ao encontrar uma porta filtrada que não responde. -
Que problema o uso de threads (
ThreadPoolExecutor) resolve num scanner de portas?✓ Resposta:Resolve a lentidão: sem threads, as portas são testadas em sequência, cada uma esperando seu timeout; com threads, muitas são testadas em paralelo, reduzindo o tempo de minutos para segundos.
-
O que é "capturar o banner" de um serviço, e por que a versão obtida é valiosa para o pentester?
✓ Resposta:É ler o texto de identificação que o serviço envia ao conectar (ex.:
SSH-2.0-OpenSSH_8.2p1). A versão é valiosa porque pode ser cruzada com bases de vulnerabilidades para identificar exploits conhecidos aplicáveis àquele software.