From 2719e0d353a683b0b9c757c45d295e814c892867 Mon Sep 17 00:00:00 2001 From: Andrew Chiles Date: Thu, 8 Mar 2018 09:11:28 +0100 Subject: [PATCH 1/4] Add DNS name resolution capability --- ctfr.py | 111 +++++++++++++++++++++++++++-------------------- requirements.txt | 3 +- 2 files changed, 67 insertions(+), 47 deletions(-) diff --git a/ctfr.py b/ctfr.py index 597878b..ebb3ac3 100644 --- a/ctfr.py +++ b/ctfr.py @@ -2,13 +2,14 @@ # -*- coding: utf-8 -*- """ ------------------------------------------------------------------------------ - CTFR - 04.03.18.02.10.00 - Sheila A. Berta (UnaPibaGeek) + CTFR - 04.03.18.02.10.00 - Sheila A. Berta (UnaPibaGeek) ------------------------------------------------------------------------------ """ ## # LIBRARIES # ## import json import requests +import dns.resolver ## # CONTEXT VARIABLES # ## version = 1.1 @@ -16,57 +17,75 @@ ## # MAIN FUNCTIONS # ## def parse_args(): - import argparse - parser = argparse.ArgumentParser() - parser.add_argument('-d', '--domain', type=str, required=True, help="Target domain.") - parser.add_argument('-o', '--output', type=str, help="Output file.") - return parser.parse_args() + import argparse + parser = argparse.ArgumentParser() + parser.add_argument('-d', '--domain', type=str, required=True, help="Target domain.") + parser.add_argument('-o', '--output', type=str, help="Output file.") + parser.add_argument('-r', '--resolve', action='store_true', help="Perform DNS Name Resolution.") + + return parser.parse_args() def banner(): - global version - from pyfiglet import figlet_format - b = figlet_format(" CTFR") + \ - ''' Version {v} - Hey don't miss AXFR! + global version + from pyfiglet import figlet_format + b = figlet_format(" CTFR") + \ + ''' Version {v} - Hey don't miss AXFR! Made by Sheila A. Berta (UnaPibaGeek) - '''.format(v=version) - print(b) + '''.format(v=version) + print(b) def save_subdomains(subdomain,output_file): - with open(output_file,"a") as f: - f.write(subdomain + '\n') - f.close() + with open(output_file,"a") as f: + f.write(subdomain + '\n') + f.close() def main(): - banner() - args = parse_args() - - subdomains = [] - target = args.domain - output = args.output - - req = requests.get("https://crt.sh/?q=%.{d}&output=json".format(d=target)) - - if req.status_code != 200: - print("[X] Error! Invalid domain or information not available!") - exit(1) - - json_data = json.loads('[{}]'.format(req.text.replace('}{', '},{'))) - - for (key,value) in enumerate(json_data): - subdomains.append(value['name_value']) - - - print("\n[!] ---- TARGET: {d} ---- [!] \n".format(d=target)) - - subdomains = sorted(set(subdomains)) - - for subdomain in subdomains: - print("[-] {s}".format(s=subdomain)) - if output is not None: - save_subdomains(subdomain,output) - - print("\n\n[!] Done. Have a nice day! ;).") - + banner() + args = parse_args() + + subdomains = [] + target = args.domain + output = args.output + resolve = args.resolve + + req = requests.get("https://crt.sh/?q=%.{d}&output=json".format(d=target)) + + if req.status_code != 200: + print("[X] Error! Invalid domain or information not available!") + exit(1) + + json_data = json.loads('[{}]'.format(req.text.replace('}{', '},{'))) + + for (key,value) in enumerate(json_data): + subdomains.append(value['name_value']) + + + print("\n[!] ---- TARGET: {d} ---- [!] \n".format(d=target)) + + subdomains = sorted(set(subdomains)) + # Perform DNS resolution + if resolve is not None: + resolver = dns.resolver.Resolver() + for subdomain in subdomains: + try: + response = resolver.query(subdomain,"A") + ips = [] + for ip in response: + ips.append(str(ip)) + except: + ips = '' + ips = ','.join(ips) + print("{s},{i}".format(s=subdomain,i=ips)) + # Continue without DNS resolution + else: + for subdomain in subdomains: + print("{s}".format(s=subdomain)) + + # Save domains to output file + if output is not None: + save_subdomains(subdomain,output) + + print("\n\n[!] Done. Have a nice day! ;).") main() - + \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 999704a..77eef26 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ requests -pyfiglet \ No newline at end of file +pyfiglet +dnspython \ No newline at end of file From ea51db96b3cb8b6673d8a34490fb55201bccef34 Mon Sep 17 00:00:00 2001 From: Andrew Chiles Date: Thu, 8 Mar 2018 14:57:26 +0100 Subject: [PATCH 2/4] Correct -r switch logic and enable proper keyboard interrupt handling --- ctfr.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/ctfr.py b/ctfr.py index ebb3ac3..164e570 100644 --- a/ctfr.py +++ b/ctfr.py @@ -51,7 +51,7 @@ def main(): req = requests.get("https://crt.sh/?q=%.{d}&output=json".format(d=target)) if req.status_code != 200: - print("[X] Error! Invalid domain or information not available!") + print("[-] Error! Invalid domain or information not available!") exit(1) json_data = json.loads('[{}]'.format(req.text.replace('}{', '},{'))) @@ -63,19 +63,24 @@ def main(): print("\n[!] ---- TARGET: {d} ---- [!] \n".format(d=target)) subdomains = sorted(set(subdomains)) + # Perform DNS resolution - if resolve is not None: + if resolve is not False: resolver = dns.resolver.Resolver() for subdomain in subdomains: try: response = resolver.query(subdomain,"A") ips = [] for ip in response: - ips.append(str(ip)) + ips.append(str(ip)) + except KeyboardInterrupt: + print("[*] Caught Keyboard Interrupt! Exiting...\n") + exit(1) except: ips = '' + ips = ','.join(ips) - print("{s},{i}".format(s=subdomain,i=ips)) + print("{s}:{i}".format(s=subdomain,i=ips)) # Continue without DNS resolution else: for subdomain in subdomains: From 3ccd035f94e5c78a7084399950d9e50972cbcef9 Mon Sep 17 00:00:00 2001 From: Andrew Chiles Date: Fri, 9 Mar 2018 10:04:10 +0100 Subject: [PATCH 3/4] Fix file output so that all names are properly written --- ctfr.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/ctfr.py b/ctfr.py index 164e570..14faed5 100644 --- a/ctfr.py +++ b/ctfr.py @@ -34,10 +34,10 @@ def banner(): '''.format(v=version) print(b) -def save_subdomains(subdomain,output_file): +def save_subdomains(subdomains,output_file): with open(output_file,"a") as f: - f.write(subdomain + '\n') - f.close() + for subdomain in subdomains: + f.write(subdomain + '\n') def main(): banner() @@ -78,19 +78,21 @@ def main(): exit(1) except: ips = '' - + # Join all returned IPs into string ips = ','.join(ips) print("{s}:{i}".format(s=subdomain,i=ips)) - # Continue without DNS resolution + + + # Print domains without performing DNS resolution else: for subdomain in subdomains: print("{s}".format(s=subdomain)) # Save domains to output file if output is not None: - save_subdomains(subdomain,output) + save_subdomains(subdomains,output) - print("\n\n[!] Done. Have a nice day! ;).") + print("\n\n[!] Done. Have a nice day! ;).") main() \ No newline at end of file From 471c6e5d473bb3580cc1361768411fea0cf5fe70 Mon Sep 17 00:00:00 2001 From: Andrew Chiles Date: Mon, 7 May 2018 13:06:33 +0200 Subject: [PATCH 4/4] Add Domain Fronting checks from @rvrsh3ll FindFrontableDomains --- ctfr.py | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 59 insertions(+), 8 deletions(-) diff --git a/ctfr.py b/ctfr.py index 14faed5..6971536 100644 --- a/ctfr.py +++ b/ctfr.py @@ -10,6 +10,8 @@ import json import requests import dns.resolver +from struct import unpack +from socket import AF_INET, inet_pton ## # CONTEXT VARIABLES # ## version = 1.1 @@ -39,6 +41,21 @@ def save_subdomains(subdomains,output_file): for subdomain in subdomains: f.write(subdomain + '\n') +# Reference link: https://stackoverflow.com/questions/691045/how-do-you-determine-if-an-ip-address-is-private-in-python +# Determine if resolved IPs are private +def lookup(ip): + f = unpack('!I',inet_pton(AF_INET,ip))[0] + private = ( + [ 2130706432, 4278190080 ], # 127.0.0.0, 255.0.0.0 http://tools.ietf.org/html/rfc3330 + [ 3232235520, 4294901760 ], # 192.168.0.0, 255.255.0.0 http://tools.ietf.org/html/rfc1918 + [ 2886729728, 4293918720 ], # 172.16.0.0, 255.240.0.0 http://tools.ietf.org/html/rfc1918 + [ 167772160, 4278190080 ], # 10.0.0.0, 255.0.0.0 http://tools.ietf.org/html/rfc1918 + ) + for net in private: + if (f & net[1]) == net[0]: + return True + return False + def main(): banner() args = parse_args() @@ -67,26 +84,60 @@ def main(): # Perform DNS resolution if resolve is not False: resolver = dns.resolver.Resolver() - for subdomain in subdomains: + for hostname in subdomains: try: - response = resolver.query(subdomain,"A") + dns.resolver.default_resolver = dns.resolver.Resolver(configure=False) + dns.resolver.default_resolver.nameservers = ['209.244.0.3', '209.244.0.4','64.6.64.6','64.6.65.6', '8.8.8.8', '8.8.4.4','84.200.69.80', '84.200.70.40', '8.26.56.26', '8.20.247.20', '208.67.222.222', '208.67.220.220','199.85.126.10', '199.85.127.10', '81.218.119.11', '209.88.198.133', '195.46.39.39', '195.46.39.40', '96.90.175.167', '193.183.98.154','208.76.50.50', '208.76.51.51', '216.146.35.35', '216.146.36.36', '37.235.1.174', '37.235.1.177', '198.101.242.72', '23.253.163.53', '77.88.8.8', '77.88.8.1', '91.239.100.100', '89.233.43.71', '74.82.42.42', '109.69.8.51'] + query = resolver.query(hostname,"A") ips = [] - for ip in response: + for ip in query: ips.append(str(ip)) + if lookup(str(ip)): + print("[!] Private IP address detected - {}:{} ".format(hostname,ip)) + + # Domain Fronting check + for i in query.response.answer: + for j in i.items: + target = j.to_text() + if not target.startswith("*"): + if 'cloudfront' in target: + print("{0}:CloundFront Frontable domain found ({1})").format(hostname,target) + elif 'appspot.com' in target: + print("{0}:Google Frontable domain found ({1})").format(hostname,target) + elif 'msecnd.net' in target: + print("{0}:Azure Frontable domain found ({1})").format(hostname,target) + elif 'aspnetcdn.com' in target: + print("{0}:Azure Frontable domain found ({1})").format(hostname,target) + elif 'azureedge.net' in target: + print("{0}:Azure Frontable domain found ({1})").format(hostname,target) + elif 'a248.e.akamai.net' in target: + print("{0}:Akamai Frontable domain found ({1})").format(hostname,target) + elif 'secure.footprint.net' in target: + print("{0}:Level 3 Frontable domain found ({1})").format(hostname,target) + elif 'cloudflare' in target: + print("{0}:Cloudflare Frontable domain found ({1})").format(hostname,target) + elif 'unbouncepages.com' in target: + print("{0}:Unbounce Frontable domain found ({1})").format(hostname,target) + elif 'amazonaws.com' in target: + print("{0}:Amazon AWS Frontable domain found ({1})").format(hostname,target) + elif 'fastly' in target: + print("{0}:Fastly Frontable domain found ({1})").format(hostname,target) + except KeyboardInterrupt: print("[*] Caught Keyboard Interrupt! Exiting...\n") exit(1) - except: + + except Exception as e: ips = '' # Join all returned IPs into string ips = ','.join(ips) - print("{s}:{i}".format(s=subdomain,i=ips)) - + print("{s}:{i}".format(s=hostname,i=ips)) + # Print domains without performing DNS resolution else: - for subdomain in subdomains: - print("{s}".format(s=subdomain)) + for hostname in subdomains: + print("{s}".format(s=hostname)) # Save domains to output file if output is not None: