Commits

Tim Tomes committed f11469b

restructured the modules tree for scalability and flow and made minor aesthetic changes to the framework.

Comments (0)

Files changed (65)

modules/auxiliary/builtwith.py

-import framework
-# unique to module
-import json
-import textwrap
-
-class Module(framework.module):
-
-    def __init__(self, params):
-        framework.module.__init__(self, params)
-        self.register_option('host', 'www.google.com', 'yes', 'target host')
-        self.register_option('verbose', self.goptions['verbose']['value'], 'yes', self.goptions['verbose']['desc'])
-        self.classify = 'passive'
-        self.info = {
-                     'Name': 'BuiltWith Server-side Enumerator',
-                     'Author': 'Tim Tomes (@LaNMaSteR53)',
-                     'Description': 'Leverages the BuiltWith API to identify server-side technologies.',
-                     'Comments': []
-                     }
-
-    def do_run(self, params):
-        if not self.validate_options(): return
-        # === begin here ===
-        self.builtwith()
-    
-    def builtwith(self):
-        host = self.options['host']['value']
-        verbose = self.options['verbose']['value']
-        key = self.manage_key('builtwith', 'BuiltWith API key')
-        if not key: return
-        url = ' http://api.builtwith.com/v1/api.json'
-        payload = {'key': key, 'lookup': host}
-        #import pdb;pdb.set_trace()
-        try: resp = self.request(url, payload=payload)
-        except KeyboardInterrupt:
-            print ''
-            return
-        except Exception as e:
-            self.error(e.__str__())
-            return
-        if resp.json == None:
-            self.error('Not a valid JSON response.')
-            return
-        if 'error' in resp.json:
-            self.error(resp.json['error'])
-            return
-        
-        if verbose:
-            for item in resp.json['Technologies']:
-                print self.ruler*50
-                for tag in item:
-                    self.output('%s: %s' % (tag, textwrap.fill(item[tag], 100, initial_indent='', subsequent_indent=self.spacer*2)))
-            print self.ruler*50
-
-        tags = ['web server', 'analytics', 'framework', 'server']
-        tdata = []
-        for item in resp.json['Technologies']:
-            tag = item['Tag']
-            if tag.lower() in tags:
-                name = item['Name']
-                tdata.append([tag.title(), name])
-
-        if len(tdata) > 0:
-            header = ['Tag', 'Name']
-            tdata.insert(0, ['Profile URL', resp.json['ProfileUrl']])
-            tdata.insert(0, header)
-            self.table(tdata, True)
-        else:
-            self.output('No results found')

modules/auxiliary/cache_snoop.py

-import framework
-# unique to module
-import os
-import dns
-import re
-
-class Module(framework.module):
-
-    def __init__(self, params):
-        framework.module.__init__(self, params)
-        self.register_option('nameserver', '', 'yes', 'ip address of target\'s nameserver')
-        self.register_option('domains', './data/av_domains.lst', 'yes', 'domain or list of domains to snoop for')
-        self.register_option('verbose', self.goptions['verbose']['value'], 'yes', self.goptions['verbose']['desc'])
-        self.classify = 'active'
-        self.info = {
-                     'Name': 'DNS Cache Snooper',
-                     'Author': 'thrapt (thrapt@gmail.com)',
-                     'Description': 'Uses the DNS cache snooping technique to check for visited domains',
-                     'Comments': [
-                                  'Nameserver must be in IP form.',
-                                  'Domains options: host.domain.com, <path/to/infile>',
-                                  'http://304geeks.blogspot.com/2013/01/dns-scraping-for-corporate-av-detection.html'
-                                 ]
-                     }
-
-    def do_run(self, params):
-        if not self.validate_options(): return
-        # === begin here ===
-        self.cachesnoop()
-
-    def cachesnoop(self):
-        verbose = self.options['verbose']['value']
-        domains = self.options['domains']['value']
-        nameserver = self.options['nameserver']['value']
-        
-        if os.path.exists(domains):
-            hosts = open(domains).read().split()
-        else:
-            hosts = [domains]
-        
-        self.output('Starting queries...')
-        
-        for host in hosts:
-            status = 'Not found'
-            # prepare our query
-            query = dns.message.make_query(host, dns.rdatatype.A, dns.rdataclass.IN)
-            # unset the Recurse flag 
-            query.flags ^= dns.flags.RD
-            try:
-                # try the query
-                response = dns.query.udp(query, nameserver)
-            except KeyboardInterrupt:
-                print ''
-                return
-            except dns.resolver.NXDOMAIN: status = 'Unknown'
-            except dns.resolver.NoAnswer: status = 'No answer'
-            except dns.exception.SyntaxError:
-                self.error('Nameserver must be in IP form.')
-                return
-            except: status = 'Error'
-
-            # searchs the response to find the answer
-            if len(response.answer) > 0:
-                status = 'Snooped!'
-                self.alert('%s => %s' % (host, status))
-            else:
-                if verbose: self.output('%s => %s' % (host, status))

modules/auxiliary/dnn_fcklinkgallery.py

-import framework
-# unique to module
-
-class Module(framework.module):
-
-    def __init__(self, params):
-        framework.module.__init__(self, params)
-        self.register_option('source', 'db', 'yes', 'source of module input')
-        self.register_option('verbose', self.goptions['verbose']['value'], 'yes', self.goptions['verbose']['desc'])
-        self.classify = 'active'
-        self.info = {
-                     'Name': 'Dot Net Nuke Remote File Upload Vulnerability Checker',
-                     'Author': 'Jay Turla (@shipcod3)',
-                     'Description': 'Checks the hosts for a DNN fcklinkgallery page which is possibly vulnerable to Remote File Upload.',
-                     'Comments': [
-                                  'Source options: [ db | <hostname> | ./path/to/file | query <sql> ]',
-                                  'http://www.exploit-db.com/exploits/12700/',
-                                  ]
-                     }
-
-    def do_run(self, params):
-        if not self.validate_options(): return
-        # === begin here ===
-        self.check_for_dnnfcklink()
-    
-    def check_for_dnnfcklink(self):
-        verbose = self.options['verbose']['value']
-        
-        hosts = self.get_source(self.options['source']['value'], 'SELECT DISTINCT host FROM hosts WHERE host IS NOT NULL ORDER BY host')
-        if not hosts: return
-
-        # check all hosts for DNN fcklinkgallery page
-        protocols = ['http', 'https']
-        cnt = 0
-        for host in hosts:
-            for proto in protocols:
-                url = '%s://%s/Providers/HtmlEditorProviders/Fck/fcklinkgallery.aspx' % (proto, host)
-                try:
-                    resp = self.request(url, redirect=False)
-                    code = resp.status_code
-                except KeyboardInterrupt:
-                    print ''
-                    return
-                except:
-                    code = 'Error'
-                if code == 200 and '> Link Gallery' in resp.text:
-                    self.alert('%s => %s. Possible DNN Fcklinkgallery page found!' % (url, code))
-                    cnt += 1
-                else:
-                    if verbose: self.output('%s => %s' % (url, code))
-        self.output('%d DNN Fcklinkgallery pages found' % (cnt))

modules/auxiliary/elmah.py

-import framework
-# unique to module
-
-class Module(framework.module):
-
-    def __init__(self, params):
-        framework.module.__init__(self, params)
-        self.register_option('source', 'db', 'yes', 'source of module input')
-        self.register_option('verbose', self.goptions['verbose']['value'], 'yes', self.goptions['verbose']['desc'])
-        self.classify = 'active'
-        self.info = {
-                     'Name': 'ELMAH Log Scanner',
-                     'Author': 'Tim Tomes (@LaNMaSteR53)',
-                     'Description': 'Checks hosts for a \'elmah.axd\' log page.',
-                     'Comments': [
-                                  'Source options: [ db | <hostname> | ./path/to/file | query <sql> ]',
-                                  'http://www.troyhunt.com/2012/01/aspnet-session-hijacking-with-google.html',
-                                  'Google dorks: inurl:elmah.axd ASPXAUTH',
-                                  '              inurl:elmah.axd intitle:"Error log for"'
-                                  ]
-                     }
-
-    def do_run(self, params):
-        if not self.validate_options(): return
-        # === begin here ===
-        self.check_for_elmah()
-    
-    def check_for_elmah(self):
-        verbose = self.options['verbose']['value']
-        
-        hosts = self.get_source(self.options['source']['value'], 'SELECT DISTINCT host FROM hosts WHERE host IS NOT NULL ORDER BY host')
-        if not hosts: return
-
-        # check all hosts for elmah page
-        protocols = ['http', 'https']
-        cnt = 0
-        for host in hosts:
-            for proto in protocols:
-                url = '%s://%s/elmah.axd' % (proto, host)
-                try:
-                    resp = self.request(url, redirect=False)
-                    code = resp.status_code
-                except KeyboardInterrupt:
-                    print ''
-                    return
-                except:
-                    code = 'Error'
-                if code == 200 and 'Error Log for' in resp.text:
-                    self.alert('%s => %s. Possible ELMAH log page found!' % (url, code))
-                    cnt += 1
-                else:
-                    if verbose: self.output('%s => %s' % (url, code))
-        self.output('%d ELMAH log pages found.' % (cnt))

modules/auxiliary/googli.py

-import framework
-# unique to module
-
-class Module(framework.module):
-
-    def __init__(self, params):
-        framework.module.__init__(self, params)
-        self.register_option('source', '21232f297a57a5a743894a0e4a801fc3', 'yes', 'source of module input')
-        self.register_option('verbose', self.goptions['verbose']['value'], 'yes', self.goptions['verbose']['desc'])
-        self.classify = 'support'
-        self.info = {
-                     'Name': 'Goog.li Hash Lookup',
-                     'Author': 'Tim Tomes (@LaNMaSteR53)',
-                     'Description': 'Uses the Goog.li hash database to perform a reverse hash lookup. This module updates the \'creds\' table of the database with the positive results.',
-                     'Comments': [
-                                  'Source options: [ db | <hash> | ./path/to/file | query <sql> ]',
-                                  'Hash types supported: MD4, MD5, MD5x2, MYSQL 3, MYSQL 4, MYSQL 5, RIPEMD160, NTLM, GOST, SHA1, SHA1x2, SHA224, SHA256, SHA384, SHA512, WHIRLPOOL'
-                                  ]
-                     }
-
-    def do_run(self, params):
-        if not self.validate_options(): return
-        # === begin here ===
-        self.googli()
-    
-    def googli(self):
-        verbose = self.options['verbose']['value']
-        
-        hashes = self.get_source(self.options['source']['value'], 'SELECT DISTINCT hash FROM creds WHERE hash IS NOT NULL and password IS NULL')
-        if not hashes: return
-
-        # lookup each hash
-        url = 'https://goog.li'
-        for hashstr in hashes:
-            payload = {'j': hashstr}
-            try: resp = self.request(url, payload=payload)
-            except KeyboardInterrupt:
-                print ''
-                break
-            except Exception as e:
-                self.error(e.__str__())
-                continue
-            if resp.json: jsonobj = resp.json
-            else:
-                self.error('Invalid JSON returned from the API for \'%s\'.' % (account))
-                continue
-            plaintext = False
-            if jsonobj['found'] == "true":
-                plaintext = jsonobj['hashes'][0]["plaintext"]
-                hashtype = jsonobj['type'].upper()
-            if plaintext:
-                self.alert('%s (%s) => %s' % (hashstr, hashtype, plaintext))
-                self.query('UPDATE creds SET password="%s", type="%s" WHERE hash="%s"' % (plaintext, hashtype, hashstr))
-            else:
-                if verbose: self.output('Value not found for hash: %s' % (hashstr))

modules/auxiliary/mangle.py

-import framework
-# unique to module
-
-class Module(framework.module):
-
-    def __init__(self, params):
-        framework.module.__init__(self, params)
-        self.register_option('domain', self.goptions['domain']['value'], 'no', 'target email domain')
-        self.register_option('pattern', '<fn>.<ln>', 'yes', 'pattern applied to mangle first and last name')
-        self.register_option('max-length', 30, 'yes', 'maximum length of email address prefix or username')
-        self.classify = 'support'
-        self.info = {
-                     'Name': 'Contact Name Mangler',
-                     'Author': 'Tim Tomes (@LaNMaSteR53)',
-                     'Description': 'Applies a mangle pattern to all of the contacts stored in the database, creating email addresses or usernames for each harvested contact. This module updates the \'contacts\' table of the database with the results.',
-                     'Comments': [
-                                  'Pattern options: <fi>,<fn>,<li>,<ln>',
-                                  'Example:         <fi>.<ln> => j.doe@domain.com',
-                                  'Note: Omit the \'domain\' option to create usernames'
-                                  ]
-                     }
-
-    def do_run(self, params):
-        if not self.validate_options(): return
-        # === begin here ===
-        self.mutate_contacts()
-
-    def mutate_contacts(self):
-        domain = self.options['domain']['value']
-        pattern = self.options['pattern']['value']
-        max = self.options['max-length']['value']
-        contacts = self.query('SELECT rowid, fname, lname FROM contacts ORDER BY fname')
-        if len(contacts) == 0:
-            self.error('No contacts in the database.')
-            return
-        for contact in contacts:
-            row = contact[0]
-            fname = contact[1]
-            lname = contact[2]
-            fn = fname.lower()
-            fi = fname[:1].lower()
-            ln = lname.lower()
-            li = lname[:1].lower()
-            email = pattern
-            try:
-                email = email.replace('<fn>', fn)
-                email = email.replace('<fi>', fi)
-                email = email.replace('<ln>', ln)
-                email = email.replace('<li>', li)
-            except:
-                self.error('Invalid Mutation Pattern \'%s\'.' % (type))
-                break
-            email = email[:max]
-            if domain: email = '%s@%s' % (email, domain)
-            self.output('%s %s => %s' % (fname, lname, email))
-            self.query('UPDATE contacts SET email="%s" WHERE rowid="%s"' % (email, row))

modules/auxiliary/namechk.py

-import framework
-# unique to module
-import re
-from hashlib import sha1
-from hmac import new as hmac
-import socket
-
-class Module(framework.module):
-
-    def __init__(self, params):
-        framework.module.__init__(self, params)
-        self.register_option('username', 'lanmaster53', 'yes', 'username to validate')
-        self.register_option('verbose', self.goptions['verbose']['value'], 'yes', self.goptions['verbose']['desc'])
-        self.classify = 'passive'
-        self.info = {
-                     'Name': 'NameChk.com Username Validator',
-                     'Author': 'Tim Tomes (@LaNMaSteR53) and thrapt (thrapt@gmail.com)',
-                     'Description': 'Leverages NameChk.com to validate the existance of usernames at specific web sites.',
-                     'Comments': [
-                                  'Note: The global socket_timeout may need to be increased to support slower sites.']
-                     }
-
-    def do_run(self, params):
-        if not self.validate_options(): return
-        # === begin here ===
-        self.namechk()
-    
-    def namechk(self):
-        username = self.options['username']['value']
-        verbose = self.options['verbose']['value']
-
-        # retrive list of sites
-        url = 'http://namechk.com/Content/sites.min.js'
-        try: resp = self.request(url)
-        except KeyboardInterrupt:
-            print ''
-            return
-        except Exception as e:
-            self.error(e.__str__())
-            return
-        
-        # extract sites info from the js file
-        pattern = 'n:"(.+?)",r:\d+,i:(\d+)'
-        sites = re.findall(pattern, resp.text)
-
-        # output table of sites info
-        if verbose:
-            tdata = [['Code', 'Name']]
-            for site in sites:
-                tdata.append([site[1], site[0]])
-            self.table(tdata, True)
-
-        # retrive statuses
-        key = "shhh it's :] super secret"
-        url = 'http://namechk.com/check'
-
-        # this header is required
-        headers = {'X-Requested-With': 'XMLHttpRequest'}
-
-        status_dict = {
-                       '1': 'Available',
-                       '2': 'User Exists!',
-                       '3': 'Unknown',
-                       '4': 'Indefinite'
-                       }
-
-        for site in sites:
-            i = site[1]
-            name = site[0]
-            # build the hmac payload
-            message = "POST&%s?i=%s&u=%s" % (url, i, username)
-            b64_hmac_sha1 = '%s' % hmac(key, message, sha1).digest().encode('base64')[:-1]
-            payload = {'i': i, 'u': username, 'o_0': b64_hmac_sha1}
-            # build and send the request
-            try: resp = self.request(url, method='POST', headers=headers, payload=payload)
-            except KeyboardInterrupt:
-                print ''
-                return
-            except Exception as e:
-                self.error('%s: %s' % (name, e.__str__()))
-                continue
-            x = resp.text
-            if int(x) > 0:
-                status = status_dict[x]
-                if int(x) == 2:
-                    self.alert('%s: %s' % (name, status))
-                else:
-                    if verbose: self.output('%s: %s' % (name, status))
-            else:
-                self.error('%s: %s' % (name, 'Error'))

modules/auxiliary/netcraft_history.py

-import framework
-# unique to module
-import re
-import hashlib
-import urllib
-import time
-import random
-
-class Module(framework.module):
-
-    def __init__(self, params):
-        framework.module.__init__(self, params)
-        self.register_option('source', 'db', 'yes', 'source of module input')
-        self.register_option('verbose', self.goptions['verbose']['value'], 'yes', self.goptions['verbose']['desc'])
-        self.classify = 'passive'
-        self.info = {
-                     'Name': 'Hosting History',
-                     'Author': 'thrapt (thrapt@gmail.com)',
-                     'Description': 'Checks Netcraft for the Hosting History of given target.',
-                     'Comments': [
-                                  'Source options: [ db | <hostname> | ./path/to/file | query <sql> ]'
-                                 ]
-                     }
-
-    def do_run(self, params):
-        if not self.validate_options(): return
-        # === begin here ===
-        self.netcraft()
-
-    def netcraft(self):
-        verbose = self.options['verbose']['value']
-        cookies = {}
-
-        hosts = self.get_source(self.options['source']['value'], 'SELECT DISTINCT host FROM hosts WHERE host IS NOT NULL ORDER BY host')
-        if not hosts: return
-
-        for host in hosts:
-            url = 'http://uptime.netcraft.com/up/graph?site=%s' % (host)
-            if verbose: self.output('URL: %s' % url)
-            try: resp = self.request(url, cookies=cookies)
-            except KeyboardInterrupt:
-                print ''
-                return
-            except Exception as e:
-                self.error(e.__str__())
-            if not resp: break
-
-            if 'set-cookie' in resp.headers:
-                # we have a cookie to set!
-                if verbose: self.output('Setting cookie...')
-                cookie = resp.headers['set-cookie']
-                # this was taken from the netcraft page's JavaScript, no need to use big parsers just for that
-                # grab the cookie sent by the server, hash it and send the response
-                challenge_token = (cookie.split('=')[1].split(';')[0])
-                response = hashlib.sha1(urllib.unquote(challenge_token))
-                cookies = {
-                            'netcraft_js_verification_response': '%s' % response.hexdigest(),
-                            'netcraft_js_verification_challenge': '%s' % challenge_token,
-                            'path' : '/'
-                          }
-
-                # Now we can request the page again
-                if verbose: self.output('URL: %s' % url)
-                try: resp = self.request(url, cookies=cookies)
-                except KeyboardInterrupt:
-                    print ''
-                except Exception as e:
-                    self.error(e.__str__())
-                if not resp: break
-
-            content = resp.text
-
-            # instantiate history list
-            history = []
-            rows = re.findall(r'<tr class="T\wtr\d*">(?:\s|.)+?<\/div>', content)
-            for row in rows:
-                cell = re.findall(r'>(.*?)<', row)
-                raw  = [cell[0], cell[2], cell[4], cell[6], cell[8]]
-                history.append([x.strip() for x in raw])
-
-            if len(history) > 0:
-                header = ['OS', 'Server', 'Last Changed', 'IP Address', 'Owner']
-                history.insert(0, header)
-                self.table(history, True)
-            else:
-                self.output('No results found')
-
-            if len(hosts) > 1:
-                # sleep script to avoid lock-out
-                if verbose: self.output('Sleeping to Avoid Lock-out...')
-                try: time.sleep(random.randint(5,15))
-                except KeyboardInterrupt:
-                    print ''
-                    break

modules/auxiliary/noisette.py

-import framework
-# unique to module
-import re
-from xml.dom.minidom import parseString
-
-class Module(framework.module):
-
-    def __init__(self, params):
-        framework.module.__init__(self, params)
-        self.register_option('source', '21232f297a57a5a743894a0e4a801fc3', 'yes', 'source of module input')
-        self.register_option('verbose', self.goptions['verbose']['value'], 'yes', self.goptions['verbose']['desc'])
-        self.classify = 'support'
-        self.info = {
-                     'Name': 'Noisette MD5 Hash Lookup',
-                     'Author': 'Tim Tomes (@LaNMaSteR53)',
-                     'Description': 'Uses the Noisette.ch hash database to perform a reverse hash lookup. This module updates the \'creds\' table of the database with the positive results.',
-                     'Comments': [
-                                  'Source options: [ db | <hash> | ./path/to/file | query <sql> ]',
-                                  'Hash types supported: MD5'
-                                  ]
-                     }
-
-    def do_run(self, params):
-        if not self.validate_options(): return
-        # === begin here ===
-        self.noisette()
-    
-    def noisette(self):
-        verbose = self.options['verbose']['value']
-        
-        hashes = self.get_source(self.options['source']['value'], 'SELECT DISTINCT hash FROM creds WHERE hash IS NOT NULL and password IS NULL')
-        if not hashes: return
-
-        # lookup each hash
-        url = 'http://md5.noisette.ch/md5.php'
-        for hashstr in hashes:
-            payload = {'hash': hashstr}
-            try: resp = self.request(url, payload=payload)
-            except KeyboardInterrupt:
-                print ''
-                return
-            except Exception as e:
-                self.error(e.__str__())
-                continue
-            dom = parseString(resp.text)
-            plaintext = False
-            hashtype = "MD5"
-            nodes = dom.getElementsByTagName('string')
-            if len(nodes) > 0:
-                plaintext = nodes[0].firstChild.wholeText
-            if plaintext:
-                self.alert('%s (%s) => %s' % (hashstr, hashtype, plaintext))
-                self.query('UPDATE creds SET password="%s", type="%s" WHERE hash="%s"' % (plaintext, hashtype, hashstr))
-            else:
-                if verbose: self.output('Value not found for hash: %s' % (hashstr))

modules/auxiliary/phpinfo.py

-import framework
-# unique to module
-
-class Module(framework.module):
-
-    def __init__(self, params):
-        framework.module.__init__(self, params)
-        self.register_option('source', 'db', 'yes', 'source of module input')
-        self.register_option('verbose', self.goptions['verbose']['value'], 'yes', self.goptions['verbose']['desc'])
-        self.classify = 'active'
-        self.info = {
-                     'Name': 'phpinfo() Page Checker',
-                     'Author': 'Jay Turla (@shipcod3)',
-                     'Description': 'Checks the hosts for phpinfo() page which outputs information about PHP configuration',
-                     'Comments': [
-                                  'Source options: [ db | <hostname> | ./path/to/file | query <sql> ]',
-                                  'Reference: http://php.net/manual/en/function.phpinfo.php',
-                                  'Google Dorks:',
-                                  '%sinurl:phpinfo.php ext:php' % (self.spacer),
-                                  '%sinurl:test.php intitle:phpinfo() ext:php' % (self.spacer)
-                                  ]
-                     }
-
-    def do_run(self, params):
-        if not self.validate_options(): return
-        # === begin here ===
-        self.check_for_phpinfo()
-    
-    def check_for_phpinfo(self):
-        verbose = self.options['verbose']['value']
-        
-        hosts = self.get_source(self.options['source']['value'], 'SELECT DISTINCT host FROM hosts WHERE host IS NOT NULL ORDER BY host')
-        if not hosts: return
-
-        # check all hosts for phpinfo() page under phpinfo.php and test.php files
-        protocols = ['http', 'https']
-        files = [('phpinfo.php'), ('test.php')]
-        cnt = 0
-        for host in hosts:
-            for proto in protocols:
-                for filename in files:
-                    url = '%s://%s/%s' % (proto, host, filename)
-                    try:
-                        resp = self.request(url, redirect=False)
-                        code = resp.status_code
-                    except KeyboardInterrupt:
-                        print ''
-                        return
-                    except:
-                        code = 'Error'
-                    if code == 200 and 'phpinfo()' in resp.text:
-                        self.alert('%s => %s. phpinfo() page found!' % (url, code))
-                        cnt += 1
-                    else:
-                        if verbose: self.output('%s => %s' % (url, code))
-        self.output('%d phpinfo() pages found' % (cnt))

modules/auxiliary/pwnedlist.py

-import framework
-# unique to module
-import re
-
-class Module(framework.module):
-
-    def __init__(self, params):
-        framework.module.__init__(self, params)
-        self.register_option('source', 'db', 'yes', 'source of module input')
-        self.register_option('verbose', self.goptions['verbose']['value'], 'yes', self.goptions['verbose']['desc'])
-        self.classify = 'passive'
-        self.info = {
-                     'Name': 'PwnedList Validator',
-                     'Author': 'Tim Tomes (@LaNMaSteR53)',
-                     'Description': 'Leverages PwnedList.com to determine if email addresses are associated with leaked credentials. This module updates the \'creds\' table of the database with the positive results.',
-                     'Comments': [
-                                  'Source options: [ db | email.address@domain.com | ./path/to/file | query <sql> ]'
-                                  ]
-                     }
-
-    def do_run(self, params):
-        if not self.validate_options(): return
-        # === begin here ===
-        self.check_pwned()
-
-    def check_pwned(self):
-        verbose = self.options['verbose']['value']
-        
-        accounts = self.get_source(self.options['source']['value'], 'SELECT DISTINCT email FROM contacts WHERE email IS NOT NULL ORDER BY email')
-        if not accounts: return
-
-        # retrieve status
-        pattern = "class='query_result_footer'>... we found your email in our database a total of (\d+?) times. It was last seen on ([\d-]+?). Please read on. <div"
-        i, pwned = 0, 0
-        while i < len(accounts):
-            status = None
-            account = accounts[i].encode('utf-8')
-            url = 'http://pwnedlist.com/query'
-            payload = {'query_input' : account, 'query_input_hash' : 'empty', 'submit' : 'CHECK' }
-            try: resp = self.request(url, payload=payload, method='POST', redirect=False)
-            except KeyboardInterrupt:
-                print ''
-                break
-            except Exception as e:
-                self.error(e.__str__())
-                break
-            the_page = resp.text
-            if 'Gotcha!' in the_page:
-                self.error('Hm... Got a captcha.')
-                return
-            elif '>NOPE!<' in the_page:
-                status = 'safe'
-                if verbose: self.output('%s => %s.' % (account, status))
-            elif '>YES<' in the_page:
-                status = 'pwned'
-                m = re.search(pattern, the_page)
-                qty = m.group(1)
-                last = m.group(2)
-                self.alert('%s => %s! Seen %s times as recent as %s.' % (account, status, qty, last))
-                self.add_cred(account)
-                pwned += 1
-            else:
-                self.error('Response not understood.')
-                return
-            i += 1
-        self.output('%d/%d targets pwned.' % (pwned, i))

modules/auxiliary/resolve.py

-import framework
-# unique to module
-import dns.resolver
-
-class Module(framework.module):
-
-    def __init__(self, params):
-        framework.module.__init__(self, params)
-        self.register_option('nameserver', '8.8.8.8', 'yes', 'ip address of a valid nameserver')
-        self.classify = 'passive'
-        self.info = {
-                     'Name': 'Hostname Resolver',
-                     'Author': 'Tim Tomes (@LaNMaSteR53)',
-                     'Description': 'Resolves IP addresses to hosts. This module updates the \'hosts\' table of the database with the results.',
-                     'Comments': [
-                                  'Note: Nameserver must be in IP form.']
-                     }
-
-    def do_run(self, params):
-        if not self.validate_options(): return
-        # === begin here ===
-        self.resolve_hosts()
-    
-    def resolve_hosts(self):
-        q = dns.resolver.get_default_resolver()
-        q.nameservers = [self.options['nameserver']['value']]
-        hosts = self.query('SELECT rowid, host FROM hosts ORDER BY host')
-        for host in hosts:
-            row = host[0]
-            host = host[1]
-            try:
-                answers = q.query(host)
-                address = answers[0].address
-            except KeyboardInterrupt:
-                print ''
-                return
-            except dns.resolver.NXDOMAIN: address = 'Unknown'
-            except dns.resolver.NoAnswer: address = 'No answer'
-            except dns.exception.SyntaxError:
-                self.error('Nameserver must be in IP form.')
-                return
-            except: address = 'Error'
-            self.output('%s => %s' % (host, address))
-            self.query('UPDATE hosts SET address="%s" WHERE rowid="%s"' % (address, row))

modules/auxiliary/robots.py

-import framework
-# unique to module
-import gzip
-from StringIO import StringIO
-
-class Module(framework.module):
-
-    def __init__(self, params):
-        framework.module.__init__(self, params)
-        self.register_option('source', 'db', 'yes', 'source of module input')
-        self.register_option('verbose', self.goptions['verbose']['value'], 'yes', self.goptions['verbose']['desc'])
-        self.classify = 'active'
-        self.info = {
-                     'Name': 'robots.txt/sitemap.xml Finder',
-                     'Author': 'thrapt (thrapt@gmail.com)',
-                     'Description': 'Checks hosts for a robots.txt, sitemap.xml and sitemap.xml.gz file.',
-                     'Comments': [
-                                  'Source options: [ db | <hostname> | ./path/to/file | query <sql> ]'
-                                  ]
-                     }
-
-    def do_run(self, params):
-        if not self.validate_options(): return
-        # === begin here ===
-        self.check_for_status()
-    
-    def uncompress(self, data_gz):
-        inbuffer = StringIO(data_gz)
-        data_ct = ''
-        f = gzip.GzipFile(mode='rb', fileobj=inbuffer)
-        try:
-            data_ct = f.read()
-        except IOError:
-            pass
-        f.close()
-        return data_ct     
-    
-    def check_for_status(self):
-        verbose = self.options['verbose']['value']
-        
-        hosts = self.get_source(self.options['source']['value'], 'SELECT DISTINCT host FROM hosts WHERE host IS NOT NULL ORDER BY host')
-        if not hosts: return
-
-        # check all hosts for robots.txt, sitemap.xml and sitemap.xml.gz
-        protocols = ['http', 'https']
-        # filename and string used to verify the file
-        filetypes = [('robots.txt', 'user-agent:'), ('sitemap.xml', '<?xml'), ('sitemap.xml.gz', '<?xml')]
-        cnt = 0
-        for host in hosts:
-            for proto in protocols:
-                for (filename, verify) in filetypes:
-                    url = '%s://%s/%s' % (proto, host, filename)
-                    try:
-                        resp = self.request(url, redirect=False)
-                        code = resp.status_code
-                    except KeyboardInterrupt:
-                        print ''
-                        return
-                    except:
-                        code = 'Error'
-                    if code == 200:
-                        # uncompress if necessary
-                        text = ('.gz' in filename and self.uncompress(resp.text)) or resp.text
-                        # check for file type since many custom 404s are returned as 200s 
-                        if (verify in text.lower()):
-                            self.alert('%s => %s. %s found!' % (url, code, filename))
-                            cnt += 1
-                        else:
-                            self.output('%s => %s. %s invalid!' % (url, code, filename))
-                    else:
-                        if verbose: self.output('%s => %s' % (url, code))
-        
-        self.output('%d files found.' % (cnt))

modules/auxiliary/server_enum.py

-import framework
-# unique to module
-from random import choice
-import textwrap
-
-class Module(framework.module):
-
-    def __init__(self, params):
-        framework.module.__init__(self, params)
-        self.register_option('host', 'www.google.com', 'yes', 'target host')
-        self.register_option('protocol', 'http', 'yes', 'protocol of the host: http, https')
-        self.register_option('redirect', False, 'yes', 'follow redirects')
-        self.register_option('verbose', self.goptions['verbose']['value'], 'yes', self.goptions['verbose']['desc'])
-        self.classify = 'active'
-        self.info = {
-                     'Name': 'Server Side Enumerator',
-                     'Author': 'Tim Tomes (@LaNMaSteR53) and Kenan Abdullahoglu (@kyabd)',
-                     'Description': 'Analyzes response headers, cookies, and errors to determine which server-side technology is being used (PHP, .NET, JSP, CF, etc.).',
-                     'Comments': []
-                     }
-
-    def do_run(self, params):
-        if not self.validate_options(): return
-        # === begin here ===
-        self.enumerate()
-
-    def lookup(self, db, name, value):
-        matches = []
-        for platform in db:
-            for i in db[platform][name.lower()]:
-                if i.lower() in value.lower():
-                    matches.append(platform)
-        if matches:
-            return ', '.join(list(set(matches)))
-        return None
-
-    def enumerate(self):
-        host = self.options['host']['value']
-        protocol = self.options['protocol']['value']
-        redirect = self.options['redirect']['value']
-        verbose = self.options['verbose']['value']
-
-        # dictionaries of search terms for each platform and check
-        # dictionary for server side scripting technologies
-        ss_script = {
-            'PHP': {
-                'ext':     ['php'],
-                'cookie':  ['phpsession', 'phpsessid'],
-                'powered': ['php']
-                },
-            'ASP/.NET': {
-                'ext':     ['asp', 'aspx'],
-                'cookie':  ['aspsessionid', 'asp.net_sessionid', 'aspsessid'],
-                'powered': ['asp', 'asp.net', 'vb.net']
-                },
-            'ColdFusion': {
-                'ext':     ['cfc', 'cfm', 'cfml', 'dbm', 'dbml'],
-                'cookie':  ['cfid', 'cftoken', 'cfglobals'],
-                'powered': ['coldfusion', 'cfmx']
-                },
-            'Java/J2E': {
-                'ext':     ['jsp', 'jspx', 'jspf'],
-                'cookie':  ['jsessionid', 'jsessid'],
-                'powered': ['jsp', 'jboss']
-                },
-            'Rails': {
-                'ext':     [],
-                'cookie':  [],
-                'powered': ['rails']
-                }
-            }
-        # dictionary for server side server technologies
-        ss_server = {
-            'Apache': {
-                'server': ['apache'],
-                'error':  ['apache']
-                },
-            'IIS': {
-                'server': ['iis'],
-                'error':  ['iis']
-                },
-            'Nginx': {
-                'server': ['nginx'],
-                'error':  ['nginx']
-                },
-            'Python': {
-                'server': ['python'],
-                'error':  ['python', 'django']
-                },
-            'Ruby': {
-                'server': ['ruby'],
-                'error':  ['ruby']
-                }
-            }
-
-        # make request
-        url = '%s://%s' % (protocol, host)
-        try: resp = self.request(url, redirect=redirect)
-        except KeyboardInterrupt:
-            print ''
-            return
-        except Exception as e:
-            self.error(e.__str__())
-            return
-        
-        if verbose:
-            print 'START'.center(50, self.ruler)
-            self.output('ORIG_URL: %s' % (url))
-            self.output('DEST_URL: %s' % (resp.url))
-            print 'HEADERS'.center(50, self.ruler)
-            for header in resp.headers:
-                self.output('%s: %s' % (header.upper(), textwrap.fill(resp.headers[header], 100, initial_indent='', subsequent_indent=self.spacer*2)))
-            print 'COOKIES'.center(50, self.ruler)
-            for cookie in resp.cookies:
-                self.output('%s: %s' % (cookie.name.upper(), textwrap.fill(cookie.value, 100, initial_indent='', subsequent_indent=self.spacer*2)))
-            print 'END'.center(50, self.ruler)
-
-        tdata = []
-        # check for redirect
-        if url != resp.url:
-            if verbose: tdata.append(['URL', url, '--'])
-            if verbose: tdata.append(['REDIR', resp.url, '--'])
-        else:
-            if verbose: tdata.append(['URL', resp.url, '--'])
-
-        # check file ext
-        from urlparse import urlparse
-        path = urlparse(resp.url).path
-        if path:
-            filename = path.split('/')[-1]
-            if verbose: tdata.append(['FILENAME', filename, '--'])
-            if '.' in filename:
-                ext = filename.split('.')[-1]
-                platform = self.lookup(ss_script, 'ext', ext)
-                if not platform: platform = 'Unknown'
-                tdata.append(['FILETYPE', ext, platform])
-
-        # check headers
-        for header in resp.headers:
-            if header.lower() == 'location': platform = '--'
-            elif header.lower() == 'server': platform = self.lookup(ss_server, 'server', resp.headers[header])
-            elif header.lower() == 'x-powered-by': platform = self.lookup(ss_script, 'powered', resp.headers[header])
-            else: continue
-            # all ifs will end here if successful
-            if not platform: platform = 'Unknown'
-            tdata.append([header.upper(), resp.headers[header], platform])
-
-        # check cookies
-        for cookie in resp.cookies:
-            platform = self.lookup(ss_script, 'cookie', cookie.name)
-            if platform:
-                tdata.append(['COOKIE', cookie.name, platform])
-            elif 'sess' in cookie.name.lower(): 
-                tdata.append(['COOKIE', cookie.name, 'Unknown'])
-
-        # check error
-        seq = ''.join(map(chr, range(97, 123)))
-        pre = ''.join(choice(seq) for x in range(10))
-        suf = ''.join(choice(seq) for x in range(3))
-        bad_file = '%s.%s' % (pre, suf)
-        bad_url = '%s/%s' % (url, bad_file)
-        try:
-            resp = self.request(bad_url, redirect=False)
-            platform = self.lookup(ss_server, 'error', resp.text)
-            if not platform: platform = 'Unknown'
-            tdata.append(['ERROR', '%s (/%s)' % (str(resp.status_code), bad_file), platform])
-        except KeyboardInterrupt:
-            print ''
-            return
-        except Exception as e:
-            self.error(e.__str__())
-
-        self.table(tdata)

