O Whois permite que a gente faça consultas sobre um domínio, com informações como o dono do domínio, números de documentos e outras informações confidenciais. Podemos escanear as chamadas do Whois num sniffer como o Wireshark. Podemos também criar nosso próprio Whois com o Python.
PS: Não é todo servidor Whois que tem as informações sobre determinados domínios.
Esse site aqui, dá as informações sobre qual servidor Whois tem determinado servidor: https://www.iana.org/whois
Vamos supor que colocamos um domínio no servidor acima, e ele mostra que o Whois que ele está é o whois.verisign-grs.com
. Nós abrimos o Netcat digitando nc -v whois.verisign-grs.com 43, e enquanto ele roda, colocamos o site que queremos (como o globo.com
). Ele retornará o servidor Whois dele, que pode ser algo como whois.1api.net
, aí rodamos o Netcat de novo com nc -v whois.1api.net 43 e passar o domínio novamente. Podemos passar o próprio IANA no Netcat, também na porta 43, digitando nc -v whois.iana.org 43.
Tudo que teremos que fazer, é uma ferramenta que automatiza isso. Crie um novo arquivo Python, e coloque esse código:
import socket
dominio = str(input("Digite o domínio: "))
dominio += "\r\n"
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(("whois.iana.org", 43))
sock.send(dominio.encode())
print(sock.recv(1024).decode())
Para pegar só o domínio do Whois que ele retorna, faça assim no print:
print(sock.recv(1024).decode().split("refer: ")[1].split("\n")[0])
Deixe o código assim, para entender como ele funciona melhor:
import socket
dominio = str(input("Digite o domínio: "))
dominio += "\r\n"
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(("whois.iana.org", 43))
sock.send(dominio.encode())
who1 = sock.recv(1024).decode().split("refer: ")[1].split("\n")[0]
print(who1)
sock.close()
Como a gente vai ter que ficar repetindo partes do código tentando vários domínios retornados pelo Whois até achar o que tem o domínio que queremos, é melhor usarmos funções pra isso, veja como fica:
import socket
def abrirSocket(who):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((who, 43))
return sock
dominio = str(input("Digite o domínio: "))
dominio += "\r\n"
sock = abrirSocket("whois.iana.org")
sock.send(dominio.encode())
who1 = sock.recv(1024).decode().split("refer: ")[1].split("\n")[0]
print(who1)
sock.close()
sock = abrirSocket(who1)
sock.send(dominio.encode())
who2 = sock.recv(1024).decode().split("Registrar WHOIS Server: ")[1].split("\r")[0] # Atenção, é "r", não "n"
print(who2)
sock.close()
sock = abrirSocket(who2)
sock.send(dominio.encode())
print(sock.recv(4096).decode())
O problema, é que mesmo aumentando o buffer do último print, ele pode cortar
a informação. Poderíamos dividir em dois ou mais prints, ou usar um while dessa forma, no lugar do último print:
while True:
data = sock.recv(1024)
if data:
print(data.decode())
else:
break
PS: Retire todos os prints que são usados pro debug, e deixe o último while assim:
resp = ""
while True:
data = sock.recv(1024)
if data:
resp += data.decode()
else:
break
print(resp)
Código completo abaixo, com mais algumas correções:
import socket
def abrirSocket(who, site):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((who, 43))
sock.send(site.encode())
return sock
dominio = str(input("Digite o domínio: "))
dominio += "\r\n"
sock = abrirSocket("whois.iana.org", dominio)
who1 = sock.recv(1024).decode().split("refer: ")[1].split("\n")[0]
sock.close()
sock = abrirSocket(who1, dominio)
who2 = sock.recv(1024).decode().split("Registrar WHOIS Server: ")[1].split("\r")[0] # Atenção, é "r", não "n"
sock.close()
sock = abrirSocket(who2, dominio)
resp = ""
while True:
data = sock.recv(1024)
if data:
resp += data.decode()
else:
break
print(resp)
No lugar do último print, vamos colocar isso pra ele salvar num arquivo:
dominio = dominio.strip()
with open(f"{dominio}-whois.txt", "w") as arqWhois:
arqWhois.write(resp)
print(f"Arquivo Whois {dominio}.whois criado!")
Podemos colocar em métodos numa classe, pode ser até a classe DnsScan mesmo:
def abrirSocket(self, who, site):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((who, 43))
sock.send(site.encode())
return sock
def whois(self, dominio):
dominio += "\r\n"
sock = self.abrirSocket("whois.iana.org", dominio)
who1 = sock.recv(1024).decode().split("refer: ")[1].split("\n")[0]
sock.close()
sock = self.abrirSocket(who1, dominio)
who2 = sock.recv(1024).decode().split("Registrar WHOIS Server: ")[1].split("\r")[0] # Atenção, é "r", não "n"
sock.close()
sock = self.abrirSocket(who2, dominio)
resp = ""
while True:
data = sock.recv(1024)
if data:
resp += data.decode()
else:
break
dominio = dominio.strip()
with open(f"{dominio}-whois.txt", "w") as arqWhois:
arqWhois.write(resp)
print(f"Arquivo Whois {dominio}.whois criado!")
E no programa principal (dnsfoda.py), fazemos assim:
from DnsScan import DnsScan
import sys
dominio = sys.argv[1]
wordlist = sys.argv[2]
dns = DnsScan()
print(f"Consultando Whois {dominio}")
dns.whois(dominio)
print(f"Consulta Whois finalizada!")
print("Iniciando bruteforce de subdomínios!")
dns.subdominios(dominio, wordlist)
print("Iniciando bruteforce de CNAME!")
dns.verifCName(dominio, wordlist)
print("Scan DNS finalizado!")
Podemos fazer com que o nosso programa crie pasta automaticamente com os scans (por exemplo, ao escanear globo.com
, ele cria a pasta com esses arquivos dentro).
Crie essa classe, pra começar:
import os
class Menu:
def __init__(self): # Construtor
if not os.path.isdir("scans"):
os.mkdir("scans") # Cria pasta scans
E no dnsfoda.py, podemos comentar tudo e deixar apenas isso:
from Menu import Menu
men = Menu()
E caso queiramos que ele crie uma pasta com o nome do domínio dentro dessa pasta scan, podemos fazer assim:
import os
class Menu:
def __init__(self, dominio): # Construtor
if not os.path.isdir("scans"):
os.mkdir("scans") # Cria pasta scans
if not os.path.isdir(f"scans/{dominio}"):
os.mkdir(f"scans/{dominio}")
E no dnsfoda.py:
from Menu import Menu
men = Menu("globo.com")
Daí, deixe a classe Menu assim:
class Menu:
def __init__(self): # Construtor
if not os.path.isdir("scans"):
os.mkdir("scans") # Cria pasta scans
E aí, nesse caso, no dnsfoda.py apenas passamos Menu(), sem argumento.
E aí, na classe DnsScan, alteramos as linhas do with open assim, como nessa:
if not os.path.isdir(f"scans/{dominio}"): # Importe os
os.mkdir(f"scans/{dominio}")
with open(f"scans/{dominio}/subdomains.json", "w") as arqJson:
arqJson.write(domJson)
Daí, faça o mesmo com todas as ocorrências do tipo.
E no programa principal:
from DnsScan import DnsScan
import sys
from Menu import Menu
men = Menu()
dns = DnsScan()
dominio = sys.argv[1]
wordlist = sys.argv[2]
print(f"Consultando Whois {dominio}")
dns.whois(dominio)
print(f"Consulta Whois finalizada!")
print("Iniciando bruteforce de subdomínios!")
dns.subdominios(dominio, wordlist)
print("Iniciando bruteforce de CNAME!")
dns.verifCName(dominio, wordlist)
print("Scan DNS finalizado!")
Na classe Menu, coloque esse método, para listar os diretórios:
def exibirMenu(self):
print(os.listdir("scans"))
E no dnsfoda.py, chame ele assim (comente o restante do código, exceto as importações, por enquanto):
men = Menu()
men.exibirMenu()
Agora descomente tudo pra criar tudo que precisamos.
Deixe a função exibirMenu assim:
def exibirMenu(self):
scans = os.listdir("scans")
id = 0
for s in scans:
print(f"{id} - {s}")
id += 1
sel = int(input("Selecione o ID: "))
pasta = scans[sel]
print(pasta)
E depois, deixe ela assim pra completar o código por ora:
def exibirMenu(self):
scans = os.listdir("scans")
id = 0
for s in scans:
print(f"{id} - {s}")
id += 1
sel = int(input("Selecione o ID: "))
pasta = scans[sel]
with open(f"scans/{pasta}/cname.json") as arqCname:
cName = arqCname.read()
print(cName)
Mude as últimas linhas para isso:
with open(f"scans/{pasta}/cname.json") as arqCname:
cName = json.loads(arqCname.read()) # Importe json
for c in cName:
print(f"{c} alias para {cName[c]}")
E depois pra isso, pra abrir todos os arquivos:
with open(f"scans/{pasta}/cname.json") as arqCname:
cName = json.loads(arqCname.read())
with open(f"scans/{pasta}/subdomains.json") as arqSubs:
subdom = json.loads(arqSubs.read())
subIPv4 = subdom["ipv4"]
subIPv6 = subdom["ipv6"]
with open(f"scans/{pasta}/whois.txt") as arqWhois:
whois = arqWhois.read()
Daí, dentro da função, fora dos with open, podemos, por exemplo, exibir a quantidade de domínios IPv4 e IPv6, assim:
print(f"Subdomínios IPv4: {len(subIPv4)}")
print(f"Subdomínios IPv6: {len(subIPv6)}")
E depois completamos tudo assim, no lugar desses dois prints:
print(f"Subdomínios IPv4: {len(subIPv4)}")
print(f"Subdomínios IPv6: {len(subIPv6)}")
print(f"CNames: {len(cName)}")
print("Escolha IPv4, IPv6, CName, Whois ou Sair!")
while True:
sel = str(input("Digite qual você quer exibir: "))
if sel.lower() == "ipv4":
for s in subIPv4:
print(f"{s} -> {subIPv4[s]}")
elif sel.lower() == "ipv6":
for s in subIPv6:
print(f"{s} -> {subIPv6[s]}")
elif sel.lower() == "cname":
for c in cName:
print(f"{c} -> {cName[c]}")
elif sel.lower() == "whois":
print(whois)
elif sel.lower() == "sair":
break
else:
print("Comando não encontrado! Escolha IPv4, IPv6, CName, Whois ou Sair!")
E pra finalizar, deixe o dnsfoda.py assim:
from DnsScan import DnsScan
import sys
from Menu import Menu
men = Menu()
dns = DnsScan()
if len(sys.argv) == 1:
men.exibirMenu()
exit()
dominio = sys.argv[1]
wordlist = sys.argv[2]
print(f"Consultando Whois {dominio}")
dns.whois(dominio)
print(f"Consulta Whois finalizada!")
print("Iniciando bruteforce de subdomínios!")
dns.subdominios(dominio, wordlist)
print("Iniciando bruteforce de CNAME!")
dns.verifCName(dominio, wordlist)
print("Scan DNS finalizado!")