1

我目前正在学习扭曲框架,并且正在尝试使用twisted.names.client.Resolverand制作异步 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()
4

1 回答 1

1

您这样做的方式是将所有子域请求缓冲到一个巨大的列表中,然后发出所有请求,然后将查询响应缓冲到一个巨大的列表中,然后打印该列表。由于您可能只想在名称解析到达时打印它们,因此您应该安排定时调用以发出请求,可能在某个非常短的时间间隔内,以某种指定大小的批次进行。

此外,如果您对高性能 Python 感兴趣,您应该使用 PyPy 而不是 CPython。仅进行更改,即使没有使您的代码更具可扩展性,也可能使您获得足够的性能提升以达到您的目标。

于 2014-01-13T23:22:24.613 回答