A maioria das pessoas da área de Segurança da Informação conhece a famigerada ferramenta
Nmap, que possibilita um scan de portas na máquina alvo de diversas formas e métodos a gosto do usuário. Neste artigo ensinarei a criar um portscanner TCP a fim de prover o entendimento de como tais ferramentas trabalham. Partirei do pressuposto de que o leitor possui conhecimentos básicos em Python e protocolos de rede.
Estrutura do programa e requisitos
Primeiramente criarei a ferramenta da forma mais básica e depois a aprimorarei adicionando threads e funcionalidades extras. Para o programa usarei os módulos:
- sys :: para a passagem de argumentos
- socket :: para o uso de sockets TCP
- threading :: para o uso de threads no programa mais avançado
A estrutura, a priori, será composta de quatro funções:
- main :: função principal que receberá os argumentos e chamará scan
- scan :: função que iterará sobre as portas chamando child para cada
- child :: função que cuidará do scan de uma determinada porta, chamará banner
- banner :: função que tentará descobrir o serviço rodando em determinada porta
Função Main
def main():
args = sys.argv
if len(args) < 2:
print("[!]Falta argumentos para o programa!Saindo...")
sys.exit(1)
ip = args[1] # 1
portas = args[2] if len(args >= 3) else "1:65536" # 2
portas = (x for x in range(int(portas.split(":")[0]), int(portas.split(":")[1])+1)) # 3
scan(ip, portas) # 4
- IP será o primeiro argumento, como o próprio programa é o primeiro item da lista então o index do segundo argumento é 1.
- Se o usuário não passar o segundo argumento então escaneará todas as portas.
- Cria-se um objeto gerador com todas as portas a serem escaneadas.
- Chamamos agora a função scan.
Função Scan
def scan(ip, portas):
for c in portas:
child(ip, c)
Função bem simples que chama uma child para cada porta.
Função Child
Agora inicia-se realmente a parte lógica do programa:
def child(ip, port):
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 1
s.settimeout(0.3) # 2
if s.connect_ex((ip, port)) == 0: # 3
print("{}/tcp open".format(port), end='|')
print(banner(s, ip, port)) # 4
except:
pass
- Criamos um socket IPv4(AF_INET), TCP(SOCK_STREAM) que interagirá com o alvo.
- Setamos um timeout, se o alvo não responder em 0.3 uma exceção será gerada e a função chegará ao fim no bloco except.
- O método connect_ex retorna um número igual a 0 se a porta estiver aberta, logo colocamos uma condicional baseada nisso.
- Após sabermos que a porta está aberta, chamamos o método banner que tentará pegar informações do serviço rodando nesta porta, printando seu retorno que pode ser 'Unknown' se não conseguir ou a string desejada.
Função Banner
def baner(sckt, ip, porta):
try:
sckt.settimeout(1) # 1
sckt.connect((ip, porta)) # 2
banner = sckt.recv(1024).decode().strip() # 3
assert banner # 4
return banner
except: # 5
return 'Unknown'
- Aumentamos um pouco o tempo de timeout haja vista que pode demorar um pouco mais para receber o banner.
- Dessa vez nos conectamos diretamente à porta.
- Recebemos 1024 bytes do serviço rodando remotamente, convertemos de bytes para string, removemos quebra de linhas.
- Se não tiver recebido nenhum banner, uma Assertion Error será gerada, indo assim para o bloco except.
- O bloco except capturará todo tipo de exceção possível, Assertion Error, socket.timeout, ou problema de conexão, retornando 'Unknown' nesses casos.