modules/auxiliary/server_status.py

-import framework
-# unique to module
-
-class Module(framework.module):
-
-    def __init__(self, params):
-        framework.module.__init__(self, params)
-        self.register_option('source', 'db', 'yes', 'source of module input')
-        self.register_option('verbose', self.goptions['verbose']['value'], 'yes', self.goptions['verbose']['desc'])
-        self.classify = 'active'
-        self.info = {
-                     'Name': 'Apache Server-Status Page Scanner',
-                     'Author': 'Tim Tomes (@LaNMaSteR53)',
-                     'Description': 'Checks hosts for a \'server-status\' page.',
-                     'Comments': [
-                                  'Source options: [ db | <hostname> | ./path/to/file | query <sql> ]',
-                                  'http://blog.sucuri.net/2012/10/popular-sites-with-apache-server-status-enabled.html',
-                                  'http://httpd.apache.org/docs/2.2/mod/mod_status.html',
-                                  'Google dork: intitle:"Apache Status" inurl:"server-status"'
-                                  ]
-                     }
-
-    def do_run(self, params):
-        if not self.validate_options(): return
-        # === begin here ===
-        self.check_for_status()
-    
-    def check_for_status(self):
-        verbose = self.options['verbose']['value']
-        
-        hosts = self.get_source(self.options['source']['value'], 'SELECT DISTINCT host FROM hosts WHERE host IS NOT NULL ORDER BY host')
-        if not hosts: return
-
-        # check all hosts for server-status pages
-        protocols = ['http', 'https']
-        cnt = 0
-        for host in hosts:
-            for proto in protocols:
-                url = '%s://%s/server-status/' % (proto, host)
-                try:
-                    resp = self.request(url, redirect=False)
-                    code = resp.status_code
-                except KeyboardInterrupt:
-                    print ''
-                    return
-                except:
-                    code = 'Error'
-                if code == 200 and '>Apache Status<' in resp.text:
-                    self.alert('%s => %s. Possible Apache Status page found!' % (url, code))
-                    cnt += 1
-                else:
-                    if verbose: self.output('%s => %s' % (url, code))
-        self.output('%d Server Status pages found.' % (cnt))

modules/auxiliary/whatweb.py

-import framework
-# unique to module
-import re
-
-class Module(framework.module):
-
-    def __init__(self, params):
-        framework.module.__init__(self, params)
-        self.register_option('source', 'db', 'yes', 'source of module input')
-        self.register_option('verbose', self.goptions['verbose']['value'], 'yes', self.goptions['verbose']['desc'])
-        self.classify = 'passive'
-        self.info = {
-                     'Name': 'WhatWeb Web Technologies scan',
-                     'Author': 'thrapt (thrapt@gmail.com)',
-                     'Description': 'Leverages WhatWeb.net to recognise web technologies being used.',
-                     'Comments': [
-                                  'Source options: [ db | <hostname> | ./path/to/file | query <sql> ]'
-                                 ]
-                     }
-
-    def do_run(self, params):
-        if not self.validate_options(): return
-        # === begin here ===
-        self.whatweb()
-
-    def whatweb(self):
-        verbose = self.options['verbose']['value']
-
-        # handle sources
-        hosts = self.get_source(self.options['source']['value'], 'SELECT DISTINCT host FROM hosts WHERE host IS NOT NULL ORDER BY host')
-        if not hosts: return
-        
-        for host in hosts:
-            url = 'http://whatweb.net/whatweb.php'
-            payload = {'target': host }
-            
-            try: resp = self.request(url, method='POST', payload=payload)
-            except KeyboardInterrupt:
-                print ''
-                return
-            except Exception as e:
-                self.error(e.__str__())
-                return
-            content = resp.text
-            sites = content.strip().split('\n')
-            for site in sites:
-                host = site[:site.index(' [')]
-                site = site.split('] ', 1)[1]
-                items = site.split(', ')
-
-                values = [['Field', 'Value'], ['Host', host]]
-                for item in items:
-                    if '[' in item:
-                        split = item.split('[')
-                        key = split[0]
-                        value = split[1][:-1]
-                        values.append([key, value])
-                    else:
-                        values.append(['', item])
-
-                self.table(values, True)

modules/contacts/add_contact.py

-import framework
-# unique to module
-
-class Module(framework.module):
-
-    def __init__(self, params):
-        framework.module.__init__(self, params)
-        self.register_option('fname', None, 'yes', 'first name')
-        self.register_option('lname', None, 'yes', 'last name')
-        self.register_option('title', None, 'yes', 'job title')
-        self.register_option('email', None, 'no', 'email address')
-        self.classify = 'support'
-        self.info = {
-                     'Name': 'Contact Adder',
-                     'Author': 'Drumm',
-                     'Description': 'Manually adds a contact.',
-                     'Comments':[]
-                     }
-
-    # do not remove or rename
-    def do_run(self, params):
-        # do not remove or modify
-        if not self.validate_options(): return
-        # === begin module code here ===
-        if self.add_contact(self.options['fname']['value'], self.options['lname']['value'], self.options['title']['value'], self.options['email']['value']):
-            self.output('Contact successfully added.')

modules/contacts/jigsaw.py

-import framework
-# unique to module
-import urllib
-import re
-
-class Module(framework.module):
-
-    def __init__(self, params):
-        framework.module.__init__(self, params)
-        self.register_option('company', self.goptions['company']['value'], 'yes', self.goptions['company']['desc'])
-        self.register_option('keywords', '', 'no', 'additional keywords to identify company')
-        self.register_option('verbose', self.goptions['verbose']['value'], 'yes', self.goptions['verbose']['desc'])
-        self.classify = 'passive'
-        self.info = {
-                     'Name': 'Jigsaw Contact Enumerator',
-                     'Author': 'Tim Tomes (@LaNMaSteR53)',
-                     'Description': 'Harvests contacts from Jigsaw.com. This module updates the \'contacts\' table of the database with the results.',
-                     'Comments': []
-                     }
-
-    def do_run(self, params):
-        if not self.validate_options(): return
-        # === begin here ===
-        company_id = self.get_company_id()
-        if company_id:
-            contact_ids = self.get_contact_ids(company_id)
-            if contact_ids:
-                self.get_contacts(contact_ids)
-
-    def get_company_id(self):
-        self.output('Gathering Company IDs...')
-        company_name = self.options['company']['value']
-        all_companies = []
-        page_cnt = 1
-        params = '%s %s' % (company_name, self.options['keywords']['value'])
-        url = 'http://www.jigsaw.com/FreeTextSearchCompany.xhtml'
-        payload = {'opCode': 'search', 'freeText': params}
-        while True:
-            if self.options['verbose']['value']: self.output('Query: %s?%s' % (url, urllib.urlencode(payload)))
-            try: content = self.request(url, payload=payload).text
-            except KeyboardInterrupt:
-                print ''
-                break
-            except Exception as e:
-                self.error(e.__str__())
-                break
-            pattern = "href=./id(\d+?)/.+?>(.+?)<.+?\n.+?title='([\d,]+?)'"
-            companies = re.findall(pattern, content)
-            if not companies:
-                if not 'did not match any results' in content and page_cnt == 1:
-                    pattern_id = '<a href="/id(\d+?)/.+?">'
-                    if 'Create a wiki' in content:
-                        pattern_id = '<a href="/.+?companyId=(\d+?)">'
-                    pattern_name = 'pageTitle.>(.+?)<'
-                    pattern_cnt = 'contactCount.+>\s+([,\d]+)\sContacts'
-                    company_id = re.findall(pattern_id, content)[0]
-                    company_name = re.findall(pattern_name, content)[0]
-                    contact_cnt = re.findall(pattern_cnt, content)[0]
-                    all_companies.append((company_id, company_name, contact_cnt))
-                break
-            for company in companies:
-                all_companies.append((company[0], company[1], company[2]))
-            page_cnt += 1
-            payload['rpage'] = str(page_cnt)
-        if len(all_companies) == 0:
-            self.output('No Company Matches Found.')
-            return False
-        else:
-            for company in all_companies:
-                self.output('%s %s (%s contacts)' % (company[0], company[1], company[2]))
-            if len(all_companies) > 1:
-                try:
-                    company_id = raw_input('Enter Company ID from list [%s - %s]: ' % (all_companies[0][1], all_companies[0][0]))
-                    if not company_id: company_id = all_companies[0][0]
-                except KeyboardInterrupt:
-                    print ''
-                    company_id = ''
-            else:
-                company_id = all_companies[0][0]
-                self.output('Unique Company Match Found: %s' % company_id)
-            return company_id
-
-    def get_contact_ids(self, company_id):
-        self.output('Gathering Contact IDs for Company \'%s\'...' % (company_id))
-        page_cnt = 1
-        contact_ids = []
-        url = 'http://www.jigsaw.com/SearchContact.xhtml'
-        payload = {'companyId': company_id, 'opCode': 'showCompDir'}
-        while True:
-            payload['rpage'] = str(page_cnt)
-            if self.options['verbose']['value']: self.output('Query: %s?%s' % (url, urllib.urlencode(payload)))
-            try: content = self.request(url, payload=payload).text
-            except KeyboardInterrupt:
-                print ''
-                break
-            except Exception as e:
-                self.error(e.__str__())
-                break
-            pattern = "showContact\('(\d+?)'\)"
-            contacts = re.findall(pattern, content)
-            if not contacts: break
-            contact_ids.extend(contacts)
-            page_cnt += 1
-        return contact_ids
-
-    def get_contacts(self, contact_ids):
-        self.output('Gathering Contacts...')
-        cnt, tot = 0, 0
-        for contact_id in contact_ids:
-            url = 'http://www.jigsaw.com/BC.xhtml'
-            payload = {'contactId': contact_id}
-            try: content = self.request(url, payload=payload).text
-            except KeyboardInterrupt:
-                print ''
-                break
-            except Exception as e:
-                self.error(e.__str__())
-                break
-            if 'Contact Not Found' in content: continue
-            pattern = '<span id="firstname">(.+?)</span>.*?<span id="lastname">(.+?)</span>'
-            names = re.findall(pattern, content)
-            fname = self.unescape(names[0][0])
-            lname = self.unescape(names[0][1])
-            pattern = '<span id="title" title=".*?">(.*?)</span>'
-            title = self.unescape(re.findall(pattern, content)[0])
-            self.output('%s %s - %s' % (fname, lname, title))
-            tot += 1
-            cnt += self.add_contact(fname, lname, title)
-        self.output('%d total contacts found.' % (tot))
-        if cnt: self.alert('%d NEW contacts found!' % (cnt))

modules/contacts/linkedin_auth.py

-import framework
-# unique to module
-import oauth2 as oauth
-import httplib2
-import urlparse
-import webbrowser
-import urllib
-import json
-import re
-
-class Module(framework.module):
-
-    def __init__(self, params):
-        framework.module.__init__(self, params)
-        self.register_option('company', self.goptions['company']['value'], 'yes', self.goptions['company']['desc'])
-        self.register_option('verbose', self.goptions['verbose']['value'], 'yes', self.goptions['verbose']['desc'])
-        self.classify = 'passive'
-        self.info = {
-                     'Name': 'LinkedIn Authenticated Contact Enumerator',
-                     'Author': 'Tim Tomes (@LaNMaSteR53)',
-                     'Description': 'Harvests contacts from LinkedIn.com using an authenticated connections network. This module updates the \'contacts\' table of the database with the results.',
-                     'Comments': []
-                     }
-
-    def do_run(self, params):
-        if not self.validate_options(): return
-        # === begin here ===
-        consumer_key = self.manage_key('linkedin_key', 'LinkedIn API Key')
-        if not consumer_key: return
-        consumer_secret = self.manage_key('linkedin_secret', 'LinkedIn Secret Key') 
-        if not consumer_secret: return
-        # Use API key and secret to instantiate consumer object
-        self.consumer = oauth.Consumer(consumer_key, consumer_secret)
-        self.access_token = {'oauth_token': self.get_key_from_db('linkedin_token'),'oauth_token_secret': self.get_key_from_db('linkedin_token_secret')}
-        if not self.access_token['oauth_token']: self.get_access_tokens()
-        if self.access_token['oauth_token']: self.get_contacts()
-
-    def get_access_tokens(self):
-        client = oauth.Client(self.consumer)
-        request_token_url = 'https://api.linkedin.com/uas/oauth/requestToken?scope=r_basicprofile+r_network'
-        try: resp, content = client.request(request_token_url, "POST")
-        except KeyboardInterrupt:
-            print ''
-            return None
-        if resp['status'] != '200':
-            raise Exception(self.error('Error: Invalid Response %s.' % resp['status']))
-        request_token = dict(urlparse.parse_qsl(content))
-        base_authorize_url = 'https://api.linkedin.com/uas/oauth/authorize'
-        authorize_url = "%s?oauth_token=%s" % (base_authorize_url, request_token['oauth_token'])
-        self.output('Go to the following link in your browser and enter the pin below:') 
-        self.output(authorize_url)
-        w = webbrowser.get()
-        w.open(authorize_url)
-        oauth_verifier = ''
-        try: oauth_verifier = raw_input('Enter PIN: ')
-        except KeyboardInterrupt: print ''
-        if not oauth_verifier: return None
-        access_token_url = 'https://api.linkedin.com/uas/oauth/accessToken'
-        token = oauth.Token(request_token['oauth_token'], request_token['oauth_token_secret'])
-        token.set_verifier(oauth_verifier)
-        client = oauth.Client(self.consumer, token)
-        try: resp, content = client.request(access_token_url, "POST")
-        except KeyboardInterrupt:
-            print ''
-            return None
-        self.access_token = dict(urlparse.parse_qsl(content))
-        self.add_key_to_db('linkedin_token', self.access_token['oauth_token'])
-        self.add_key_to_db('linkedin_token_secret', self.access_token['oauth_token_secret'])
-    
-    def get_contacts(self):
-        if not hasattr(self, 'access_token'): return
-        # Use developer token and secret to instantiate access token object
-        token = oauth.Token(key=self.access_token['oauth_token'], secret=self.access_token['oauth_token_secret'])
-        client = oauth.Client(self.consumer, token)
-        count = 25
-        base_url = "http://api.linkedin.com/v1/people-search:(people:(id,first-name,last-name,headline))?format=json&company-name=%s&current-company=true&count=%d" % (urllib.quote_plus(self.options['company']['value']), count)
-        url = base_url
-        cnt, tot = 0, 0
-        page = 1
-        while True:
-            try: resp, content = client.request(url)
-            except KeyboardInterrupt:
-                print ''
-                break
-            jsonstr = content
-            try: jsonobj = json.loads(jsonstr)
-            except ValueError as e:
-                self.error(e.__str__())
-                continue
-            if resp['status'] == '401':
-                self.error('Access Token Needed or Expired.')
-                self.get_access_tokens()
-                self.get_contacts()
-                break
-            elif resp['status'] == '403':
-                self.error('Error accessing API: %s' % jsonobj['message'])
-                break
-            if not 'values' in jsonobj['people']: break
-            for contact in jsonobj['people']['values']:
-                if 'headline' in contact:
-                    title = self.unescape(contact['headline'])
-                    fname = self.unescape(re.split('[\s]',contact['firstName'])[0])
-                    lname = self.unescape(re.split('[,;]',contact['lastName'])[0])
-                    self.output('%s %s - %s' % (fname, lname, title))
-                    tot += 1
-                    cnt += self.add_contact(fname, lname, title)
-            if not '_start' in jsonobj['people']: break
-            if jsonobj['people']['_start'] + jsonobj['people']['_count'] == jsonobj['people']['_total']: break
-            start = page * jsonobj['people']['_count']
-            url = '%s&start=%d' % (base_url, start)
-            page += 1
-        self.output('%d total contacts found.' % (tot))
-        if cnt: self.alert('%d NEW contacts found!' % (cnt))

modules/discovery/countermeasures/dns/cache_snoop.py

+import framework
+# unique to module
+import os
+import dns
+import re
+
+class Module(framework.module):
+
+    def __init__(self, params):
+        framework.module.__init__(self, params)
+        self.register_option('nameserver', '', 'yes', 'ip address of target\'s nameserver')
+        self.register_option('domains', './data/av_domains.lst', 'yes', 'domain or list of domains to snoop for')
+        self.register_option('verbose', self.goptions['verbose']['value'], 'yes', self.goptions['verbose']['desc'])
+        self.classify = 'active'
+        self.info = {
+                     'Name': 'DNS Cache Snooper',
+                     'Author': 'thrapt (thrapt@gmail.com)',
+                     'Description': 'Uses the DNS cache snooping technique to check for visited domains',
+                     'Comments': [
+                                  'Nameserver must be in IP form.',
+                                  'Domains options: host.domain.com, <path/to/infile>',
+                                  'http://304geeks.blogspot.com/2013/01/dns-scraping-for-corporate-av-detection.html'
+                                 ]
+                     }
+
+    def do_run(self, params):
+        if not self.validate_options(): return
+        # === begin here ===
+        self.cachesnoop()
+
+    def cachesnoop(self):
+        verbose = self.options['verbose']['value']
+        domains = self.options['domains']['value']
+        nameserver = self.options['nameserver']['value']
+        
+        if os.path.exists(domains):
+            hosts = open(domains).read().split()
+        else:
+            hosts = [domains]
+        
+        self.output('Starting queries...')
+        
+        for host in hosts:
+            status = 'Not found'
+            # prepare our query
+            query = dns.message.make_query(host, dns.rdatatype.A, dns.rdataclass.IN)
+            # unset the Recurse flag 
+            query.flags ^= dns.flags.RD
+            try:
+                # try the query
+                response = dns.query.udp(query, nameserver)
+            except KeyboardInterrupt:
+                print ''
+                return
+            except dns.resolver.NXDOMAIN: status = 'Unknown'
+            except dns.resolver.NoAnswer: status = 'No answer'
+            except dns.exception.SyntaxError:
+                self.error('Nameserver must be in IP form.')
+                return
+            except: status = 'Error'
+
+            # searchs the response to find the answer
+            if len(response.answer) > 0:
+                status = 'Snooped!'
+                self.alert('%s => %s' % (host, status))
+            else:
+                if verbose: self.output('%s => %s' % (host, status))

modules/discovery/exploitable/http/dnn_fcklinkgallery.py

+import framework
+# unique to module
+
+class Module(framework.module):
+
+    def __init__(self, params):
+        framework.module.__init__(self, params)
+        self.register_option('source', 'db', 'yes', 'source of module input')
+        self.register_option('verbose', self.goptions['verbose']['value'], 'yes', self.goptions['verbose']['desc'])
+        self.classify = 'active'
+        self.info = {
+                     'Name': 'Dot Net Nuke Remote File Upload Vulnerability Checker',
+                     'Author': 'Jay Turla (@shipcod3)',
+                     'Description': 'Checks the hosts for a DNN fcklinkgallery page which is possibly vulnerable to Remote File Upload.',
+                     'Comments': [
+                                  'Source options: [ db | <hostname> | ./path/to/file | query <sql> ]',
+                                  'http://www.exploit-db.com/exploits/12700/',
+                                  ]
+                     }
+
+    def do_run(self, params):
+        if not self.validate_options(): return
+        # === begin here ===
+        self.check_for_dnnfcklink()
+    
+    def check_for_dnnfcklink(self):
+        verbose = self.options['verbose']['value']
+        
+        hosts = self.get_source(self.options['source']['value'], 'SELECT DISTINCT host FROM hosts WHERE host IS NOT NULL ORDER BY host')
+        if not hosts: return
+
+        # check all hosts for DNN fcklinkgallery page
+        protocols = ['http', 'https']
+        cnt = 0
+        for host in hosts:
+            for proto in protocols:
+                url = '%s://%s/Providers/HtmlEditorProviders/Fck/fcklinkgallery.aspx' % (proto, host)
+                try:
+                    resp = self.request(url, redirect=False)
+                    code = resp.status_code
+                except KeyboardInterrupt:
+                    print ''
+                    return
+                except:
+                    code = 'Error'
+                if code == 200 and '> Link Gallery' in resp.text:
+                    self.alert('%s => %s. Possible DNN Fcklinkgallery page found!' % (url, code))
+                    cnt += 1
+                else:
+                    if verbose: self.output('%s => %s' % (url, code))
+        self.output('%d DNN Fcklinkgallery pages found' % (cnt))

modules/discovery/info_disclosure/http/elmah.py

+import framework
+# unique to module
+
+class Module(framework.module):
+
+    def __init__(self, params):
+        framework.module.__init__(self, params)
+        self.register_option('source', 'db', 'yes', 'source of module input')
+        self.register_option('verbose', self.goptions['verbose']['value'], 'yes', self.goptions['verbose']['desc'])
+        self.classify = 'active'
+        self.info = {
+                     'Name': 'ELMAH Log Scanner',
+                     'Author': 'Tim Tomes (@LaNMaSteR53)',
+                     'Description': 'Checks hosts for a \'elmah.axd\' log page.',
+                     'Comments': [
+                                  'Source options: [ db | <hostname> | ./path/to/file | query <sql> ]',
+                                  'http://www.troyhunt.com/2012/01/aspnet-session-hijacking-with-google.html',
+                                  'Google dorks: inurl:elmah.axd ASPXAUTH',
+                                  '              inurl:elmah.axd intitle:"Error log for"'
+                                  ]
+                     }
+
+    def do_run(self, params):
+        if not self.validate_options(): return
+        # === begin here ===
+        self.check_for_elmah()
+    
+    def check_for_elmah(self):
+        verbose = self.options['verbose']['value']
+        
+        hosts = self.get_source(self.options['source']['value'], 'SELECT DISTINCT host FROM hosts WHERE host IS NOT NULL ORDER BY host')
+        if not hosts: return
+
+        # check all hosts for elmah page
+        protocols = ['http', 'https']
+        cnt = 0
+        for host in hosts:
+            for proto in protocols:
+                url = '%s://%s/elmah.axd' % (proto, host)
+                try:
+                    resp = self.request(url, redirect=False)
+                    code = resp.status_code
+                except KeyboardInterrupt:
+                    print ''
+                    return
+                except:
+                    code = 'Error'
+                if code == 200 and 'Error Log for' in resp.text:
+                    self.alert('%s => %s. Possible ELMAH log page found!' % (url, code))
+                    cnt += 1
+                else:
+                    if verbose: self.output('%s => %s' % (url, code))
+        self.output('%d ELMAH log pages found.' % (cnt))

modules/discovery/info_disclosure/http/phpinfo.py

+import framework
+# unique to module
+
+class Module(framework.module):
+
+    def __init__(self, params):
+        framework.module.__init__(self, params)
+        self.register_option('source', 'db', 'yes', 'source of module input')
+        self.register_option('verbose', self.goptions['verbose']['value'], 'yes', self.goptions['verbose']['desc'])
+        self.classify = 'active'
+        self.info = {
+                     'Name': 'phpinfo() Page Checker',
+                     'Author': 'Jay Turla (@shipcod3)',
+                     'Description': 'Checks the hosts for phpinfo() page which outputs information about PHP configuration',
+                     'Comments': [
+                                  'Source options: [ db | <hostname> | ./path/to/file | query <sql> ]',
+                                  'Reference: http://php.net/manual/en/function.phpinfo.php',
+                                  'Google Dorks:',
+                                  '%sinurl:phpinfo.php ext:php' % (self.spacer),
+                                  '%sinurl:test.php intitle:phpinfo() ext:php' % (self.spacer)
+                                  ]
+                     }
+
+    def do_run(self, params):
+        if not self.validate_options(): return
+        # === begin here ===
+        self.check_for_phpinfo()
+    
+    def check_for_phpinfo(self):
+        verbose = self.options['verbose']['value']
+        
+        hosts = self.get_source(self.options['source']['value'], 'SELECT DISTINCT host FROM hosts WHERE host IS NOT NULL ORDER BY host')
+        if not hosts: return
+
+        # check all hosts for phpinfo() page under phpinfo.php and test.php files
+        protocols = ['http', 'https']
+        files = [('phpinfo.php'), ('test.php')]
+        cnt = 0
+        for host in hosts:
+            for proto in protocols:
+                for filename in files:
+                    url = '%s://%s/%s' % (proto, host, filename)
+                    try:
+                        resp = self.request(url, redirect=False)
+                        code = resp.status_code
+                    except KeyboardInterrupt:
+                        print ''
+                        return
+                    except:
+                        code = 'Error'
+                    if code == 200 and 'phpinfo()' in resp.text:
+                        self.alert('%s => %s. phpinfo() page found!' % (url, code))
+                        cnt += 1
+                    else:
+                        if verbose: self.output('%s => %s' % (url, code))
+        self.output('%d phpinfo() pages found' % (cnt))

modules/discovery/info_disclosure/http/robots.py

+import framework
+# unique to module
+import gzip
+from StringIO import StringIO
+
+class Module(framework.module):
+
+    def __init__(self, params):
+        framework.module.__init__(self, params)
+        self.register_option('source', 'db', 'yes', 'source of module input')
+        self.register_option('verbose', self.goptions['verbose']['value'], 'yes', self.goptions['verbose']['desc'])
+        self.classify = 'active'
+        self.info = {
+                     'Name': 'robots.txt/sitemap.xml Finder',
+                     'Author': 'thrapt (thrapt@gmail.com)',
+                     'Description': 'Checks hosts for a robots.txt, sitemap.xml and sitemap.xml.gz file.',
+                     'Comments': [
+                                  'Source options: [ db | <hostname> | ./path/to/file | query <sql> ]'
+                                  ]
+                     }
+
+    def do_run(self, params):
+        if not self.validate_options(): return
+        # === begin here ===
+        self.check_for_status()
+    
+    def uncompress(self, data_gz):
+        inbuffer = StringIO(data_gz)
+        data_ct = ''
+        f = gzip.GzipFile(mode='rb', fileobj=inbuffer)
+        try:
+            data_ct = f.read()
+        except IOError:
+            pass
+        f.close()
+        return data_ct     
+    
+    def check_for_status(self):
+        verbose = self.options['verbose']['value']
+        
+        hosts = self.get_source(self.options['source']['value'], 'SELECT DISTINCT host FROM hosts WHERE host IS NOT NULL ORDER BY host')
+        if not hosts: return
+
+        # check all hosts for robots.txt, sitemap.xml and sitemap.xml.gz
+        protocols = ['http', 'https']
+        # filename and string used to verify the file
+        filetypes = [('robots.txt', 'user-agent:'), ('sitemap.xml', '<?xml'), ('sitemap.xml.gz', '<?xml')]
+        cnt = 0
+        for host in hosts:
+            for proto in protocols:
+                for (filename, verify) in filetypes:
+                    url = '%s://%s/%s' % (proto, host, filename)
+                    try:
+                        resp = self.request(url, redirect=False)
+                        code = resp.status_code
+                    except KeyboardInterrupt:
+                        print ''
+                        return
+                    except:
+                        code = 'Error'
+                    if code == 200:
+                        # uncompress if necessary
+                        text = ('.gz' in filename and self.uncompress(resp.text)) or resp.text
+                        # check for file type since many custom 404s are returned as 200s 
+                        if (verify in text.lower()):
+                            self.alert('%s => %s. %s found!' % (url, code, filename))
+                            cnt += 1
+                        else:
+                            self.output('%s => %s. %s invalid!' % (url, code, filename))
+                    else:
+                        if verbose: self.output('%s => %s' % (url, code))
+        
+        self.output('%d files found.' % (cnt))

