├── .gitignore ├── LICENSE ├── README.md ├── core ├── __init__.py ├── config.py ├── dnsfind.py └── utils.py ├── dict └── domain.txt └── run.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | local_settings.py 55 | 56 | # Flask stuff: 57 | instance/ 58 | .webassets-cache 59 | 60 | # Scrapy stuff: 61 | .scrapy 62 | 63 | # Sphinx documentation 64 | docs/_build/ 65 | 66 | # PyBuilder 67 | target/ 68 | 69 | # IPython Notebook 70 | .ipynb_checkpoints 71 | 72 | # pyenv 73 | .python-version 74 | 75 | # celery beat schedule file 76 | celerybeat-schedule 77 | 78 | # dotenv 79 | .env 80 | 81 | # virtualenv 82 | venv/ 83 | ENV/ 84 | 85 | # Spyder project settings 86 | .spyderproject 87 | 88 | # Rope project settings 89 | .ropeproject 90 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Tangyucong 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DnsFind 域名穷举工具 2 | 3 | [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) 4 | [![build|passing](https://img.shields.io/appveyor/ci/gruntjs/grunt/master.svg)](#) 5 | 6 | ``` 7 | ____ _______ __ 8 | / __ \____ _____/ ____(_)___ ____/ / 9 | / / / / __ \/ ___/ /_ / / __ \/ __ / 10 | / /_/ / / / (__ ) __/ / / / / / /_/ / 11 | /_____/_/ /_/____/_/ /_/_/ /_/\__,_/ 12 | 13 | Author: Smarttang && Ver: V1.1 14 | ``` 15 | 16 | ###前言 17 | --- 18 | 平常经常需要穷举某些特殊的站点踩点,用了很多东西都不太顺手,所以开发了这个东西,平常可以自己离线扫下。字典是结合别人的去重后弄出来的,增加了关键字筛选,方便找目录可浏览漏洞,同时方便识别WEB服务。差个webservice,后面补上。web service主要是用于分布式扫描用,等有时间加,先凑合用着。(PS: 自己给自己的生日礼物) 19 | 20 | ###功能列表 21 | --- 22 | 1. dns子域名穷举。 23 | 2. 定制网站关键字筛选。 24 | 3. 自动识别 Web Server 指纹。 25 | 26 | ###目录结构 27 | --- 28 | 目录结构基本上比较清晰,命名规范都比较标准,目的在于长久使用和维护。 29 | 30 | ``` 31 | ├── core 32 | │   ├── __init__.py 33 | │   ├── config.py 34 | │   ├── dnsfind.py 35 | │   └── utils.py 36 | ├── dict 37 | │   └── domain.txt 38 | └── run.py 39 | ``` 40 | 41 | ### Console Service: 42 | --- 43 | Console版本主要用于测试和脱机使用,应用场景在于自身特殊的应用,比如单兵作战,我需要单独的扫描目标直接输出结果。在对参数不了解时,可以help一下。:) 44 | 45 | ``` 46 | console: python run.py -h 47 | ``` 48 | 49 | ``` 50 | ____ _______ __ 51 | / __ \____ _____/ ____(_)___ ____/ / 52 | / / / / __ \/ ___/ /_ / / __ \/ __ / 53 | / /_/ / / / (__ ) __/ / / / / / /_/ / 54 | /_____/_/ /_/____/_/ /_/_/ /_/\__,_/ 55 | 56 | Author: Smarttang && Ver: V1.1 57 | 58 | Usage: run.py [options] 59 | 60 | Options: 61 | -h, --help show this help message and exit 62 | -u TARGET, --target=TARGET 63 | set domain.(simple: baidu.com) 64 | -t THREADS_COUNT, --threads=THREADS_COUNT 65 | threads count.(default 10) 66 | -s TIMEOUT, --timeout=TIMEOUT 67 | set timeout of requests.(default 5s) 68 | -d DICTNAME, --dictname=DICTNAME 69 | choice dicts.(default All) 70 | -f, --finger choice finger on/off.(default False) 71 | -k KEYWORDS, --keywords=KEYWORDS 72 | set keywords.(default None) 73 | ``` 74 | 75 | 常规的命令执行诸如以下,执行子域名穷举。: 76 | 77 | ``` 78 | console: python run.py -u baidu.com -t 30 -s 5 --finger --keywords welcome,nginx,apache,index,of 79 | ``` 80 | 81 | ###依赖包 82 | --- 83 | * gevent 84 | * optparse 85 | * requests 86 | 87 | -------------------------------------------------------------------------------- /core/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smarttang/dnsfind/61c9533d61c102332674a3e6d30a0da51347051a/core/__init__.py -------------------------------------------------------------------------------- /core/config.py: -------------------------------------------------------------------------------- 1 | #coding:utf-8 2 | import os 3 | 4 | version = 'V1.1' 5 | author = 'Smarttang' 6 | 7 | def init_option(keys): 8 | vardict = { 9 | 'timeout': check_time(keys.timeout), 10 | 'target': check_target(keys.target), 11 | 'dictname': check_dictname(keys.dictname), 12 | 'threads_count': check_threads(keys.threads_count), 13 | 'finger':keys.finger, 14 | 'keywords':check_keywords(keys.keywords) 15 | } 16 | return vardict 17 | 18 | 19 | # 检查关键字 20 | def check_keywords(val): 21 | if val: 22 | return val.split(',') 23 | else: 24 | return None 25 | 26 | # 最高设置为超时10 27 | def check_time(val): 28 | if val > 0 and val < 10: 29 | return val 30 | else: 31 | return 5 32 | 33 | # 默认不能有空值 34 | def check_target(val): 35 | domain = val.split('.') 36 | if len(domain) > 1 and '' not in domain: 37 | return '.'.join(domain) 38 | else: 39 | return 'baidu.com' 40 | 41 | # 字典检查 42 | def check_dictname(val): 43 | default_val = 'dict/domain.txt' 44 | if val: 45 | if os.path.exists('dict/'+val): 46 | return 'dict/'+val 47 | else: 48 | return default_val 49 | else: 50 | return default_val 51 | 52 | # 线程数控制 53 | def check_threads(val): 54 | try: 55 | threads_count = int(val) 56 | 57 | if threads_count > 0 and threads_count < 21: 58 | return threads_count 59 | else: 60 | return 10 61 | except Exception, e: 62 | threads_count = 5 63 | finally: 64 | return threads_count -------------------------------------------------------------------------------- /core/dnsfind.py: -------------------------------------------------------------------------------- 1 | #coding:utf-8 2 | 3 | import socket, gevent, os 4 | from gevent.pool import Pool 5 | from core.utils import findreport, FindObj, getpath 6 | 7 | class DnsFind: 8 | 9 | def __init__(self,options): 10 | self.options = options 11 | self.blockip = None 12 | self.keywords = False 13 | self.pool = Pool(self.options['threads_count']) 14 | self.document = self.options['target'].replace('.','_')+'.txt' 15 | socket.setdefaulttimeout(self.options['timeout']) 16 | 17 | # 域名解析 18 | def checkdomain(self,domain=None,mode=None): 19 | print '[*] Check domain: %s' % domain 20 | try: 21 | results = socket.getaddrinfo(domain,None)[0][4][0] 22 | except Exception, e: 23 | results = None 24 | finally: 25 | if results is not None and mode != 'block': 26 | 27 | # 识别指纹 28 | if self.options['finger']: 29 | domain_finger = FindObj(domain).findfinger() 30 | print '\033[1;32;40m[*] Domain alive: %s => %s => %s!! \033[0m' % (domain,results,domain_finger) 31 | else: 32 | print '\033[1;32;40m[*] Domain alive: %s => %s!! \033[0m' % (domain,results) 33 | 34 | # 页面关键字搜索 35 | if self.options['keywords']: 36 | for _keyword in self.options['keywords']: 37 | if FindObj(domain,_keyword).findcontent(): 38 | self.keywords = True 39 | 40 | ipgroup = results.split('.') 41 | data = [results,'.'.join(ipgroup[:3])] 42 | 43 | # 存储结果 44 | # 第一种,激活了关键字筛选且有关键字匹配,则存储。 45 | # 第二种,没有激活关键字,存储 46 | if self.options['keywords'] is not None and self.keywords == True or self.options['keywords'] is None: 47 | report = open(getpath()+'/result/'+self.document,'a+') 48 | report.write(domain+'\n') 49 | report.close() 50 | else: 51 | data = None 52 | 53 | # block模式,主要是测试是否有block功能 54 | if mode == 'block': 55 | return data 56 | 57 | # 字典爆破 58 | def run(self): 59 | # 先判断结果是否存在过 60 | # 以及报告目录是否存在 61 | findreport(self.document) 62 | 63 | # 先检测一个不存在的地址 64 | # 如果能解析,则证明做了一些保护措施 65 | # 将对方地址记录下来,作为block的标准 66 | block_check_results = self.checkdomain('d312379196bd822558ca7dfb3c95ba61.'+self.options['target'],'block') 67 | 68 | if block_check_results: 69 | self.blockip = block_check_results[0] 70 | 71 | # 构建字典 72 | dic_list = (dic.strip('\n')+'.'+self.options['target'] for dic in open(getpath() + '/' +self.options['dictname'],'r')) 73 | # 协程爆破测试 74 | self.pool.map(self.checkdomain,dic_list) -------------------------------------------------------------------------------- /core/utils.py: -------------------------------------------------------------------------------- 1 | #coding:utf-8 2 | 3 | import os, requests, sys 4 | 5 | # 获得绝对路径 6 | def getpath(): 7 | return sys.path[0] 8 | 9 | # 查报告是否存在 10 | # 判断报告目录是否存在,不存在择新建 11 | # 如果存在,则删除历史报告 12 | def findreport(val): 13 | path = getpath() + '/result/' 14 | if not os.path.exists(path): 15 | os.mkdir(path) 16 | else: 17 | target = path + val 18 | if os.path.exists(target): 19 | os.remove(target) 20 | 21 | 22 | # 1、查服务指纹 23 | # 2、页面关键字筛选 24 | class FindObj: 25 | 26 | def __init__(self,target,keyword=None): 27 | self.target = target 28 | self.response = None 29 | self.keyword = keyword 30 | 31 | def _getrequest(self): 32 | try: 33 | self.response = requests.get('http://'+target) 34 | except: 35 | pass 36 | 37 | # 获得服务指纹 38 | def findfinger(self): 39 | self._getrequest() 40 | 41 | if self.response: 42 | return self.response.headers['Server'] 43 | else: 44 | return None 45 | 46 | # 获得页面内容 47 | # 比对关键字规则 48 | def findcontent(self): 49 | self._getrequest() 50 | 51 | if self.response: 52 | content = ''.join([con.strip() for con in self.response.content.split('\n')]) 53 | 54 | if self.keyword: 55 | for keyword_item in self.keyword: 56 | if content.find(keyword_item) != -1: 57 | return True 58 | return False 59 | -------------------------------------------------------------------------------- /run.py: -------------------------------------------------------------------------------- 1 | #coding:utf-8 2 | 3 | import sys 4 | reload(sys) 5 | sys.setdefaultencoding('utf8') 6 | 7 | from core.config import version,author,init_option 8 | from core.dnsfind import DnsFind 9 | from optparse import OptionParser 10 | 11 | if __name__ == '__main__': 12 | usage = '''   13 | ____ _______ __ 14 | / __ \____ _____/ ____(_)___ ____/ / 15 | / / / / __ \/ ___/ /_ / / __ \/ __ / 16 | / /_/ / / / (__ ) __/ / / / / / /_/ / 17 | /_____/_/ /_/____/_/ /_/_/ /_/\__,_/ 18 | 19 | Author: %s && Ver: %s 20 | ''' % (author,version) 21 | print usage 22 | parser = OptionParser() 23 | parser.add_option("-u", "--target", dest="target", default='baidu.com', help="set domain.(simple: baidu.com)") 24 | parser.add_option("-t", "--threads", dest="threads_count", default=10, help="threads count.(default 10)") 25 | parser.add_option("-s", "--timeout", dest="timeout", default=5, help="set timeout of requests.(default 5s)") 26 | parser.add_option("-d", "--dictname", dest="dictname", default=None, help="choice dicts.(default All)") 27 | parser.add_option("-f", "--finger", dest="finger", default=False, action="store_true",help= "choice finger on/off.(default False)") 28 | parser.add_option("-k", "--keywords", dest="keywords", default=None, help="set keywords.(default None)") 29 | 30 | 31 | (options, args) = parser.parse_args() 32 | 33 | try: 34 | DnsFind(init_option(options)).run() 35 | except KeyboardInterrupt, e: 36 | sys.exit(1) 37 | --------------------------------------------------------------------------------