Voltando ao Servidor TCP que criamos, podemos por exemplo criar log a cada acesso ao nosso servidor. Colocaríamos, por exemplo, esse código entre o sock.listen(1) e o primeiro print:
log = open("servidor.log", "a")
log.write(f"Aguardando conexão {ip}:{porta}...")
log.close()
No entanto, podemos colocar logs em vários trechos do código, então para facilitar e deixar o código mais limpo, podemos criar uma função assim:
def criarLog(msg):
log = open("servidor.log", "a")
log.write(msg + "\n")
log.close()
print(msg)
Daí, podemos fazer o código do nosso servidor assim:
import socket
def criarLog(msg):
log = open("servidor.log", "a")
log.write(msg + "\n")
log.close()
print(msg)
ip = "0.0.0.0"
porta = 8080
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind((ip, porta))
sock.listen(1)
criarLog(f"Aguardando conexão {ip}:{porta}...")
conn, cliente = sock.accept()
criarLog(f"Conectado com {cliente}.")
conn.send("Digite a senha: ".encode())
senha = conn.recv(1024)
if senha.decode().strip() == "senhafoda":
criarLog(f"{cliente} acertou a senha!")
while True:
msg = input("> ")
if msg == "sair":
conn.send(f"Servidor encerrando conexão com {cliente}...")
sock.close()
break
msg += "\n"
conn.send(msg.encode())
dados = conn.recv(1024)
print(dados.decode(), end = "")
else:
criarLog(f"{cliente} errou a senha!")
conn.send("Senha Incorreta!".encode())
sock.close()
PS: Podemos melhorar a escrita dos logs, usando, por exemplo, data e hora do site.
Para escolher se ele printa na tela ou só escreve no log, podemos melhorar assim:
def criarLog(msg, v = ""):
log = open("servidor.log", "a")
log.write(msg + "\n")
log.close()
if v == "-v":
print(msg)
E daí, onde tiver que colocar verbose, basta especificar o -v
entre aspas, dessa forma:
criarLog(f"Aguardando conexão {ip}:{porta}...", verb) # verb teria o "-v" como argumento
Usando com parâmetros:
ip = "0.0.0.0"
porta = int(sys.argv[1])
try:
verb = sys.argv[2]
except:
verb = 0
Usando a POO, podemos criar uma classe pro servidor que trabalhe tanto com TCP quanto com UDP, dessa forma:
import socket
class Servidor:
def tcp(self, ip, porta):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind((ip, porta))
sock.listen(1)
print(f"Aguardando conexão TCP {ip}:{porta}.")
conn, cliente = sock.accept()
conn.send("Digite a senha: ".encode())
senha = conn.recv(1024)
if senha.decode().strip() == "senhafoda":
while True:
msg = input("> ")
if msg == "sair":
sock.close()
break
msg += "\n"
conn.send(msg.encode())
dados = conn.recv(1024)
print(dados.decode(), end = "")
else:
sock.close()
def udp(self, ip, porta):
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind((ip, porta))
print(f"Aguardando conexão UDP {ip}:{porta}.")
while True:
dados, cliente = sock.recvfrom(1024)
print(f"{cliente} - {dados.decode()}")
msg = input("> ")
sock.sendto(msg.encode(), cliente)
Daí, num outro arquivo (como ncfoda.py) podemos chamar ele assim:
from Servidor import Servidor
ip = "0.0.0.0"
porta = 8080
serv = Servidor()
serv.tcp(ip, porta)
# serv.udp(ip, porta)
Daí, é só rodar um cliente UDP ou TCP e vê se ele responde (teste com os dois).
No nosso código principal, podemos melhorar ainda mais, assim:
import sys
from Servidor import Servidor
ip = "0.0.0.0"
arg = sys.argv[1]
porta = int(sys.argv[2])
serv = Servidor()
if arg.lower() == "-t":
serv.tcp(ip, porta)
elif arg.lower() == "-u":
serv.udp(ip, porta)
Daí é só rodar na sintaxe python3 ncfoda.py -t 8080.
Da mesma forma, criaremos uma classe pra cliente, usando a mesma base.
Para fazer a classe do cliente, podemos fazer assim:
import socket
class Cliente:
def tcp(self, ip, porta):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((ip, porta))
msg = sock.recv(1024).decode()
senha = input(msg)
sock.send(senha.encode())
while True:
print(sock.recv(1024).decode(), end = "")
msg = input("> ")
msg += "\n"
sock.send(msg.encode())
def udp(self, ip, porta):
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
while True:
msg = input("> ")
sock.sendto(msg.encode(), (ip, porta))
dados, serv = sock.recvfrom(1024)
print(f"{serv} - {dados.decode()}")
Como estamos criando um programa que funciona como o Netcat, podemos fazer que o mesmo programa seja tanto cliente como servidor. No ncfoda.py podemos fazer assim:
import sys
from Servidor import Servidor
from Cliente import Cliente
help = """Forma de uso:
python3 ncfoda.py -s [tcp/udp] port
python3 ncfoda.py -c [tcp/udp] ip port"""
try:
tipo = sys.argv[1]
except:
print(help, end = "")
exit(0)
if tipo.lower() == "-s":
ip = "0.0.0.0"
protoc = sys.argv[2]
porta = int(sys.argv[3])
serv = Servidor()
if protoc.lower() == "tcp":
try:
serv.tcp(ip, porta)
except KeyboardInterrupt:
exit(0)
elif protoc.lower() == "udp":
try:
serv.udp(ip, porta)
except KeyboardInterrupt:
exit(0)
else:
print(help, end = "")
exit(0)
elif tipo.lower() == "-c":
protoc = sys.argv[2]
ip = sys.argv[3]
porta = int(sys.argv[4])
clie = Cliente()
if protoc.lower() == "tcp":
try:
clie.tcp(ip, porta)
except KeyboardInterrupt:
exit(0)
elif protoc.lower() == "udp":
try:
clie.udp(ip, porta)
except KeyboardInterrupt:
exit(0)
else:
print(help, end = "")
exit(0)
elif tipo.lower() == "-h" or tipo.lower() == "--help":
print(help, end = "")
exit(0)
else:
print(help, end = "")
exit(0)
Daí, fazemos um programa tipo Netcat completo, com modo cliente e servidor, e com TCP e UDP. Podemos abrir dois terminais, um simulando o servidor e outro simulando o cliente.
No servidor, podemos fazer, por exemplo, assim:
python3 ncfoda.py -s tcp 8080
E no cliente:
python3 ncfoda.py -c tcp 127.0.0.1 8080
Vamos supor que nós temos poucas informações sobre um domínio qualquer, para descobrir informações sobre subdomínios existentes nesse domíno em si, por exemplo, o domínio twitter.com
tem como subdomínios coisas como mail.twitter.com
e mobile.twitter.com
, nós podemos criar um script que realiza o bruteforce dos subdomínios de um domínio. Veja um exemplo abaixo:
import socket
dominio = str(input("Informe o domínio: "))
dados = socket.getaddrinfo(dominio, None, socket.AF_INET)
print(dados)
Essa saída é o retorno de alguns itens que foram puxados do domínio informado, com uma lista com duas tuplas dentro uma da outra. Pra filtrar o IP, fazemos assim:
print(dados[0][4][0])
E pra fazer um filtro de subdomínios, podemos fazer assim:
import socket
dominio = str(input("Informe o domínio: "))
sub = ("admin", "teste", "cpanel", "ftp", "ssh", "mail", "www", "mobile", "m")
for s in sub:
subdom = f"{s}.{dominio}"
try:
dados = socket.getaddrinfo(subdom, None, socket.AF_INET)
print(f"{subdom} - {dados[0][4][0]}")
except socket.gaierror:
pass
Mas o melhor é usar um arquivo txt com os subdomínios, podemos usar por exemplo um que está num repositório no GitHub, que clonamos digitando git clone https://github.com/danielmiessler/SecLists.git. Vá na pasta Discovery e DNS e pegue uma das wordlists lá. Ou baixe essa como exemplo, clicando aqui.
Podemos usar a estrutura with open para abrir arquivos de textos muito grandes, como com a wordlist, assim:
import socket
dominio = str(input("Informe o domínio: "))
with open("wordlist-dominios.txt") as wordlist:
for w in wordlist.readlines():
w = w.replace("\n", "")
subdom = f"{w}.{dominio}"
try:
dados = socket.getaddrinfo(subdom, None, socket.AF_INET)
print(f"{subdom} - {dados[0][4][0]}")
except socket.gaierror:
pass
Para ele funcionar tanto com IPv4 quanto com IPv6, podemos criar com funções, assim:
import socket
def dnsIPv4(subdom):
try:
dados = socket.getaddrinfo(subdom, None, socket.AF_INET)
print(f"{subdom} - {dados[0][4][0]}")
except socket.gaierror:
pass
def dnsIPv6(subdom):
try:
dados = socket.getaddrinfo(subdom, None, socket.AF_INET6)
print(f"{subdom} - {dados[0][4][0]}")
except socket.gaierror:
pass
dominio = str(input("Informe o domínio: "))
with open("wordlist-dominios.txt") as wordlist:
for w in wordlist.readlines():
w = w.replace("\n", "")
subdom = f"{w}.{dominio}"
dnsIPv4(subdom)
dnsIPv6(subdom)
PS: Podemos colocar a biblioteca sys e os parâmetros sys.argv[1] e sys.argv[2] no domínio e na wordlist.