Update turbolist3r.py
Changed the analysis DNS requests to use dnslib for better subdomain takeover detection. The dns.resolver library could miss out on some subdomains pointing to vulnerable cloud services that returned NXDOMAIN status since the cloud service did not exist but the target domain's pointer record still existed and pointed to it.
This commit is contained in:
parent
99086ac1e0
commit
0271f4c63f
|
|
@ -1,13 +1,16 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# coding: utf-8
|
# coding: utf-8
|
||||||
# Turbolist3r v1.0
|
# Turbolist3r v0.2
|
||||||
# By Carl Pearson - github.com/fleetcaptain
|
# By Carl Pearson - github.com/fleetcaptain
|
||||||
# Based on Sublist3r code created by Ahmed Aboul-Ela - twitter.com/aboul3la
|
# Based on Sublist3r code created by Ahmed Aboul-Ela - twitter.com/aboul3la
|
||||||
# Tested on Ubuntu Linux 16.10
|
|
||||||
#
|
#
|
||||||
# Changes to Turbolist3r from Sublist3r:
|
# Changes to Turbolist3r from Sublist3r:
|
||||||
# - check subdomain for text "From http://PTRarchive.com: " and remove it (otherwise it ends up in the output and can impede automated analysis with other tools)
|
# - check subdomain for text "From http://PTRarchive.com: " and remove it (otherwise it ends up in the output and can impede automated analysis with other tools)
|
||||||
# - added functionality to query found subdomains, record answer, and catagorize as A or CNAME record. Speeds up subdomain takeover analysis as CNAME records and the services they point to are collected and displayed
|
# - added functionality to query found subdomains, record answer, and catagorize as A or CNAME record. Speeds up subdomain takeover analysis as CNAME records and the services they point to are collected and displayed
|
||||||
|
#
|
||||||
|
# TODO - merge Sublist3r dns requests with dnslib to avoid duplication of dns libraries
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
# modules in standard library
|
# modules in standard library
|
||||||
import re
|
import re
|
||||||
|
|
@ -23,9 +26,13 @@ import socket
|
||||||
from collections import Counter
|
from collections import Counter
|
||||||
|
|
||||||
# external modules
|
# external modules
|
||||||
from subbrute import subbrute
|
#from subbrute import subbrute
|
||||||
import dns.resolver
|
import dns.resolver
|
||||||
import requests
|
import requests
|
||||||
|
# import dnslib, which provides better features compared to dns.resolver for finding subdomains
|
||||||
|
# for example, a return status of NXDOMAIN causes an exception with dns.resolver, but dnslib allows
|
||||||
|
# us to easily capture the reply, which could indicate the precsence of subdomain takeover
|
||||||
|
import dnslib
|
||||||
|
|
||||||
# Python 2.x and 3.x compatiablity
|
# Python 2.x and 3.x compatiablity
|
||||||
if sys.version > '3':
|
if sys.version > '3':
|
||||||
|
|
@ -1023,49 +1030,47 @@ def main(domain, threads, savefile, ports, silent, verbose, enable_bruteforce, e
|
||||||
cnames = ['== CNAME records ==']
|
cnames = ['== CNAME records ==']
|
||||||
ahosts = ['== A records ==']
|
ahosts = ['== A records ==']
|
||||||
def lookup(guess, name_server):
|
def lookup(guess, name_server):
|
||||||
Resolver = dns.resolver.Resolver()
|
#print 'Trying ' + guess + ' at ' + name_server
|
||||||
Resolver.timeout = 3
|
use_tcp = False
|
||||||
Resolver.lifetime = 3
|
response = None
|
||||||
Resolver.nameservers = [name_server]
|
failed = False
|
||||||
answer = None
|
record_type = ""
|
||||||
|
record_value = ""
|
||||||
|
query = dnslib.DNSRecord.question(guess, 'ANY')
|
||||||
try:
|
try:
|
||||||
# obtain the DNS reply in DIG format, convert to string, and split newlines into an array
|
response_q = query.send(name_server, 53, use_tcp, timeout = 3)
|
||||||
answer = str(Resolver.query(guess).response).split('\n')
|
if response_q:
|
||||||
|
response = dnslib.DNSRecord.parse(response_q)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print 'User exit'
|
||||||
|
exit()
|
||||||
except:
|
except:
|
||||||
return "ERROR", "e"
|
# probably socket timed out
|
||||||
|
print "ERROR - possible socket timeout when trying " + guess
|
||||||
'''
|
pass
|
||||||
the answer in DIG format looks like this
|
if response:
|
||||||
|
#print response
|
||||||
id 35423
|
rcode = dnslib.RCODE[response.header.rcode]
|
||||||
opcode QUERY
|
if rcode == 'NOERROR' or rcode == 'NXDOMAIN':
|
||||||
rcode NOERROR
|
# success, this is a valid subdomain
|
||||||
flags QR RD RA
|
for r in response.rr:
|
||||||
;QUESTION
|
rtype = None
|
||||||
myservice.example.com. IN A
|
try:
|
||||||
;ANSWER
|
rtype = str(dnslib.QTYPE[r.rtype])
|
||||||
myservice.example.com. 299 IN CNAME myservice.cloudservice.net.
|
except:
|
||||||
myservice.cloudservice.net. 9 IN A 500.600.700.800
|
rtype = str(r.rtype)
|
||||||
;AUTHORITY
|
#print rtype
|
||||||
;ADDITIONAL
|
|
||||||
|
if (rtype == 'CNAME'):
|
||||||
we grab the first line after ";ANSWER" - it's the first answer and the one we care about. May be the only answer depending on the specific host (like if it's an A record only 1 IP may be returned)
|
#print r.rdata
|
||||||
'''
|
record_type = 'CNAME'
|
||||||
answerline = ""
|
record_value = str(r.rdata)
|
||||||
for x in range(0, len(answer)): # for each line
|
elif (rtype == 'A' or rtype == 'AAAA'):
|
||||||
if answer[x] == ";ANSWER":
|
record_type = 'A'
|
||||||
answerline = answer[x + 1] # first answer
|
record_value = str(r.rdata)
|
||||||
break
|
else:
|
||||||
lineitems = answerline.split(' ')
|
print "ERROR - returned stats " + rcode + " when trying " + guess
|
||||||
host = lineitems[len(lineitems) - 1] # host is the last line
|
return record_type, record_value
|
||||||
# determine if this is a CNAME or A record. A records can be interesting to find vulnerable hosts and CNAME
|
|
||||||
# records can be interesting for subdomain takeover
|
|
||||||
for item in lineitems:
|
|
||||||
if item == 'CNAME':
|
|
||||||
host = host[:-1] # remove the trailing period
|
|
||||||
return "CNAME", host
|
|
||||||
elif item == 'A':
|
|
||||||
return "A", host
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1130,4 +1135,3 @@ if __name__ == "__main__":
|
||||||
#print ""
|
#print ""
|
||||||
# save the analysis to a file. Merge the arrays into one list for easier reading
|
# save the analysis to a file. Merge the arrays into one list for easier reading
|
||||||
write_file(analysis, ahosts + ["\n"] + cnames)
|
write_file(analysis, ahosts + ["\n"] + cnames)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue