improved dns modules, swithed to dnspython

pull/4/merge
lanjelot 12 years ago
parent 01121b3cc0
commit b89667a9a2

@ -48,8 +48,8 @@ Currently it supports the following modules:
- pgsql_login : Brute-force PostgreSQL - pgsql_login : Brute-force PostgreSQL
- vnc_login : Brute-force VNC - vnc_login : Brute-force VNC
- dns_forward : Forward lookup subdomains - dns_forward : Brute-force DNS
- dns_reverse : Reverse lookup subnets - dns_reverse : Brute-force DNS (reverse lookup subnets)
- snmp_login : Brute-force SNMPv1/2 and SNMPv3 - snmp_login : Brute-force SNMPv1/2 and SNMPv3
- unzip_pass : Brute-force the password of encrypted ZIP files - unzip_pass : Brute-force the password of encrypted ZIP files
@ -128,7 +128,7 @@ psycopg | PostgreSQL | http://initd.org/psycopg/
-------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------
pycrypto | VNC | http://www.dlitz.net/software/pycrypto/ | 2.3 | pycrypto | VNC | http://www.dlitz.net/software/pycrypto/ | 2.3 |
-------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------
pydns | DNS | http://pydns.sourceforge.net/ | 2.3.4 | dnspython | DNS | http://www.dnspython.org/ | 1.10.0 |
-------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------
pysnmp | SNMP | http://pysnmp.sourceforge.net/ | 4.2.1 | pysnmp | SNMP | http://pysnmp.sourceforge.net/ | 4.2.1 |
-------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------
@ -494,25 +494,29 @@ unzip_pass zipfile=path/to/file.zip password=FILE0 0=passwords.txt -x ignore:cod
}}} }}}
{{{ DNS {{{ DNS
* Forward lookup subdomains. * Brute-force subdomains.
(a) Ignore NXDOMAIN responses (rcode 3). (a) Ignore NXDOMAIN responses (rcode 3).
----------- -----------
dns_forward domain=FILE0.google.com 0=names.txt -x ignore:code=3 dns_forward name=FILE0.google.com 0=names.txt -x ignore:code=3
(a) (a)
* Forward lookup domain with all possible TLDs. * Brute-force domain with every possible TLDs.
----------- -----------
dns_forward domain=google.MOD0 0=TLD -x ignore:code=3 dns_forward name=google.MOD0 0=TLD -x ignore:code=3
* Foward lookup SRV records. * Brute-force SRV records.
----------- -----------
dns_forward domain=MOD0.microsoft.com 0=SRV qtype=SRV -x ignore:code=3 dns_forward name=MOD0.microsoft.com 0=SRV qtype=SRV -x ignore:code=3
* Reverse lookup several subnets. * Grab the version of several hosts.
-----------
dns_forward server=FILE0 0=hosts.txt name=version.bind qtype=txt qclass=ch
* Reverse lookup several networks.
(a) Ignore names that do not contain 'google.com'. (a) Ignore names that do not contain 'google.com'.
(b) Ignore generic PTR records. (b) Ignore generic PTR records.
----------- -----------
dns_reverse host=NET0 0=216.239.32.0-216.239.47.255,8.8.8.0/24 -x ignore:code=3 -x ignore:fgrep!=google.com -x ignore:fgrep=216-239- dns_reverse host=NET0 0=216.239.32.0-216.239.47.255,8.8.8.0/24 -x ignore:code=3 -x ignore:fgrep!=google.com -x ignore:fgrep=216-239-
(a) (b) (a) (b)
}}} }}}
{{{ SNMP {{{ SNMP
@ -554,7 +558,7 @@ TODO
---- ----
* SSL support for SMTP, MySQL, ... (use socat in the meantime) * SSL support for SMTP, MySQL, ... (use socat in the meantime)
* new option -e ns like in Medusa (not likely to be implemented due to design) * new option -e ns like in Medusa (not likely to be implemented due to design)
* replace PyDNS|paramiko|IPy with a better module (scapy|libssh2|... ?) * replace dnspython|paramiko|IPy with a better module (scapy|libssh2|... ?)
* rewrite itertools.product that eats too much memory when processing large wordlists * rewrite itertools.product that eats too much memory when processing large wordlists
''' '''
@ -585,6 +589,7 @@ from struct import unpack
import socket import socket
import subprocess import subprocess
import hashlib import hashlib
from collections import defaultdict
try: try:
# python3+ # python3+
from queue import Queue, Empty, Full from queue import Queue, Empty, Full
@ -2737,6 +2742,72 @@ class VNC_login:
# }}} # }}}
# DNS {{{ # DNS {{{
try:
import dns.rdatatype
import dns.message
import dns.query
import dns.reversename
except ImportError:
warnings.append('dnspython')
def dns_query(server, timeout, protocol, qname, qtype, qclass):
request = dns.message.make_query(qname, qtype, qclass)
if protocol == 'tcp':
response = dns.query.tcp(request, server, timeout=timeout, one_rr_per_rrset=True)
else:
response = dns.query.udp(request, server, timeout=timeout, one_rr_per_rrset=True)
if response.flags & dns.flags.TC:
response = dns.query.tcp(request, server, timeout=timeout, one_rr_per_rrset=True)
return response
def generate_tld():
gtld = [
'aero', 'arpa', 'asia', 'biz', 'cat', 'com', 'coop', 'edu',
'gov', 'info', 'int', 'jobs', 'mil', 'mobi', 'museum', 'name',
'net', 'org', 'pro', 'tel', 'travel']
cctld = [''.join(i) for i in product(*[ascii_lowercase]*2)]
tld = gtld + cctld
return tld, len(tld)
def generate_srv():
common = [
'_gc._tcp', '_kerberos._tcp', '_kerberos._udp', '_ldap._tcp',
'_test._tcp', '_sips._tcp', '_sip._udp', '_sip._tcp', '_aix._tcp', '_aix._udp',
'_finger._tcp', '_ftp._tcp', '_http._tcp', '_nntp._tcp', '_telnet._tcp',
'_whois._tcp', '_h323cs._tcp', '_h323cs._udp', '_h323be._tcp', '_h323be._udp',
'_h323ls._tcp', '_h323ls._udp', '_sipinternal._tcp', '_sipinternaltls._tcp',
'_sip._tls', '_sipfederationtls._tcp', '_jabber._tcp', '_xmpp-server._tcp', '_xmpp-client._tcp',
'_imap.tcp', '_certificates._tcp', '_crls._tcp', '_pgpkeys._tcp', '_pgprevokations._tcp',
'_cmp._tcp', '_svcp._tcp', '_crl._tcp', '_ocsp._tcp', '_PKIXREP._tcp',
'_smtp._tcp', '_hkp._tcp', '_hkps._tcp', '_jabber._udp', '_xmpp-server._udp',
'_xmpp-client._udp', '_jabber-client._tcp', '_jabber-client._udp',
'_adsp._domainkey', '_policy._domainkey', '_domainkey', '_ldap._tcp.dc._msdcs', '_ldap._udp.dc._msdcs']
def distro():
import os
import re
files = ['/usr/share/nmap/nmap-protocols', '/usr/share/nmap/nmap-services', '/etc/protocols', '/etc/services']
ret = []
for f in files:
if not os.path.isfile(f):
logger.warn("File '%s' is missing, there will be less records to test" % f)
continue
for line in open(f):
match = re.match(r'([a-zA-Z0-9]+)\s', line)
if not match: continue
for w in re.split(r'[^a-z0-9]', match.group(1).strip().lower()):
ret.extend(['_%s.%s' % (w, i) for i in ('_tcp', '_udp')])
return ret
srv = set(common + distro())
return srv, len(srv)
class HostInfo: class HostInfo:
def __init__(self): def __init__(self):
self.name = set() self.name = set()
@ -2757,61 +2828,99 @@ class HostInfo:
return line return line
class Controller_DNS(Controller): class Controller_DNS(Controller):
hostmap = {} records = defaultdict(list)
hostmap = defaultdict(HostInfo)
# show_final {{{ # show_final {{{
def show_final(self): def show_final(self):
''' Expected output:
Records -----
ftp.example.com. IN A 10.0.1.1
www.example.com. IN A 10.0.1.1
prod.example.com. IN CNAME www.example.com.
ipv6.example.com. IN AAAA dead:beef::
dev.example.com. IN A 10.0.1.2
svn.example.com. IN A 10.0.2.1
websrv1.example.com. IN CNAME prod.example.com.
blog.example.com. IN CNAME example.wordpress.com.
''' '''
1.2.3.4 ftp.example.com print('Records ' + '-'*42)
. www.example.com for name, infos in sorted(self.records.items()):
. www2.example.com for qclass, qtype, rdata in infos:
noip cms.example.com -> www.mistake.com print('%34s %8s %-8s %s' % (name, qclass, qtype, rdata))
''' Expected output:
Hostmap ------
ipv6.example.com dead:beef::
ftp.example.com 10.0.1.1
www.example.com 10.0.1.1
prod.example.com
websrv1.example.com
dev.example.com 10.0.1.2
svn.example.com 10.0.2.1
example.wordpress.com ?
blog.example.com
Domains ---------------------------
example.com 8
Networks --------------------------
dead:beef::
10.0.1.x
10.0.2.1
''' '''
ipmap = {} ipmap = defaultdict(HostInfo)
noips = set() noips = defaultdict(list)
''' '''
hostmap = { hostmap = {
'ftp.example.com': {'ip': ['1.2.3.4'], 'alias': []}, 'www.example.com': {'ip': ['10.0.1.1'], 'alias': ['prod.example.com']},
'www.example.com': {'ip': ['1.2.3.4'], 'alias': ['www2.example.com']}, 'ftp.example.com': {'ip': ['10.0.1.1'], 'alias': []},
'www.mistake.com': {'ip': [], 'alias': ['cms.example.com']}, ...} 'prod.example.com': {'ip': [], 'alias': ['websrv1.example.com']},
ipmap = {'1.2.3.4': {'name': ['www.example.com', 'ftp.example.com'], 'alias': ('www2.example.com')}} 'ipv6.example.com': {'ip': ['dead:beef::'], 'alias': []},
noips = ['cms.example.com -> www.mistake.com', ...] 'dev.example.com': {'ip': ['10.0.1.2'], 'alias': []},
'example.wordpress.com': {'ip': [], 'alias': ['blog.example.com']},
ipmap = {'10.0.1.1': {'name': ['www.example.com', 'ftp.example.com'], 'alias': ['prod.example.com', 'websrv1.example.com']}, ...
noips = {'example.wordpress.com': ['blog.example.com'],
''' '''
for name, hinfo in self.hostmap.items(): for name, hinfo in self.hostmap.items():
logger.debug('%s -> %s' % (name, hinfo)) for ip in hinfo.ip:
if not hinfo.ip: # orphan CNAME hostnames (with no IP address) may be still valid virtual hosts ip = IP(ip)
for alias in hinfo.alias: ipmap[ip].name.add(name)
noips.add('%s -> %s' % (alias, name)) ipmap[ip].alias.update(hinfo.alias)
else:
for ip in hinfo.ip: for name, hinfo in self.hostmap.items():
if ip not in ipmap: ipmap[ip] = HostInfo() if not hinfo.ip and hinfo.alias:
ipmap[ip].name.add(name) found = False
ipmap[ip].alias.update(hinfo.alias) for ip, v in ipmap.items():
if name in v.alias:
# pretty print for alias in hinfo.alias:
def pprint_info(key, infos): ipmap[ip].alias.add(alias)
first = True found = True
for info in infos:
if first: if not found: # orphan CNAME hostnames (with no IP address) may be still valid virtual hosts
print('%34s %s' % (info, key)) noips[name].extend(hinfo.alias)
first = False
else:
print('%34s %s' % (info, key))
print('Hostmap ' + '-'*42) print('Hostmap ' + '-'*42)
for ip, hinfo in sorted(ipmap.items()): for ip, hinfo in sorted(ipmap.items()):
pprint_info( ip, hinfo.name) for name in hinfo.name:
pprint_info('.', hinfo.alias) print('%34s %s' % (name, ip))
for alias in hinfo.alias:
pprint_info('noip', noips) print('%34s' % alias)
for k, v in noips.items():
print('%34s ?' % k)
for alias in v:
print('%34s' % alias)
print('Domains ' + '-'*42) print('Domains ' + '-'*42)
domains = {} domains = {}
networks = {}
for ip, hinfo in ipmap.items(): for ip, hinfo in ipmap.items():
for name in hinfo.name: for name in hinfo.name.union(hinfo.alias):
i = 1 if name.count('.') > 1 else 0 if name.count('.') > 1:
i = 1
else:
i = 0
d = '.'.join(name.split('.')[i:]) d = '.'.join(name.split('.')[i:])
if d not in domains: domains[d] = 0 if d not in domains: domains[d] = 0
domains[d] += 1 domains[d] += 1
@ -2831,67 +2940,37 @@ class Controller_DNS(Controller):
for net, ips in sorted(nets.items()): for net, ips in sorted(nets.items()):
if len(ips) == 1: if len(ips) == 1:
print(' '*10 + '%39s' % ips[0]) print(' '*34 + ' %s' % ips[0])
else: else:
print(' '*10 + '%37s.x' % '.'.join(str(net).split('.')[:-1])) print(' '*34 + ' %s.x' % '.'.join(str(net).split('.')[:-1]))
# }}} # }}}
def push_final(self, resp): def push_final(self, resp):
for name, hinfo in resp.hostmap.items(): if hasattr(resp, 'rrs'):
if name not in self.hostmap: for rr in resp.rrs:
self.hostmap[name] = hinfo name, qclass, qtype, data = rr
else:
self.hostmap[name].ip.update(hinfo.ip)
self.hostmap[name].alias.update(hinfo.alias)
def generate_tld(): info = (qclass, qtype, data)
gtld = [ if info not in self.records[name]:
'aero', 'arpa', 'asia', 'biz', 'cat', 'com', 'coop', 'edu', self.records[name].append(info)
'gov', 'info', 'int', 'jobs', 'mil', 'mobi', 'museum', 'name',
'net', 'org', 'pro', 'tel', 'travel']
cctld = [''.join(i) for i in product(*[ascii_lowercase]*2)] if not qclass == 'IN':
tld = gtld + cctld continue
return tld, len(tld)
def generate_srv(): if qtype == 'PTR':
common = [ data = data[:-1]
'_gc._tcp', '_kerberos._tcp', '_kerberos._udp', '_ldap._tcp', self.hostmap[data].ip.add(name)
'_test._tcp', '_sips._tcp', '_sip._udp', '_sip._tcp', '_aix._tcp', '_aix._udp',
'_finger._tcp', '_ftp._tcp', '_http._tcp', '_nntp._tcp', '_telnet._tcp',
'_whois._tcp', '_h323cs._tcp', '_h323cs._udp', '_h323be._tcp', '_h323be._udp',
'_h323ls._tcp', '_h323ls._udp', '_sipinternal._tcp', '_sipinternaltls._tcp',
'_sip._tls', '_sipfederationtls._tcp', '_jabber._tcp', '_xmpp-server._tcp', '_xmpp-client._tcp',
'_imap.tcp', '_certificates._tcp', '_crls._tcp', '_pgpkeys._tcp', '_pgprevokations._tcp',
'_cmp._tcp', '_svcp._tcp', '_crl._tcp', '_ocsp._tcp', '_PKIXREP._tcp',
'_smtp._tcp', '_hkp._tcp', '_hkps._tcp', '_jabber._udp', '_xmpp-server._udp',
'_xmpp-client._udp', '_jabber-client._tcp', '_jabber-client._udp',
'_adsp._domainkey', '_policy._domainkey', '_domainkey', '_ldap._tcp.dc._msdcs', '_ldap._udp.dc._msdcs']
def distro(): else:
import os if qtype in ('A', 'AAAA'):
import re name = name[:-1]
files = ['/usr/share/nmap/nmap-protocols', '/usr/share/nmap/nmap-services', '/etc/protocols', '/etc/services'] self.hostmap[name].ip.add(data)
ret = []
for f in files:
if not os.path.isfile(f):
logger.warn("File '%s' is missing, there will be less records to test" % f)
continue
for line in open(f):
match = re.match(r'([a-zA-Z0-9]+)\s', line)
if not match: continue
for w in re.split(r'[^a-z0-9]', match.group(1).strip().lower()):
ret.extend(['_%s.%s' % (w, i) for i in ('_tcp', '_udp')])
return ret
srv = set(common + distro()) elif qtype == 'CNAME':
return srv, len(srv) name, data = name[:-1], data[:-1]
self.hostmap[data].alias.add(name)
try:
from DNS import DnsRequest, DNSError
except ImportError:
warnings.append('pydns')
class DNS_reverse: class DNS_reverse:
'''Reverse lookup subnets''' '''Reverse lookup subnets'''
@ -2902,50 +2981,46 @@ class DNS_reverse:
] ]
available_options = ( available_options = (
('host', 'IP addresses to reverse'), ('host', 'IP addresses to reverse lookup'),
('server', 'name server to query (directly asking a zone authoritative NS may return more results) [8.8.8.8]'), ('server', 'name server to query (directly asking a zone authoritative NS may return more results) [8.8.8.8]'),
('timeout', 'seconds to wait for a DNS response [10]'), ('timeout', 'seconds to wait for a DNS response [5]'),
('protocol', 'send queries over udp or tcp [udp]'),
) )
available_actions = () available_actions = ()
Response = Response_Base Response = Response_Base
def execute(self, host, server='8.8.8.8', timeout='10'): def execute(self, host, server='8.8.8.8', timeout='5', protocol='udp'):
resolver = DnsRequest(qtype='PTR', server=server, timeout=int(timeout))
ip = IP(host)
ptr = ip.reverseName()
result = resolver.req(ptr.rstrip('.'))
hostnames = [ans['data'] for ans in result.answers]
hostmap = {} response = dns_query(server, int(timeout), protocol, dns.reversename.from_address(host), qtype='PTR', qclass='IN')
for n in hostnames:
if n not in hostmap: hostmap[n] = HostInfo()
hostmap[n].ip.add(ip)
code = result.header['rcode'] code = response.rcode()
status = result.header['status'] status = dns.rcode.to_text(code)
mesg = '%s %s' % (status, ', '.join(hostnames)) rrs = [[host, c, t, d] for _, _, c, t, d in [rr.to_text().split(' ', 4) for rr in response.answer]]
mesg = '%s %s' % (status, ''.join('[%s]' % ' '.join(rr) for rr in rrs))
resp = self.Response(code, mesg) resp = self.Response(code, mesg)
resp.hostmap = hostmap
resp.rrs = rrs
return resp return resp
class DNS_forward: class DNS_forward:
'''Forward lookup subdomains''' '''Forward lookup names'''
usage_hints = [ usage_hints = [
"""%prog domain=FILE0.google.com 0=names.txt -x ignore:code=3""", """%prog name=FILE0.google.com 0=names.txt -x ignore:code=3""",
"""%prog domain=google.MOD0 0=TLD -x ignore:code=3""", """%prog name=google.MOD0 0=TLD -x ignore:code=3""",
"""%prog domain=MOD0.microsoft.com 0=SRV qtype=SRV -x ignore:code=3""", """%prog name=MOD0.microsoft.com 0=SRV qtype=SRV -x ignore:code=3""",
] ]
available_options = ( available_options = (
('domain', 'domains to lookup'), ('name', 'domain names to lookup'),
('server', 'name server to query (directly asking the zone authoritative NS may return more results) [8.8.8.8]'), ('server', 'name server to query (directly asking the zone authoritative NS may return more results) [8.8.8.8]'),
('timeout', 'seconds to wait for a DNS response [10]'), ('timeout', 'seconds to wait for a DNS response [5]'),
('qtype', 'comma-separated list of types to query [ANY,A,AAAA]'), ('protocol', 'send queries over udp or tcp [udp]'),
('qtype', 'type to query [ANY]'),
('qclass', 'class to query [IN]'),
) )
available_actions = () available_actions = ()
@ -2956,48 +3031,18 @@ class DNS_forward:
Response = Response_Base Response = Response_Base
def execute(self, domain, server='8.8.8.8', timeout='10', qtype='ANY,A,AAAA'): def execute(self, name, server='8.8.8.8', timeout='5', protocol='udp', qtype='ANY', qclass='IN'):
resolver = DnsRequest(server=server, timeout=int(timeout))
hostmap = {}
for qt in qtype.split(','):
result = resolver.req(domain, qtype=qt.strip())
for r in result.answers + result.additional + result.authority: response = dns_query(server, int(timeout), protocol, name, qtype=qtype, qclass=qclass)
t = r['typename']
n = r['name']
d = r['data']
if t not in ('A', 'AAAA', 'CNAME', 'DNAME', 'SRV'):
continue
if t == 'SRV':
_, _, _, d = d
if t in ('CNAME', 'DNAME', 'SRV'):
n, d = d, n
if n not in hostmap:
hostmap[n] = HostInfo()
if t == 'A': code = response.rcode()
hostmap[n].ip.add(IP(d)) status = dns.rcode.to_text(code)
rrs = [[n, c, t, d] for n, _, c, t, d in [rr.to_text().split(' ', 4) for rr in response.answer + response.additional + response.authority]]
elif t == 'AAAA':
hostmap[n].ip.add(IP(hexlify(d)))
elif t in ('CNAME', 'DNAME'):
hostmap[n].alias.add(d)
elif t == 'SRV':
hostmap[n].alias.add(d)
code = result.header['rcode']
status = result.header['status']
mesg = '%s %s' % (status, ' | '.join('%s / %s' % (k, v) for k, v in hostmap.items()))
mesg = '%s %s' % (status, ''.join('[%s]' % ' '.join(rr) for rr in rrs))
resp = self.Response(code, mesg) resp = self.Response(code, mesg)
resp.hostmap = hostmap
resp.rrs = rrs
return resp return resp
@ -3156,8 +3201,8 @@ modules = [
('pgsql_login', (Controller, Pgsql_login)), ('pgsql_login', (Controller, Pgsql_login)),
('vnc_login', (Controller, VNC_login)), ('vnc_login', (Controller, VNC_login)),
('dns_reverse', (Controller_DNS, DNS_reverse)),
('dns_forward', (Controller_DNS, DNS_forward)), ('dns_forward', (Controller_DNS, DNS_forward)),
('dns_reverse', (Controller_DNS, DNS_reverse)),
('snmp_login', (Controller, SNMP_login)), ('snmp_login', (Controller, SNMP_login)),
('unzip_pass', (Controller, Unzip_pass)), ('unzip_pass', (Controller, Unzip_pass)),
@ -3173,7 +3218,7 @@ dependencies = {
'mysql-python': [('mysql_login',), 'http://sourceforge.net/projects/mysql-python/'], 'mysql-python': [('mysql_login',), 'http://sourceforge.net/projects/mysql-python/'],
'psycopg': [('pgsql_login',), 'http://initd.org/psycopg/'], 'psycopg': [('pgsql_login',), 'http://initd.org/psycopg/'],
'pycrypto': [('vnc_login',), 'http://www.dlitz.net/software/pycrypto/'], 'pycrypto': [('vnc_login',), 'http://www.dlitz.net/software/pycrypto/'],
'pydns': [('dns_reverse', 'dns_forward'), 'http://pydns.sourceforge.net/'], 'dnspython': [('dns_reverse', 'dns_forward'), 'http://www.dnspython.org/'],
'IPy': [('dns_reverse', 'dns_forward'), 'https://github.com/haypo/python-ipy'], 'IPy': [('dns_reverse', 'dns_forward'), 'https://github.com/haypo/python-ipy'],
'pysnmp': [('snmp_login',), 'http://pysnmp.sf.net/'], 'pysnmp': [('snmp_login',), 'http://pysnmp.sf.net/'],
'unzip': [('unzip_pass',), 'http://www.info-zip.org/'], 'unzip': [('unzip_pass',), 'http://www.info-zip.org/'],

Loading…
Cancel
Save