Construindo um portscanner TCP com Python

Implementação de um portscanner básico em Python a fim de compreender um pouco o funcionamento de ferramentas como NMAP e sockets TCP.

[ Hits: 12.427 ]

Por: Perfil removido em 05/11/2018


Introdução



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

  1. IP será o primeiro argumento, como o próprio programa é o primeiro item da lista então o index do segundo argumento é 1.
  2. Se o usuário não passar o segundo argumento então escaneará todas as portas.
  3. Cria-se um objeto gerador com todas as portas a serem escaneadas.
  4. 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

  1. Criamos um socket IPv4(AF_INET), TCP(SOCK_STREAM) que interagirá com o alvo.
  2. 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.
  3. O método connect_ex retorna um número igual a 0 se a porta estiver aberta, logo colocamos uma condicional baseada nisso.
  4. 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'

  1. Aumentamos um pouco o tempo de timeout haja vista que pode demorar um pouco mais para receber o banner.
  2. Dessa vez nos conectamos diretamente à porta.
  3. Recebemos 1024 bytes do serviço rodando remotamente, convertemos de bytes para string, removemos quebra de linhas.
  4. Se não tiver recebido nenhum banner, uma Assertion Error será gerada, indo assim para o bloco except.
  5. O bloco except capturará todo tipo de exceção possível, Assertion Error, socket.timeout, ou problema de conexão, retornando 'Unknown' nesses casos.

    Próxima página

Páginas do artigo
   1. Introdução
   2. Código completo
Outros artigos deste autor

Gerencie suas informações através de instruções SQL com selects turbinados (para leigos e experts)

Passos essenciais para configurar seu modem 3G no Linux

Usando aMSN com plugin Music com suporte ao Juk e Amarok

Por que existem mais games para Windows do que para Linux?

Ingressando estações de trabalho Ubuntu no AD com Closed In Directory

Leitura recomendada

Python Flask Básico

PEP 8 - Guia de estilo para código Python

Pydev - Preparando o Eclipse para o Python

Clicador automático de Tinder com Python

Splash Screen para Inkscape

  
Comentários
[1] Comentário enviado por fabiocabrini em 20/03/2020 - 10:35h

Artigo muito bom!

Habilitei o multithreading e ficou bem rápido, foi necessário alterar o timeout da função child para 10ms.
Segue o código:

import sys
import socket
from threading import *

def main():
args = sys.argv
if len(args) < 2:
print("[!]Falta argumentos para o programa! Saindo...")
sys.exit(1)
ip = args[1]
portas = args[2] if len(args) >= 3 else "1:65536"
portas = (x for x in range(int(portas.split(":")[0]), int(portas.split(":")[1])+1))
scan(ip, portas)

def banner(sckt, ip, porta):
try:
sckt.settimeout(1)
sckt.connect((ip, porta))
banner = sckt.recv(1024).decode().strip()
assert banner
return banner
except:
return "Unknown"

def child(ip, port):
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
s.settimeout(10)
if s.connect_ex((ip, port)) == 0:
print("{}/tcp open".format(port), end="|")
print(banner(s, ip, port))
except:
pass

def scan(ip, portas):
for c in portas:
t = Thread(target=child, args=(ip, c))
t.start()

if __name__ == '__main__':
main()


Contribuir com comentário




Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts