subdomainbrute源码分析(进阶篇)

上篇文章我们介绍了这套源码当中的一些模块,并进行了实际的分析,这次我们主要是把subdomainbrute源码的流程走一遍。
(PS:看源码流程可以Pycharm调试,看不懂的地方多多print下,多动手,才能收获更多)

我们从主函数开始看:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
if __name__ == '__main__':
options, args = parse_args()
start_time = time.time()
# make tmp dirs
tmp_dir = 'tmp/%s_%s' % (args[0], int(time.time()))
if not os.path.exists(tmp_dir):
os.makedirs(tmp_dir)

multiprocessing.freeze_support()
all_process = []
dns_servers = load_dns_servers()

1、从parse_args中返回了options和args两个参数
2、会在tmp目录下新建一个目录(要是没有tmp目录就新建一个)
3、multiprocessing.freeze_support()这个函数是为了防止报错用必须加上

然后跟踪到load_dns_servers()函数,之前的文章有讲,创建Pool进程池并调用了test_server()函数,返回的是可用的dns_servers列表,继续看主函数

1
next_subs = load_next_sub(options)

跟踪到这个函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def load_next_sub(options):
next_subs = []
_set = set()
_file = 'dict/next_sub_full.txt' if options.full_scan else 'dict/next_sub.txt'
with open(_file) as f:
for line in f:
sub = line.strip()
if sub and sub not in next_subs:
tmp_set = {sub}
while tmp_set:
item = tmp_set.pop()
if item.find('{alphnum}') >= 0:
for _letter in 'abcdefghijklmnopqrstuvwxyz0123456789':
tmp_set.add(item.replace('{alphnum}', _letter, 1))
elif item.find('{alpha}') >= 0:
for _letter in 'abcdefghijklmnopqrstuvwxyz':
tmp_set.add(item.replace('{alpha}', _letter, 1))
elif item.find('{num}') >= 0:
for _letter in '0123456789':
tmp_set.add(item.replace('{num}', _letter, 1))
elif item not in _set:
_set.add(item)
next_subs.append(item)
return next_subs

这个函数特别有趣,为什么呢,这个函数有{alphnum}、{alpha}、{num},一开始以为是什么特定的表达式,特么搜了好久都不知道是什么,后来问了问其他人,这特么的就是一个字符串呐。

函数会判断是否有options.full_scan从而选择导入的文件,将文件中的数据append到next_subs中,如过读取的字符串有{alphnum}、{alpha}、{num}这样的,函数将会对这些进行替换,测试结构如下:

1
2
3
4
从txt文件中读取出来的数据:test{alphnum}
替换后的列表
['test1', 'test0', 'test3', 'test2', 'test5', 'test4', 'test7', 'test6', 'test9', 'test8', 'testq', 'testp', 'tests', 'testr', 'testu', 'testt', 'testw', 'testv', 'testy', 'testx', 'testz', 'testa', 'testc', 'testb', 'teste', 'testd', 'testg', 'testf', 'testi', 'testh', 'testk', 'testj', 'testm', 'testl', 'testo', 'testn']
[Finished in 0.3s]

看懂了吧,再回过头来看主函数:

1
2
3
scan_count = multiprocessing.Value('i', 0)
found_count = multiprocessing.Value('i', 0)
queue_size_list = multiprocessing.Array('i', options.process)

设置multiprocessing共享变量三个

1
2
3
4
5
6
7
8
9
10
print '[+] Init %s scan process.' % options.process
for process_num in range(options.process):
p = multiprocessing.Process(target=run_process,
args=(args[0], options, process_num,
dns_servers, next_subs,
scan_count, found_count,queue_size_list,
tmp_dir)
)
all_process.append(p)
p.start()

用多进程调用我们的run_process函数

1
2
3
4
5
6
7
8
def run_process(target, options, process_num, dns_servers, next_subs, scan_count, found_count, queue_size_list,
tmp_dir):
signal.signal(signal.SIGINT, user_abort)
s = SubNameBrute(target=target, options=options, process_num=process_num,
dns_servers=dns_servers, next_subs=next_subs,
scan_count=scan_count, found_count=found_count, queue_size_list=queue_size_list,
tmp_dir=tmp_dir)
s.run()

