我目前正在学习扭曲框架,并且正在尝试使用twisted.names.client.Resolver
and制作异步 DNS 解析器twisted.names.client.getHostByName
。
该脚本应通过查询权威名称服务器来破解子域。每秒 10000-50000 个并发连接是我的最低阈值,以便考虑该工具可用于我的意图。
我的问题是:
- 扭曲是否适合/适合这种努力?
- 对于此类项目,与 C 相比,Python/Twisted 的一般性能斗争有多强?我认为扭曲并不完全适合这种想法,并且内部反应堆管理在涉及许多连接时有相当大的开销......
- 像masscan这样的项目非常快。作者设法发送了 2 Mio。数据包/秒(使用专用驱动程序 PF_RING 甚至更多)。我目前正在弄清楚他是如何做到的,但我希望我不需要走这条路,因为我想和twisted呆在一起。
变得具体:下面的脚本是我的第一次尝试,但它并没有像希望的那么快。
我强烈认为我的方法是完全错误的。如果你这样调用底部脚本:
[nikolai@niko-arch subdomains]$ python2 subdomains.py -n50 nytimes.com
www ==> ``170.149.168.130``
blog ==> ``170.149.168.153``
cs ==> ``199.181.175.242``
my ==> ``170.149.172.130``
blogs ==> ``170.149.168.153``
search ==> ``170.149.168.135``
cn ==> ``61.244.110.199``
feeds ==> ``170.149.172.130``
app ==> ``54.243.156.140``
games ==> ``184.73.175.199``
mail ==> ``170.149.172.135``
up ==> ``107.20.203.136``
tv ==> ``170.149.168.135``
data ==> ``174.129.28.73``
p ==> ``75.101.137.16``
open ==> ``170.149.168.153``
ts ==> ``170.149.97.51``
education ==> ``170.149.168.130``
wap ==> ``170.149.172.163``
m ==> ``170.149.172.163``
在大多数情况下,50 个子域请求一切正常。但是,当我指定 -n1000(以及因此 1000 个 upd dns 请求)时,它需要很长时间(5 分钟及以上),并且反应器会产生各种奇怪的错误,例如 twisted.internet.error.DNSLookupError 和 twisted.internet.defer。超时错误(示例:)Failure: twisted.internet.defer.TimeoutError: [Query('blogger.l.google.com', 255, 1)]
。通常,它只是挂起并且没有完成。
我希望每个不存在的子域都会收到一个 twisted.names.error.DNSNameError 或者在子域存在的情况下,一个有效的 A 或 AAAA 资源记录答案,但没有上面的 DNSLookupError。
谁能给我一个提示我做错了什么?通常,epoll() 应该能够轻松发送超过 1000 个请求(几年前,我在 C 和 10000 个 udp 数据报中做了同样的事情,在几秒钟内发送)。那么我没有正确理解扭曲的哪一部分?
collectResults() 不正确吗?我不知道我在做什么错..
最好提前感谢所有答案!
# Looks promising: https://github.com/zhangyuyan/github
# https://github.com/zhangyuyan/github/blob/01dd311a1f07168459b222cb5c59ac1aa4d5d614/scan-dns-e3-1.py
import os
import argparse
import exceptions
from twisted.internet import defer, reactor
import twisted.internet.error as terr
from twisted.names import client, dns, error
def printResults(results, subdomain):
"""
Print the ip address for the successful query.
"""
return '%s ==> ``%s``' % (subdomain, results)
def printError(failure, subdomain):
"""
Lookup failed for some reason, just catch the DNSNameError and DomainError.
"""
reason = failure.trap(error.DNSNameError, error.DomainError, terr.DNSLookupError, defer.TimeoutError) # Subdomain wasn't found
print(failure)
return reason
def printRes(results):
for i in results:
if not isinstance(i, type): # Why the heck are Failure objects of type 'type'???
print(i)
reactor.stop()
global res
res = results
def get_args():
parser = argparse.ArgumentParser(
description='Brute force subdomains of a supplied target domain. Fast, using async IO./n')
parser.add_argument('target_domain', type=str, help='The domain name to squeeze the subdomains from')
parser.add_argument('-r', '--default-resolver', type=str, help='Add here the ip of your preferred DNS server')
parser.add_argument('-n', '--number-connections', default=100, type=int, help='The number of file descriptors to acquire')
parser.add_argument('-f', '--subdomain-file', help='This file should contain the subdomains separated by newlines')
parser.add_argument('-v', '--verbosity', action='count', help='Increase the verbosity of output', default=0)
args = parser.parse_args()
if args.number_connections > 1000:
# root privs required to acquire more than 1024 fd's
if os.geteuid() != 0:
parser.error('You need to be root in order to use {} connections'.format(args.number_connections))
if not args.default_resolver:
# Parse /etc/resolv.conf
args.default_resolver = [line.split(' ')[1].strip() for line in open('/etc/resolv.conf', 'r').readlines() if 'nameserver' in line][0]
return args
def main(args=None):
if args:
args = args
else:
args = get_args()
subs = [sub.strip() for sub in open('subs.txt', 'r').readlines()[:args.number_connections]]
# use openDNS servers
r = client.Resolver('/etc/resolv.conf', servers=[('208.67.222.222', 53), ('208.67.220.220', 53)])
d = defer.gatherResults([r.getHostByName('%s.%s' % (subdomain, args.target_domain)).addCallbacks(printResults, printError, callbackArgs=[subdomain], errbackArgs=[subdomain]) for subdomain in subs])
d.addCallback(printRes)
reactor.run()
if __name__ == '__main__':
main()