modules/discovery/info_disclosure/http/server_status.py

+import framework
+# unique to module
+
+class Module(framework.module):
+
+    def __init__(self, params):
+        framework.module.__init__(self, params)
+        self.register_option('source', 'db', 'yes', 'source of module input')
+        self.register_option('verbose', self.goptions['verbose']['value'], 'yes', self.goptions['verbose']['desc'])
+        self.classify = 'active'
+        self.info = {
+                     'Name': 'Apache Server-Status Page Scanner',
+                     'Author': 'Tim Tomes (@LaNMaSteR53)',
+                     'Description': 'Checks hosts for a \'server-status\' page.',
+                     'Comments': [
+                                  'Source options: [ db | <hostname> | ./path/to/file | query <sql> ]',
+                                  'http://blog.sucuri.net/2012/10/popular-sites-with-apache-server-status-enabled.html',
+                                  'http://httpd.apache.org/docs/2.2/mod/mod_status.html',
+                                  'Google dork: intitle:"Apache Status" inurl:"server-status"'
+                                  ]
+                     }
+
+    def do_run(self, params):
+        if not self.validate_options(): return
+        # === begin here ===
+        self.check_for_status()
+    
+    def check_for_status(self):
+        verbose = self.options['verbose']['value']
+        
+        hosts = self.get_source(self.options['source']['value'], 'SELECT DISTINCT host FROM hosts WHERE host IS NOT NULL ORDER BY host')
+        if not hosts: return
+
+        # check all hosts for server-status pages
+        protocols = ['http', 'https']
+        cnt = 0
+        for host in hosts:
+            for proto in protocols:
+                url = '%s://%s/server-status/' % (proto, host)
+                try:
+                    resp = self.request(url, redirect=False)
+                    code = resp.status_code
+                except KeyboardInterrupt:
+                    print ''
+                    return
+                except:
+                    code = 'Error'
+                if code == 200 and '>Apache Status<' in resp.text:
+                    self.alert('%s => %s. Possible Apache Status page found!' % (url, code))
+                    cnt += 1
+                else:
+                    if verbose: self.output('%s => %s' % (url, code))
+        self.output('%d Server Status pages found.' % (cnt))

modules/hosts/add_host.py

-import framework
-# unique to module
-
-class Module(framework.module):
-
-    def __init__(self, params):
-        framework.module.__init__(self, params)
-        self.register_option('host', None, 'yes', 'fully qualified domain name')
-        self.register_option('address', None, 'no', 'ip address')
-        self.classify = 'support'
-        self.info = {
-                     'Name': 'Host Adder',
-                     'Author': 'Drumm',
-                     'Description': 'Manually adds a host.',
-                     'Comments':[]
-                     }
-
-    # do not remove or rename
-    def do_run(self, params):
-        # do not remove or modify
-        if not self.validate_options(): return
-        # === begin module code here ===
-        if self.add_host(self.options['host']['value'], self.options['address']['value']):
-            self.output('Host successfully added.')

modules/hosts/baidu.py

-import framework
-# unique to module
-import urllib
-import re
-import time
-import random
-
-class Module(framework.module):
-
-    def __init__(self, params):
-        framework.module.__init__(self, params)
-        self.register_option('domain', self.goptions['domain']['value'], 'yes', self.goptions['domain']['desc'])
-        self.register_option('verbose', self.goptions['verbose']['value'], 'yes', self.goptions['verbose']['desc'])
-        self.classify = 'passive'
-        self.info = {
-                     'Name': 'Baidu Hostname Enumerator',
-                     'Author': 'Tim Tomes (@LaNMaSteR53)',
-                     'Description': 'Harvests hosts from Baidu.com by using the \'site\' search operator. This module updates the \'hosts\' table of the database with the results.',
-                     'Comments': []
-                     }
-
-    def do_run(self, params):
-        if not self.validate_options(): return
-        # === begin here ===
-        self.get_hosts()
-    
-    def get_hosts(self):
-        verbose = self.options['verbose']['value']
-        domain = self.options['domain']['value']
-        url = 'http://www.baidu.com/s'
-        base_query = 'site:' + domain
-        pattern = '<span class="g">\s\s(\S*?)\.%s.*?</span>'  % (domain)
-        subs = []
-        cnt = 0
-        # control variables
-        new = True
-        page = 0
-        nr = 10
-        # execute search engine queries and scrape results storing subdomains in a list
-        # loop until no new subdomains are found
-        while new == True:
-            content = None
-            query = ''
-            # build query based on results of previous results
-            for sub in subs:
-                query += ' -site:%s.%s' % (sub, domain)
-            full_query = base_query + query
-            payload = {'pn': page*nr, 'wd': full_query}
-            #rn=10
-            #cl=3
-            #
-            if verbose: self.output('URL: %s?%s' % (url, urllib.urlencode(payload)))
-            # send query to search engine
-            try: content = self.request(url, payload=payload)
-            except KeyboardInterrupt:
-                print ''
-            except Exception as e:
-                self.error(e.__str__())
-            if not content: break
-            content = content.text
-            sites = re.findall(pattern, content)
-            # create a unique list
-            sites = list(set(sites))
-            new = False
-            # add subdomain to list if not already exists
-            for site in sites:
-               if site not in subs:
-                    subs.append(site)
-                    new = True
-                    host = '%s.%s' % (site, domain)
-                    self.output('%s' % (host))
-                    cnt += self.add_host(host)
-            if not new:
-                # exit if all subdomains have been found
-                if not u'>\u4e0b\u4e00\u9875&gt;<' in content:
-                    break
-                else:
-                    page += 1
-                    if verbose: self.output('No New Subdomains Found on the Current Page. Jumping to Result %d.' % ((page*nr)+1))
-                    new = True
-            # sleep script to avoid lock-out
-            if verbose: self.output('Sleeping to Avoid Lock-out...')
-            try: time.sleep(random.randint(5,15))
-            except KeyboardInterrupt:
-                print ''
-                break
-        if verbose: self.output('Final Query String: %s?%s' % (url, urllib.urlencode(payload)))
-        self.output('%d total hosts found.' % (len(subs)))
-        if cnt: self.alert('%d NEW hosts found!' % (cnt))

modules/hosts/bing.py

-import framework
-# unique to module
-import urllib
-import re
-import time
-import random
-
-class Module(framework.module):
-
-    def __init__(self, params):
-        framework.module.__init__(self, params)
-        self.register_option('domain', self.goptions['domain']['value'], 'yes', self.goptions['domain']['desc'])
-        self.register_option('verbose', self.goptions['verbose']['value'], 'yes', self.goptions['verbose']['desc'])
-        self.classify = 'passive'
-        self.info = {
-                     'Name': 'Bing Hostname Enumerator',
-                     'Author': 'Tim Tomes (@LaNMaSteR53)',
-                     'Description': 'Harvests hosts from Bing.com by using the \'site\' search operator. This module updates the \'hosts\' table of the database with the results.',
-                     'Comments': []
-                     }
-
-    def do_run(self, params):
-        if not self.validate_options(): return
-        # === begin here ===
-        self.get_hosts()
-    
-    def get_hosts(self):
-        verbose = self.options['verbose']['value']
-        domain = self.options['domain']['value']
-        url = 'http://www.bing.com/search'
-        base_query = 'site:' + domain
-        pattern = '"sb_tlst"><h3><a href="\w+://(\S+?)\.%s' % (domain)
-        subs = []
-        cnt = 0
-        # control variables
-        new = True
-        page = 0
-        nr = 50
-        # execute search engine queries and scrape results storing subdomains in a list
-        # loop until no new subdomains are found
-        while new == True:
-            content = None
-            query = ''
-            # build query based on results of previous results
-            for sub in subs:
-                query += ' -site:%s.%s' % (sub, domain)
-            full_query = base_query + query
-            payload = {'first': str(page*nr), 'q': full_query}
-            #
-            #
-            cookies = {'SRCHHPGUSR': 'NEWWND=0&NRSLT=%d&SRCHLANG=&AS=1' % (nr)}
-            if verbose: self.output('URL: %s?%s' % (url, urllib.urlencode(payload)))
-            # send query to search engine
-            try: content = self.request(url, payload=payload, cookies=cookies)
-            except KeyboardInterrupt:
-                print ''
-            except Exception as e:
-                self.error(e.__str__())
-            if not content: break
-            content = content.text
-            sites = re.findall(pattern, content)
-            # create a unique list
-            sites = list(set(sites))
-            new = False
-            # add subdomain to list if not already exists
-            for site in sites:
-                if site not in subs:
-                    subs.append(site)
-                    new = True
-                    host = '%s.%s' % (site, domain)