用我们的run_process函数来调用我们的run函数,这里设置了一个signal表示当我们Ctrl+C的时候会退出

1
2
3
def run(self):
threads = [gevent.spawn(self._scan, i) for i in range(self.options.threads)]
gevent.joinall(threads)

用多协程来调用我们最为重要的_scan函数了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
def _scan(self, j):
self.resolvers[j].nameservers = [self.dns_servers[j % self.dns_count]]
while not self.queue.empty():
try:
item = self.queue.get(timeout=3.0)[1]
self.scan_count_local += 1
if time.time() - self.local_time > 3.0:
self.scan_count.value += self.scan_count_local
self.scan_count_local = 0
self.queue_size_list[self.process_num] = self.queue.qsize()
except Exception as e:
break
try:
if item.find('{alphnum}') >= 0:
for _letter in 'abcdefghijklmnopqrstuvwxyz0123456789':
self.put_item(item.replace('{alphnum}', _letter, 1))
continue
elif item.find('{alpha}') >= 0:
for _letter in 'abcdefghijklmnopqrstuvwxyz':
self.put_item(item.replace('{alpha}', _letter, 1))
continue
elif item.find('{num}') >= 0:
for _letter in '0123456789':
self.put_item(item.replace('{num}', _letter, 1))
continue
elif item.find('{next_sub}') >= 0:
for _ in self.next_subs:
self.queue.put((0, item.replace('{next_sub}', _, 1)))
continue
else:
sub = item

if sub in self.found_subs:
continue

cur_sub_domain = sub + '.' + self.target
_sub = sub.split('.')[-1]
try:
answers = self.resolvers[j].query(cur_sub_domain)
except dns.resolver.NoAnswer, e:
answers = self.ex_resolver.query(cur_sub_domain)

if answers:
self.found_subs.add(sub)
ips = ', '.join(sorted([answer.address for answer in answers]))
if ips in ['1.1.1.1', '127.0.0.1', '0.0.0.0']:
continue

if self.options.i and is_intranet(answers[0].address):
continue

try:
self.scan_count_local += 1
answers = self.resolvers[j].query(cur_sub_domain, 'cname')
cname = answers[0].target.to_unicode().rstrip('.')
if cname.endswith(self.target) and cname not in self.found_subs:
self.found_subs.add(cname)
cname_sub = cname[:len(cname) - len(self.target) - 1] # new sub
self.queue.put((0, cname_sub))

except:
pass

if (_sub, ips) not in self.ip_dict:
self.ip_dict[(_sub, ips)] = 1
else:
self.ip_dict[(_sub, ips)] += 1
if self.ip_dict[(_sub, ips)] > 30:
continue

self.found_count_local += 1
if time.time() - self.local_time > 3.0:
self.found_count.value += self.found_count_local
self.found_count_local = 0
self.queue_size_list[self.process_num] = self.queue.qsize()
self.local_time = time.time()

msg = cur_sub_domain.ljust(30) + ips
# print_msg(msg, line_feed=True)

self.outfile.write(cur_sub_domain.ljust(30) + '\t' + ips + '\n')
self.outfile.flush()
try:
self.resolvers[j].query('lijiejietest.' + cur_sub_domain)
except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer) as e:
self.queue.put((999999999, '{next_sub}.' + sub))
except:
pass

except (dns.resolver.NXDOMAIN, dns.name.EmptyLabel) as e:
pass
except (dns.resolver.NoNameservers, dns.resolver.NoAnswer, dns.exception.Timeout) as e:
pass
except Exception as e:
import traceback
traceback.print_exc()
with open('errors.log', 'a') as errFile:
errFile.write('[%s] %s %s\n' % (type(e), cur_sub_domain, str(e)))


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 sher10cksec@foxmail.com

文章标题:subdomainbrute源码分析(进阶篇)

本文作者:sher10ck

发布时间:2019-01-13, 13:33:23

最后更新:2020-01-13, 13:01:24

原始链接:http://sherlocz.github.io/2019/01/13/subdomainbrute-2/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

目录