添加fuzz模式支持字典功能

This commit is contained in:
Jing Ling
2020-08-11 18:29:02 +08:00
parent 4c14166a1d
commit 649ad736c5
2 changed files with 77 additions and 43 deletions
+74 -41
View File
@@ -77,39 +77,16 @@ def detect_wildcard(domain, authoritative_ns):
wildcard = do_query_a(random_subdomain, resolver)
except Exception as e:
logger.log('DEBUG', e.args)
logger.log('ALERT', f'Multiple detection errors, so temporarily {domain} does not use wildcard dns record')
logger.log('ALERT', f'Multiple detection errors, so temporarily {domain} '
f'does not use wildcard dns record')
return False
else:
return wildcard
def gen_fuzz_subdomains(expression, rule):
def gen_subdomains(expression, path):
"""
Generate subdomains based on fuzz mode
:param str expression: generate subdomains's expression
:param str rule: regexp rule
:return list subdomains: list of subdomains
"""
subdomains = list()
fuzz_count = exrex.count(rule)
if fuzz_count > 10000000:
logger.log('ALERT', f'The dictionary generated by this rule is too large{fuzz_count} > 10000000')
logger.log('DEBUG', f'Dictionary size based on fuzz mode: {fuzz_count}')
for fuzz_string in exrex.generate(rule):
fuzz_string = fuzz_string.lower()
if not fuzz_string.isalnum():
continue
fuzz_domain = expression.replace('*', fuzz_string)
subdomains.append(fuzz_domain)
random_domain = random.choice(subdomains)
logger.log('ALERT', f'Please check whether {random_domain} is correct or not')
return subdomains
def gen_word_subdomains(expression, path):
"""
Generate subdomains based on word mode
Generate subdomains
:param str expression: generate subdomains's expression
:param str path: path of wordlist
@@ -129,12 +106,59 @@ def gen_word_subdomains(expression, path):
word = word[:-1]
subdomain = expression.replace('*', word)
subdomains.append(subdomain)
size = len(subdomains)
logger.log('DEBUG', f'The size of the dictionary generated by {path} is {size}')
if size == 0:
logger.log('ALERT', 'Please check the dictionary content!')
else:
random_domain = random.choice(subdomains)
logger.log('DEBUG', f'Dictionary based on word mode size: {len(subdomains)}')
logger.log('ALERT', f'Please check whether {random_domain} is correct or not')
return subdomains
def gen_fuzz_subdomains(expression, rule, fuzzlist):
"""
Generate subdomains based on fuzz mode
:param str expression: generate subdomains's expression
:param str rule: regexp rule
:param str fuzzlist: fuzz dictionary
:return list subdomains: list of subdomains
"""
subdomains = list()
if fuzzlist:
fuzz_domain = gen_subdomains(expression, fuzzlist)
subdomains = subdomains.extend(fuzz_domain)
if rule:
fuzz_count = exrex.count(rule)
if fuzz_count > 10000000:
logger.log('ALERT', f'The dictionary generated by this rule is too large: '
f'{fuzz_count} > 10000000')
for fuzz_string in exrex.generate(rule):
fuzz_string = fuzz_string.lower()
if not fuzz_string.isalnum():
continue
fuzz_domain = expression.replace('*', fuzz_string)
subdomains.append(fuzz_domain)
random_domain = random.choice(subdomains)
logger.log('ALERT', f'Please check whether {random_domain} is correct or not')
logger.log('DEBUG', f'Dictionary size based on fuzz mode: {len(subdomains)}')
return subdomains
def gen_word_subdomains(expression, path):
"""
Generate subdomains based on word mode
:param str expression: generate subdomains's expression
:param str path: path of wordlist
:return list subdomains: list of subdomains
"""
subdomains = gen_subdomains(expression, path)
logger.log('DEBUG', f'Dictionary based on word mode size: {len(subdomains)}')
return subdomains
def query_domain_ns_a(ns_list):
logger.log('INFOR', f'Querying A record from authoritative name server: {ns_list} ')
if not isinstance(ns_list, list):
@@ -172,7 +196,8 @@ def query_domain_ns(domain):
@tenacity.retry(stop=tenacity.stop_after_attempt(2))
def get_wildcard_record(domain, resolver):
logger.log('INFOR', f'Query {domain} \'s wildcard dns record in authoritative name server')
logger.log('INFOR', f"Query {domain} 's wildcard dns record "
f"in authoritative name server")
try:
answer = resolver.query(domain, 'A')
# 如果查询随机域名A记录时抛出Timeout异常则重新查询
@@ -186,7 +211,8 @@ def get_wildcard_record(domain, resolver):
return None, None
except Exception as e:
logger.log('ERROR', e.args)
logger.log('ERROR', f'Query {domain} wildcard dns record in authoritative name server error')
logger.log('ERROR', f'Query {domain} wildcard dns record in '
f'authoritative name server error')
exit(1)
else:
if answer.rrset is None:
@@ -444,6 +470,7 @@ class Brute(Module):
brute.py --target domain.com --word True --wordlist subnames.txt run
brute.py --target domain.com --word True --recursive True --depth 2 run
brute.py --target d.com --fuzz True --place m.*.d.com --rule '[a-z]' run
brute.py --target d.com --fuzz True --place m.*.d.com --fuzzlist subnames.txt run
Note:
--alive True/False Only export alive subdomains or not (default False)
@@ -455,14 +482,15 @@ class Brute(Module):
:param int process: Number of processes (default 1)
:param int concurrent: Number of concurrent (default 10000)
:param bool word: Use word mode generate dictionary (default False)
:param str wordlist: Dictionary path used in word mode (default use ./config/setting.py)
:param str wordlist: Dictionary path used in word mode (default use ./config/default.py)
:param bool recursive: Use recursion (default False)
:param int depth: Recursive depth (default 2)
:param str nextlist: Dictionary file path used by recursive (default use ./config/setting.py)
:param str nextlist: Dictionary file path used by recursive (default use ./config/default.py)
:param bool fuzz: Use fuzz mode generate dictionary (default False)
:param bool alive: Only export alive subdomains (default False)
:param str place: Designated fuzz position (required if use fuzz mode)
:param str rule: Specify the regexp rules used in fuzz mode (required if use fuzz mode)
:param str fuzzlist: Dictionary path used in fuzz mode (default use ./config/default.py)
:param bool export: Export the results (default True)
:param str format: Result format (default csv)
:param str path: Result directory (default None)
@@ -471,8 +499,8 @@ class Brute(Module):
def __init__(self, target, process=None, concurrent=None, word=False,
wordlist=None, recursive=False, depth=None, nextlist=None,
fuzz=False, place=None, rule=None, export=True, alive=True,
format='csv', path=None):
fuzz=False, place=None, rule=None, fuzzlist=None, export=True,
alive=True, format='csv', path=None):
Module.__init__(self)
self.module = 'Brute'
self.source = 'Brute'
@@ -487,6 +515,7 @@ class Brute(Module):
self.fuzz = fuzz or settings.enable_fuzz
self.place = place or settings.fuzz_place
self.rule = rule or settings.fuzz_rule
self.fuzzlist = fuzzlist or settings.fuzz_list
self.export = export
self.alive = alive
self.format = format
@@ -516,7 +545,7 @@ class Brute(Module):
# set可以合并list
dict_set = dict_set.union(word_subdomains)
if self.fuzz:
fuzz_subdomains = gen_fuzz_subdomains(self.place, self.rule)
fuzz_subdomains = gen_fuzz_subdomains(self.place, self.rule, self.fuzzlist)
dict_set = dict_set.union(fuzz_subdomains)
count = len(dict_set)
logger.log('INFOR', f'Dictionary size: {count}')
@@ -532,8 +561,11 @@ class Brute(Module):
if len(self.domains) > 1:
self.bulk = True
if self.fuzz:
if self.place is None or self.rule is None:
logger.log('FATAL', f'No fuzz position or rules specified')
if self.place is None:
logger.log('FATAL', f'No fuzz position specified')
exit(1)
if self.rule is None and self.fuzzlist is None:
logger.log('FATAL', f'No fuzz rules or fuzz dictionary specified')
exit(1)
if self.bulk:
logger.log('FATAL', f'Cannot use fuzz mode in the bulk brute')
@@ -614,7 +646,7 @@ class Brute(Module):
return self.subdomains
def run(self):
logger.log('INFOR', f'Start runing {self.source} module')
logger.log('INFOR', f'Start running {self.source} module')
if self.check_env:
utils.check_env()
self.domains = utils.get_domains(self.target)
@@ -622,7 +654,8 @@ class Brute(Module):
for self.domain in self.domains:
self.check_brute_params()
if self.recursive_brute:
logger.log('INFOR', f'Start recursively brute the first layer subdomain of {self.domain}')
logger.log('INFOR', f'Start recursively brute the 1 layer subdomain'
f' of {self.domain}')
valid_subdomains = self.main(self.domain)
all_subdomains.extend(valid_subdomains)
@@ -631,8 +664,8 @@ class Brute(Module):
if self.recursive_brute:
for layer_num in range(1, self.recursive_depth):
# 之前已经做过1层子域爆破 当前实际递归层数是layer+1
logger.log('INFOR', f'Start recursively brute'
f'the {layer_num + 1} layer subdomain of {self.domain}')
logger.log('INFOR', f'Start recursively brute the {layer_num+1} layer'
f' subdomain of {self.domain}')
for subdomain in all_subdomains:
self.place = '*.' + subdomain
# 进行下一层子域爆破的限制条件
+2 -1
View File
@@ -73,7 +73,8 @@ only_save_valid = True # 是否在处理爆破结果时只存入解析成功的
check_time = 10 # 检查字典配置停留时间(默认10秒)
enable_fuzz = False # 是否使用fuzz模式枚举域名
fuzz_place = None # 指定爆破的位置 指定的位置用`@`表示 示例:www.@.example.com
fuzz_rule = None # fuzz域名的正则 示例:'[a-z][0-9]' 表示第一位是字母 第二位是数字
fuzz_rule = None # fuzz域名使用的正则表达式 示例:'[a-z][0-9]' 表示第一位是字母 第二位是数字
fuzz_list = None # fuzz域名使用的字典路径
brute_ip_blacklist = {'0.0.0.0', '0.0.0.1'} # IP黑名单 子域解析到IP黑名单则标记为非法子域
ip_appear_maximum = 100 # 多个子域解析到同一IP次数超过100次则标记为非法(泛解析)子域