O nosso bruteforce de subdomínios irá fazer parte de uma ferramenta mais aprimorada mais pra frente, por isso podemos e devemos fazer melhorias.
Vamos começar adicionando abaixo da entrada de domínio, dois dicionários, assim:
cons = list()
dom4 = dict()
dom6 = dict()
relDom = dict()
E altere as funções dnsIPv4 e dnsIPv6 para que elas retornem o endereço. Veja como ficará o dnsIPv4 e faça o mesmo com o dnsIPv6:
def dnsIPv4(subdom):
try:
dados = socket.getaddrinfo(subdom, None, socket.AF_INET)
addr = dados[0][4][0]
print(f"{subdom} - {addr}")
return addr
except socket.gaierror:
return 0
E altere a estrutura with open assim:
with open("wordlist-dominios.txt") as wordlist:
for w in wordlist.readlines():
w = w.replace("\n", "")
subdom = f"{w}.{dominio}"
if subdom not in cons:
addr4 = dnsIPv4(subdom)
addr6 = dnsIPv6(subdom)
cons.append(subdom)
Para ele salvar os dicionários, deixe a estrutura assim:
with open("wordlist-dominios.txt") as wordlist:
for w in wordlist.readlines():
w = w.replace("\n", "")
subdom = f"{w}.{dominio}"
if subdom not in cons:
addr4 = dnsIPv4(subdom)
addr6 = dnsIPv6(subdom)
if addr4 != 0:
dom4[subdom] = addr4
if addr6 != 0:
dom6[subdom] = addr6
cons.append(subdom)
E debaixo de tudo isso, fora de todas as identações, coloque isso pra ele colocar os dicionários em IPv4 e IPv6 dentro de um outro dicionário:
relDom["ipv4"] = dom4
relDom["ipv6"] = dom6
print(relDom)
Agora apague o print acima e coloque isso no lugar pra ele salvar como JSON:
domJson = json.dumps(relDom) # Coloque "import json" acima do código
with open(f"{dominio}-subdomains.json", "w") as arqJson:
arqJson.write(domJson)
No caso, salvaremos tudo num arquivo JSON só, que terá esse formato:
{"ipv4": {
"www.facebook.com": "157.240.222.35",
"mobile.facebook.com": "157.240.222.16",
"m.facebook.com": "157.240.222.35"},
"ipv6": {
"www.facebook.com": "2a03:2880:f148:82:face:b00c:0:25de",
"mobile.facebook.com": "2a03:2880:f048:11:face:b00c:0:2",
"m.facebook.com": "2a03:2880:f148:82:face:b00c:0:25de"}
}
Para ver um arquivo JSON, podemos usar um site como esse: https://jsonviewer.stack.hu/
Mas futuramente faremos isso com o próprio Python.
Podemos também fazer ele retornar o CNAME, CNAME é um tipo de registro DNS que funciona como um alias: Ele aponta um nome de domínio para outro. Por exemplo, o domínio help.4d3358.com
pode ter um CNAME apontando para um outro site de nome mais amigável. Isso significa que, ao resolver help.4d3358.com
, o DNS retorna o endereço desse outro site mais amigável. Porém, isso não garante redirecionamento no navegador, apenas o apontamento de nome.
Instale a biblioteca dnspython e vamos fazer esse programa:
import dns.resolver
dominio = str(input("Digite o domínio: "))
resp = dns.resolver.resolve(dominio, "CNAME")
print(resp)
Ele dará um erro caso o domínio não tenha nenhuma resposta. Para filtrar, fazemos assim:
import dns.resolver
dominio = str(input("Digite o domínio: "))
try:
resp = dns.resolver.resolve(dominio, "CNAME")
print(resp[0].target)
except dns.resolver.NoAnswer:
print("Não encontrado o CNAME!")
Caso passemos um outro subdomínio no mesmo domínio, ele dará erro. Também podemos filtrar ele:
import dns.resolver
dominio = str(input("Digite o domínio: "))
try:
resp = dns.resolver.resolve(dominio, "CNAME")
print(resp[0].target)
except dns.resolver.NoAnswer:
print("Não encontrado o CNAME!")
except dns.resolver.NXDOMAIN:
print("Não encontrado o subdomínio!")
Podemos também usar o bruteforce de subdomínios para isso, deixando dessa forma, nesse caso passe só o domínio:
import dns.resolver
dominio = str(input("Digite o domínio: "))
with open("wordlist-dominios.txt") as wordlist:
for w in wordlist.readlines():
w = w.replace("\n", "")
subdom = f"{w}.{dominio}"
try:
resp = dns.resolver.resolve(subdom, "CNAME")
cname = resp[0].target
print(f"{subdom} tem um alias {cname}")
except dns.resolver.NoAnswer:
pass
except dns.resolver.NXDOMAIN:
pass
PS: Não é porque tem um alias que o domínio exatamente tenha uma vulnerabilidade.
Ao fazer essas análises, pode ser que o próprio servidor bloqueie ele, então nesse caso, podemos fazer um filtro nele também:
with open("wordlist-dominios.txt") as wordlist:
for w in wordlist.readlines():
w = w.replace("\n", "")
subdom = f"{w}.{dominio}"
while True:
try:
resp = dns.resolver.resolve(subdom, "CNAME")
cname = resp[0].target
print(f"{subdom} tem um alias {cname}")
break
except dns.resolver.NoAnswer:
break
except dns.resolver.NXDOMAIN:
break
except dns.resolver.LifetimeTimeout:
print("Caiu no Timeout CNAME...")
time.sleep(2) # Adicione "import time" logo acima, aumente ou diminua conforme a necessidade
Vamos usar o JSON para salvar os resultados das nossas análises. No começo do script, chame o JSON e crie um dicionário, assim:
import dns.resolver
import time
import json
dominio = str(input("Digite o domínio: "))
cnameDic = dict()
E no try, faça assim:
resp = dns.resolver.resolve(subdom, "CNAME")
cname = resp[0].target
cnameDic[subdom] = str(cname)
print(f"{subdom} tem um alias {cname}")
break
E no final do código, fora de qualquer identação, coloque isso:
cnameJson = json.dumps(cnameDic)
with open(f"{dominio}-cname.json", "w") as arqJson:
arqJson.write(cnameJson)
print(f"Arquivo JSON {dominio}-cname.json criado!")
Agora, vamos organizar tudo num arquivo só que unirá os dois programas, volte no arquivo de subdomínios e vamos unir os dois. Primeiramente, crie uma classe assim:
import socket
import json
class DnsScan:
def subdominios(self, dominio, listaPl):
cons = list()
dom4 = dict()
dom6 = dict()
relDom = dict()
with open(listaPl) as wordlist:
for w in wordlist.readlines():
w = w.replace("\n", "")
subdom = f"{w}.{dominio}"
if subdom not in cons:
addr4 = self.dnsIPv4(subdom)
addr6 = self.dnsIPv6(subdom)
if addr4 != 0:
dom4[subdom] = addr4
if addr6 != 0:
dom6[subdom] = addr6
cons.append(subdom)
relDom["ipv4"] = dom4
relDom["ipv6"] = dom6
domJson = json.dumps(relDom)
with open(f"{dominio}-subdomains.json", "w") as arqJson:
arqJson.write(domJson)
print(f"Arquivo JSON {dominio}-subdomains.json criado!")
def dnsIPv4(self, subdom):
try:
dados = socket.getaddrinfo(subdom, None, socket.AF_INET)
addr = dados[0][4][0]
print(f"{subdom} - {addr}")
return addr
except socket.gaierror:
return 0
def dnsIPv6(self, subdom):
try:
dados = socket.getaddrinfo(subdom, None, socket.AF_INET6)
addr = dados[0][4][0]
print(f"{subdom} - {addr}")
return addr
except socket.gaierror:
return 0
No arquivo principal (como dnsfoda.py), fazemos assim:
from DnsScan import DnsScan
import sys
dominio = sys.argv[1]
wordlist = sys.argv[2]
dns = DnsScan()
dns.subdominios(dominio, wordlist)
E rode pelo terminal digitando python3 dnsfoda.py nomedosite.com wordlist.txt.
Para unir as funcionalidades dos dois programas, coloque esse método na classe DnsScan:
# Não esqueça de importar "dns.resolver" e "time"
def verifCName(self, dominio, listaPl):
cNameDic = dict()
with open(listaPl) as wordlist:
for w in wordlist.readlines():
w = w.replace("\n", "")
subdom = f"{w}.{dominio}"
while True:
try:
resp = dns.resolver.resolve(subdom, "CNAME")
cName = resp[0].target
cNameDic[subdom] = str(cName)
print(f"{subdom} tem um alias {cName}")
break
except dns.resolver.NoAnswer:
break
except dns.resolver.NXDOMAIN:
break
except dns.resolver.LifetimeTimeout:
print("Caiu no Timeout CNAME...")
time.sleep(3)
cNameJson = json.dumps(cNameDic)
with open(f"{dominio}-cname.json", "w") as arqJson:
arqJson.write(cNameJson)
print(f"Arquivo JSON {dominio}-cname.json criado!")
E altere o dnsfoda.py assim:
from DnsScan import DnsScan
import sys
dominio = sys.argv[1]
wordlist = sys.argv[2]
dns = DnsScan()
# dns.subdominios(dominio, wordlist)
dns.verifCName(dominio, wordlist)
Pra finalizar, deixe ele assim:
from DnsScan import DnsScan
import sys
dominio = sys.argv[1]
wordlist = sys.argv[2]
dns = DnsScan()
print("Iniciando bruteforce de subdomínios!")
dns.subdominios(dominio, wordlist)
print("Iniciando bruteforce de CNAME!")
dns.verifCName(dominio, wordlist)
PS: Podemos criar condições também para escolher apenas um dos scans.