mirror of
https://github.com/shmilylty/OneForAll.git
synced 2026-08-26 12:57:50 +08:00
内存优化效果显著
This commit is contained in:
+118
-101
@@ -7,6 +7,7 @@ OneForAll子域爆破模块
|
|||||||
:copyright: Copyright (c) 2019, Jing Ling. All rights reserved.
|
:copyright: Copyright (c) 2019, Jing Ling. All rights reserved.
|
||||||
:license: GNU General Public License v3.0, see LICENSE for more details.
|
:license: GNU General Public License v3.0, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
|
import gc
|
||||||
import json
|
import json
|
||||||
import time
|
import time
|
||||||
import stat
|
import stat
|
||||||
@@ -19,7 +20,7 @@ import exrex
|
|||||||
import fire
|
import fire
|
||||||
import tenacity
|
import tenacity
|
||||||
from dns.exception import Timeout
|
from dns.exception import Timeout
|
||||||
from dns.resolver import Answer, NXDOMAIN, YXDOMAIN, NoAnswer, NoNameservers
|
from dns.resolver import NXDOMAIN, YXDOMAIN, NoAnswer, NoNameservers
|
||||||
|
|
||||||
import config
|
import config
|
||||||
import dbexport
|
import dbexport
|
||||||
@@ -327,10 +328,88 @@ def read_result(result_path):
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def deal_result(result_path):
|
def gen_records(items, records, subdomains, ip_times, wc_ips, wc_ttl):
|
||||||
|
qname = items.get('name')[:-1] # 去出最右边的`.`点号
|
||||||
|
reason = items.get('status')
|
||||||
|
resolver = items.get('resolver')
|
||||||
|
data = items.get('data')
|
||||||
|
answers = data.get('answers')
|
||||||
|
record = dict()
|
||||||
|
cname = list()
|
||||||
|
ips = list()
|
||||||
|
public = list()
|
||||||
|
times = list()
|
||||||
|
ttls = list()
|
||||||
|
have_a_record = False
|
||||||
|
is_valid_flag = True
|
||||||
|
for answer in answers:
|
||||||
|
if answer.get('type') != 'A':
|
||||||
|
logger.log('TRACE', f'查询{qname}返回的应答没有A记录\n{answer}')
|
||||||
|
continue
|
||||||
|
logger.log('TRACE', f'查询{qname}返回的应答具有A记录\n{answer}')
|
||||||
|
have_a_record = True
|
||||||
|
ttl = answer.get('ttl')
|
||||||
|
ttls.append(ttl)
|
||||||
|
cname.append(answer.get('name')[:-1]) # 去出最右边的`.`点号
|
||||||
|
ip = answer.get('data')
|
||||||
|
ips.append(ip)
|
||||||
|
public.append(utils.ip_is_public(ip))
|
||||||
|
num = ip_times.get(ip)
|
||||||
|
times.append(num)
|
||||||
|
isvalid, reason = is_valid_subdomain(ip, ttl, num, wc_ips, wc_ttl)
|
||||||
|
logger.log('TRACE', f'{ip}是否有效:{isvalid} 原因:{reason}')
|
||||||
|
if isvalid == 0:
|
||||||
|
is_valid_flag = False # 只要有一条A记录判断通不过就认为改子域为无效子域
|
||||||
|
break
|
||||||
|
if not have_a_record:
|
||||||
|
logger.log('TRACE', f'查询{qname}返回的所有应答都中没有A记录{answers}')
|
||||||
|
# 为了优化内存 判断通不过的子域暂时不添加到记录里
|
||||||
|
if is_valid_flag:
|
||||||
|
record['resolve'] = 1
|
||||||
|
record['reason'] = reason
|
||||||
|
record['ttl'] = ttls
|
||||||
|
record['cname'] = cname
|
||||||
|
record['content'] = ips
|
||||||
|
record['public'] = public
|
||||||
|
record['times'] = times
|
||||||
|
record['resolver'] = resolver
|
||||||
|
records[qname] = record
|
||||||
|
subdomains.append(qname)
|
||||||
|
return records, subdomains
|
||||||
|
|
||||||
|
|
||||||
|
def stat_ip_times(result_path):
|
||||||
|
logger.log('INFOR', f'正在统计IP次数')
|
||||||
|
times = dict()
|
||||||
|
with open(result_path) as fd:
|
||||||
|
for line in fd:
|
||||||
|
line = line.strip()
|
||||||
|
try:
|
||||||
|
items = json.loads(line)
|
||||||
|
except Exception as e:
|
||||||
|
logger.log('ERROR', e.args)
|
||||||
|
logger.log('ERROR', f'解析行{line}出错跳过解析该行')
|
||||||
|
continue
|
||||||
|
status = items.get('status')
|
||||||
|
if status != 'NOERROR':
|
||||||
|
continue
|
||||||
|
data = items.get('data')
|
||||||
|
if 'answers' not in data:
|
||||||
|
continue
|
||||||
|
answers = data.get('answers')
|
||||||
|
for answer in answers:
|
||||||
|
if answer.get('type') == 'A':
|
||||||
|
ip = answer.get('data')
|
||||||
|
# 取值 如果是首次出现的IP集合 出现次数先赋值0
|
||||||
|
value = times.setdefault(ip, 0)
|
||||||
|
times[ip] = value + 1
|
||||||
|
return times
|
||||||
|
|
||||||
|
|
||||||
|
def deal_result(result_path, ip_times, wildcard_ips, wildcard_ttl):
|
||||||
logger.log('INFOR', f'正在处理解析结果')
|
logger.log('INFOR', f'正在处理解析结果')
|
||||||
records = dict() # 用来记录域名解析数据
|
records = dict() # 用来记录所有域名解析数据
|
||||||
times = dict() # 用来统计IP出现次数
|
subdomains = list() # 用来保存所有通过有效性检查的子域
|
||||||
with open(result_path) as fd:
|
with open(result_path) as fd:
|
||||||
for line in fd:
|
for line in fd:
|
||||||
line = line.strip()
|
line = line.strip()
|
||||||
@@ -349,114 +428,52 @@ def deal_result(result_path):
|
|||||||
if 'answers' not in data:
|
if 'answers' not in data:
|
||||||
logger.log('TRACE', f'处理{line}时发现{qname}返回的结果无应答')
|
logger.log('TRACE', f'处理{line}时发现{qname}返回的结果无应答')
|
||||||
continue
|
continue
|
||||||
answers = data.get('answers')
|
records, subdomains = gen_records(items, records, subdomains,
|
||||||
flag = False
|
ip_times, wildcard_ips,
|
||||||
record = dict()
|
wildcard_ttl)
|
||||||
cname = list()
|
return records, subdomains
|
||||||
ips = list()
|
|
||||||
public = list()
|
|
||||||
ttl = list()
|
|
||||||
resolver = items.get('resolver')
|
|
||||||
for answer in answers:
|
|
||||||
logger.log('TRACE', f'处理{line}时发现{qname}返回的应答{answer}无问题')
|
|
||||||
if answer.get('type') == 'A':
|
|
||||||
flag = True
|
|
||||||
ttl.append(answer.get('ttl'))
|
|
||||||
cname.append(answer.get('name')[:-1]) # 去出最右边的`.`点号
|
|
||||||
ip = answer.get('data')
|
|
||||||
ips.append(ip)
|
|
||||||
public.append(utils.ip_is_public(ip))
|
|
||||||
record['reason'] = status
|
|
||||||
record['ttl'] = ttl
|
|
||||||
record['cname'] = cname
|
|
||||||
record['content'] = ips
|
|
||||||
record['public'] = public
|
|
||||||
record['resolver'] = resolver
|
|
||||||
records[qname] = record
|
|
||||||
# 取值 如果是首次出现的IP集合 出现次数先赋值0
|
|
||||||
value = times.setdefault(ip, 0)
|
|
||||||
times[ip] = value + 1
|
|
||||||
if not flag:
|
|
||||||
logger.log('TRACE', f'处理{line}时发现{qname}返回的应答中没有A记录')
|
|
||||||
return records, times
|
|
||||||
|
|
||||||
|
|
||||||
def add_times(records, ip_times):
|
def check_by_compare(ip, ttl, wc_ips, wc_ttl):
|
||||||
for name, record in records.items():
|
|
||||||
times = list()
|
|
||||||
ips = record.get('content')
|
|
||||||
if not ips:
|
|
||||||
continue
|
|
||||||
for ip in ips:
|
|
||||||
times.append(ip_times.get(ip))
|
|
||||||
record['times'] = times
|
|
||||||
records[name] = record
|
|
||||||
return records
|
|
||||||
|
|
||||||
|
|
||||||
def check_validity(records, ip_times, wildcard_ips, wildcard_ttl):
|
|
||||||
valid_subdomains = list()
|
|
||||||
for name, record in records.items():
|
|
||||||
if record.get('resolve') is None:
|
|
||||||
ips = record['content']
|
|
||||||
ttl = record['ttl']
|
|
||||||
status, reason = is_valid_subdomain(ips, ttl, ip_times,
|
|
||||||
wildcard_ips, wildcard_ttl)
|
|
||||||
record['resolve'], record['reason'] = status, reason
|
|
||||||
records[name] = record
|
|
||||||
# 在打了有效性标签后 暂且把除无效子域的子域都认为是有效子域
|
|
||||||
if record.get('resolve') != 0:
|
|
||||||
valid_subdomains.append(name)
|
|
||||||
return records, valid_subdomains
|
|
||||||
|
|
||||||
|
|
||||||
def check_by_compare(ips, ttl, wildcard_ips, wildcard_ttl):
|
|
||||||
"""
|
"""
|
||||||
通过与泛解析返回的IP集合和返回的TTL值进行对比判断发现的子域是否是泛解析子域
|
通过与泛解析返回的IP集合和返回的TTL值进行对比判断发现的子域是否是泛解析子域
|
||||||
|
|
||||||
:param set ips: 子域A记录查询出的IP集合
|
:param set ip: 子域A记录查询出的IP
|
||||||
:param int ttl: 子域A记录查询出的TTL
|
:param int ttl: 子域A记录查询出的TTL
|
||||||
:param set wildcard_ips: 泛解析的IP集合
|
:param set wc_ips: 泛解析的IP集合
|
||||||
:param int wildcard_ttl: 泛解析的TTL
|
:param int wc_ttl: 泛解析的TTL
|
||||||
:return: 判断结果
|
:return: 判断结果
|
||||||
"""
|
"""
|
||||||
# 参考:http://sh3ll.me/archives/201704041222.txt
|
# 参考:http://sh3ll.me/archives/201704041222.txt
|
||||||
if not ips.intersection(wildcard_ips):
|
if ip not in wc_ips:
|
||||||
return False # 子域IP集合与泛解析IP集合无任何交集则不是泛解析
|
return False # 子域IP不在泛解析IP集合则不是泛解析
|
||||||
if ttl != wildcard_ttl and ttl % 60 == 0 and wildcard_ttl % 60 == 0:
|
if ttl != wc_ttl and ttl % 60 == 0 and wc_ttl % 60 == 0:
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def check_ip_times(ips, times):
|
def check_ip_times(times):
|
||||||
"""
|
"""
|
||||||
根据ip出现次数判断是否为泛解析
|
根据ip出现次数判断是否为泛解析
|
||||||
|
|
||||||
:param set ips: 子域IP集合
|
:param times: 子域IP出现次数
|
||||||
:param times: 子域IP出现次数统计字典
|
|
||||||
:return: 判断结果
|
:return: 判断结果
|
||||||
"""
|
"""
|
||||||
for ip in ips:
|
if times > config.ip_appear_maximum:
|
||||||
num = times.get(ip)
|
return True
|
||||||
if num > config.ip_appear_maximum:
|
|
||||||
# 解析得到IPS集合有任意IP出现次数大于指定值都标记为非法(泛解析)子域
|
|
||||||
return True
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def is_valid_subdomain(ips, ttl, times, wildcard_ips, wildcard_ttl):
|
def is_valid_subdomain(ip, ttl, times, wc_ips, wc_ttl):
|
||||||
ip_blacklist = config.brute_ip_blacklist
|
ip_blacklist = config.brute_ip_blacklist
|
||||||
ips = set(ips)
|
if ip in ip_blacklist: # 解析ip在黑名单ip则为非法子域
|
||||||
if ips.intersection(ip_blacklist): # 解析ip与黑名单ip有交集则标记为非法子域
|
|
||||||
return 0, 'IP blacklist'
|
return 0, 'IP blacklist'
|
||||||
if len(set(ttl)) == 1: # 只有一个相同TTL才进行对比
|
if all([wc_ips, wc_ttl]): # 有泛解析记录才进行对比
|
||||||
ttl = ttl[0]
|
if check_by_compare(ip, ttl, wc_ips, wc_ttl):
|
||||||
if all([wildcard_ttl, wildcard_ttl]): # 有泛解析记录才进行对比
|
return 0, 'IP wildcard'
|
||||||
if check_by_compare(ips, ttl, wildcard_ips, wildcard_ttl):
|
if check_ip_times(times):
|
||||||
return 0, 'IP wildcard '
|
|
||||||
if check_ip_times(ips, times):
|
|
||||||
return 0, 'IP exceeded'
|
return 0, 'IP exceeded'
|
||||||
return 1, None
|
return 1, 'OK'
|
||||||
|
|
||||||
|
|
||||||
def save_brute_dict(path, data):
|
def save_brute_dict(path, data):
|
||||||
@@ -611,15 +628,19 @@ class Brute(Module):
|
|||||||
ns_path = get_nameservers_path(self.enable_wildcard, ns_ip_list)
|
ns_path = get_nameservers_path(self.enable_wildcard, ns_ip_list)
|
||||||
|
|
||||||
dict_set = self.gen_brute_dict(domain)
|
dict_set = self.gen_brute_dict(domain)
|
||||||
self.subdomains = dict_set
|
dict_len = len(dict_set)
|
||||||
dict_data = '\n'.join(dict_set)
|
dict_data = '\n'.join(dict_set)
|
||||||
|
del dict_set
|
||||||
|
gc.collect()
|
||||||
|
|
||||||
dict_name = f'generated_subdomains_{domain}_{timestring}.txt'
|
dict_name = f'generated_subdomains_{domain}_{timestring}.txt'
|
||||||
dict_path = temp_dir.joinpath(dict_name)
|
dict_path = temp_dir.joinpath(dict_name)
|
||||||
save_brute_dict(dict_path, dict_data)
|
save_brute_dict(dict_path, dict_data)
|
||||||
|
del dict_data
|
||||||
|
gc.collect()
|
||||||
|
|
||||||
output_name = f'resolved_result_{domain}_{timestring}.json'
|
output_name = f'resolved_result_{domain}_{timestring}.json'
|
||||||
output_path = temp_dir.joinpath(output_name)
|
output_path = temp_dir.joinpath(output_name)
|
||||||
|
|
||||||
log_path = result_dir.joinpath('massdns.log')
|
log_path = result_dir.joinpath('massdns.log')
|
||||||
check_dict()
|
check_dict()
|
||||||
|
|
||||||
@@ -629,23 +650,19 @@ class Brute(Module):
|
|||||||
concurrent_num=self.concurrent_num)
|
concurrent_num=self.concurrent_num)
|
||||||
logger.log('INFOR', f'结束执行massdns')
|
logger.log('INFOR', f'结束执行massdns')
|
||||||
|
|
||||||
resolved_records, ip_times = deal_result(output_path)
|
ip_times = stat_ip_times(output_path)
|
||||||
|
self.records, self.subdomains = deal_result(output_path, ip_times,
|
||||||
|
wildcard_ips, wildcard_ttl)
|
||||||
delete_file(dict_path, output_path)
|
delete_file(dict_path, output_path)
|
||||||
added_records = add_times(resolved_records, ip_times)
|
|
||||||
checked_records, valid_subdomains = check_validity(added_records,
|
|
||||||
ip_times,
|
|
||||||
wildcard_ips,
|
|
||||||
wildcard_ttl)
|
|
||||||
self.records = checked_records
|
|
||||||
end = time.time()
|
end = time.time()
|
||||||
self.elapse = round(end - start, 1)
|
self.elapse = round(end - start, 1)
|
||||||
logger.log('INFOR', f'{self.source}模块耗时{self.elapse}秒'
|
logger.log('INFOR', f'{self.source}模块耗时{self.elapse}秒'
|
||||||
f'发现{domain}的子域{len(valid_subdomains)}个')
|
f'发现{domain}的子域{len(self.subdomains)}个')
|
||||||
logger.log('DEBUG', f'{self.source}模块发现{domain}的子域:\n'
|
logger.log('DEBUG', f'{self.source}模块发现{domain}的子域:\n'
|
||||||
f'{valid_subdomains}')
|
f'{self.subdomains}')
|
||||||
self.gen_result(brute=len(self.subdomains), valid=len(valid_subdomains))
|
self.gen_result(brute=dict_len, valid=len(self.subdomains))
|
||||||
self.save_db()
|
self.save_db()
|
||||||
return valid_subdomains
|
return self.subdomains
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
logger.log('INFOR', f'开始执行{self.source}模块')
|
logger.log('INFOR', f'开始执行{self.source}模块')
|
||||||
|
|||||||
Reference in New Issue
Block a user