def fqdns_from_hosts(hosts, domains, timeout=2):

    result = set()

    addrs = {}

    for host in list(hosts):
        resolves_to = libdns.resolve(host)
        if resolves_to:
            for addr in resolves_to:
                addrs[addr] = set(addrs.get(addr, [])) | set(host)

    for addr, fqdns in addrs.items():

        shodan_result = shodan_get_result(addr)

        if not shodan_result:
            continue

        for port in shodan_result['data']:

            if port['transport'] != 'tcp':
                continue

            if 'ssl' not in port:
                continue

            logging.info('connecting to {}:{}'.format(addr, port['port']))

            for fqdn in fqdns:

                context = ssl.create_default_context()
                context.check_hostname = False
                context.verify_mode = ssl.CERT_NONE

                try:
                    with socket.create_connection(
                        (str(addr), int(port['port'])),
                            timeout=timeout) as sock:
                        with context.wrap_socket(sock) as ssock:
                            cert = ssock.getpeercert(binary_form=True)
                            fqdns_in_cert = set(fqdns_from_certificate(cert))
                            logging.debug('obtained fqdns: {}'.format(
                                ', '.join(fqdns_in_cert)))
                            result |= fqdns_in_cert
                except Exception as e:
                    break

    # Remove FQDNs that are not in any of the domains
    for fqdn in list(result):
        if not any([fqdn.endswith(domain) for domain in domains]):
            result.remove(fqdn)

    return result
def fqdns_from_weblinks(hostname, domains, timeout=5):

    fqdns = set()

    addrs = libdns.resolve(hostname)

    if not addrs:
        return fqdns

    addr = addrs[0]

    shodan_result = shodan_get_result(addr)

    if not shodan_result:
        return fqdns

    for port in shodan_result['data']:

        if 'http' not in port:
            continue

        if 'ssl' in port:
            scheme = 'https'
        else:
            scheme = 'http'

        url = '{}://{}:{}'.format(scheme, hostname, port['port'])

        logging.info('connecting to {}'.format(url))

        links = web_links(url)

        for link in links:
            tld = tldextract.extract(link)
            domain = '{}.{}'.format(tld.domain, tld.suffix)
            if domain in domains:
                if tld.subdomain:
                    fqdns.add('{}.{}.{}'.format(tld.subdomain, tld.domain,
                                                tld.suffix))

    return fqdns
Exemplo n.º 3
0
def cmd_fqdn_finder(domains, brute_force, connect, xfr, ctlog, weblinks, timeout, verbose, debug, json_output):
    """
    Uses various techniques to obtain valid FQDNs for the specified domains.

    \b
    1. Check for Certificate Transparency Logs
    2. Connect to specified ports, obtain SSL certificates and get FQDNs from them

    Next versions will also do the following:

    \b
    3. DNS Brute Force for common names
    4. Try DNS Zone Transfer first

    The results are cleaned to remove FQDNs that does not resolve by DNS

    Example:

    \b
    $ habu.fqdn.finder educacionit.com
    barometrosalarial.educacionit.com
    blog.educacionit.com
    ci.educacionit.com
    educacionit.com
    intranet.educacionit.com
    lecdev.educacionit.com
    lecweb.educacionit.com
    mail.educacionit.com
    plantillas.educacionit.com
    www.educacionit.com
    """

    domains = set(domains)
    #print(domains)

    if debug:
        logging.basicConfig(level=logging.DEBUG)
    elif verbose:
        logging.basicConfig(level=logging.INFO)
    else:
        logging.basicConfig(level=logging.WARNING)

    fqdns = set()

    if xfr:
        logging.info('Trying to get DNS Zones with a Zone Transfer..')
        for domain in list(domains):
            for ns_server in libdns.ns(domain):
                try:
                    z = dns.zone.from_xfr(dns.query.xfr(ns_server, domain, lifetime=5))
                except Exception as e:
                    z = None
                if z:
                    logging.info('Got DNS Zone with XFR for {} domain'.format(domain))
                    domains.remove(domain) # it's safe to remove the domains if zone available via XFR?
                    for name in z.nodes.keys():
                        fqdns.add(str(name) + '.' + domain)
                    break

    if ctlog:
        for domain in domains:
            logging.info('Getting FQDNs for {} from certificate transparency logs...'.format(domain))
            result = fqdns_from_ct_log(domain)
            logging.info('Got {} FQDNs'.format(len(result)))
            fqdns |= result

    logging.info('Removing FQDNs that does not resolve by DNS...')
    for fqdn in list(fqdns):
        if not libdns.resolve(fqdn):
            logging.info('Removing {} because does not resolves by DNS'.format(fqdn))
            fqdns.remove(fqdn)

    if connect:
        logging.info('Getting FQDNs from SSL certificates')
        result = fqdns_from_hosts(fqdns, domains=domains)
        logging.info('Got {} FQDNs'.format(len(result)))
        fqdns |= result

    if weblinks:
        logging.info('Getting FQDNs from web links')
        for fqdn in list(fqdns):
            result = fqdns_from_weblinks(fqdn, domains=domains)
            logging.info('Got {} FQDNs'.format(len(result)))
            fqdns |= result

    if brute_force:
        logging.info('Running DNS brute force...')
        for domain in domains:
            result = fqdns_from_brute_force(domain)
            logging.info('Got {} FQDNs'.format(len(result)))
            fqdns |= result

    if json_output:
        print(json.dumps(sorted(fqdns), indent=4))
    else:
        print('\n'.join(sorted(fqdns)))
def cmd_shodan(ip, cache, verbose, output_format):
    """Simple shodan API client.

    Prints the JSON result of a shodan query.

    Example:

    \b
    $ habu.shodan 216.58.222.36
    asn                      AS15169
    isp                      Google
    hostnames                eze04s06-in-f4.1e100.net, gru09s17-in-f36.1e100.net
    country_code             US
    region_code              CA
    city                     Mountain View
    org                      Google
    open_ports               tcp/443, tcp/80
    """

    try:
        ipaddress.ip_address(ip)
    except ValueError:
        ip = libdns.resolve(ip)

    if ip and isinstance(ip, list):
        ip = ip[0]
        logging.info('Resolved to {}'.format(ip))

    if not ip:
        logging.error('Invalid IP address or unresolvable name')
        return False

    habucfg = loadcfg()

    if 'SHODAN_APIKEY' not in habucfg:
        print(
            'You must provide a shodan apikey. Use the ~/.habu.json file (variable SHODAN_APIKEY), or export the variable HABU_SHODAN_APIKEY'
        )
        print('Get your API key from https://www.shodan.io/')
        sys.exit(1)

    if verbose:
        logging.basicConfig(level=logging.INFO, format='%(message)s')

    data = shodan_get_result(ip,
                             habucfg['SHODAN_APIKEY'],
                             cache=cache,
                             verbose=verbose)

    if output_format == 'json':
        print(json.dumps(data, indent=4))
        return True

    if not data:
        logging.error('Shodan seems to have no data for this host')
        return False

    if output_format == 'nmap':
        ports_string = ','.join([
            '{}:{}'.format(port['transport'][0].upper(), port['port'])
            for port in data['data']
        ])
        print(ports_string, end='')
        return True

    default_fields = [
        'asn',
        'isp',
        'hostnames',
        'country_code',
        'region_code',
        'city',
        'org',
        'os',
    ]

    for field in default_fields:
        value = data.get(field, None)
        if not value:
            continue

        if output_format == 'csv':
            if not isinstance(value, list):
                value = [value]
            for v in sorted(value):
                print('"{}","shodan.{}","{}"'.format(ip, field, v))
        else:
            if isinstance(value, list):
                value = ', '.join(sorted(value))
            print('{:<25}{}'.format(field, value))

    if output_format == 'txt':
        ports_string = ', '.join([
            '{}/{}'.format(port['transport'], port['port'])
            for port in data['data']
        ])
        print('{:<25}{}'.format('open_ports', ports_string))
    else:
        for port in data['data']:
            print('"{}","shodan.open_port","{}/{}"'.format(
                ip, port['transport'], port['port']))