diff --git a/cloudflare_enum.py b/cloudflare_enum.py index 6f52e06..5f2fffe 100755 --- a/cloudflare_enum.py +++ b/cloudflare_enum.py @@ -1,215 +1,427 @@ #!/usr/bin/env python + # -*- coding: utf-8 -*- + # Created using Metafidv2 by Matthew Bryant (mandatory) + # Unauthorized use is stricly prohibited, please contact mandatory@gmail.com with questions/comments. + import requests + import json + import time + import csv + import sys + import os + from bs4 import BeautifulSoup + + class cloudflare_enum: + def __init__( self ): + # Master list of headers to be used in each connection + self.global_headers = { + } + self.verbose = True + + self.s = requests.Session() + self.s.headers.update( self.global_headers ) + self.atok = '' + + def log_in( self, username, password ): + parse_dict = {} + + r = self.s.get('https://www.cloudflare.com/', ) + + new_headers = { + 'Referer': 'https://www.cloudflare.com/', + } + self.s.headers.update( dict( new_headers.items() + self.global_headers.items() ) ) + r = self.s.get('https://www.cloudflare.com/a/login', ) + parse_dict[ 'security_token_0' ] = self.find_between_r( r.text, '"security_token":"', '"}};' ) # http://xkcd.com/292/ + + post_data = { + 'email': username, + 'password': password, + 'security_token': parse_dict[ 'security_token_0' ], + } + new_headers = { + 'Referer': 'https://www.cloudflare.com/a/login', + 'Content-Type': 'application/x-www-form-urlencoded', + } + self.s.headers.update( dict( new_headers.items() + self.global_headers.items() ) ) + r = self.s.post('https://www.cloudflare.com/a/login', data=post_data) + self.atok = self.find_between_r( r.text, 'window.bootstrap = {"atok":"', '","locale":"' ) # http://xkcd.com/292/ + + def get_domain_dns( self, domain ): + parse_dict = {} + post_data = { + "betas": [], + "created_on": "2015-08-24T00:27:16.048Z", + "development_mode": False, + "jump_start": True, + "meta": {}, + "modified_on": 'null', + "name": domain, + "owner": {}, + "paused": False, + "status": "initializing", + "type": "full" + } + + new_headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'X-Requested-With': 'XMLHttpRequest', + 'Referer': 'https://www.cloudflare.com/a/add-site', + 'Pragma': 'no-cache', + 'Cache-Control': 'no-cache', + 'X-ATOK': self.atok, + } + self.s.headers.update( dict( new_headers.items() + self.global_headers.items() ) ) + r = self.s.post('https://www.cloudflare.com/api/v4/zones', data=json.dumps( post_data )) + data = json.loads( r.text ) + success = data['success'] + if not success: - print r.text + + print (r.text) + return False + + request_id = data['result']['id'] + time.sleep( 60 ) + + get_data = { + 'per_page': '100', + 'direction': 'asc', + 'page': '1', + 'order': 'type', + } + new_headers = { + 'X-Requested-With': 'XMLHttpRequest', + 'Referer': 'https://www.cloudflare.com/a/setup/' + domain + '/step/2', + 'X-ATOK': self.atok, + } + self.s.headers.update( dict( new_headers.items() + self.global_headers.items() ) ) + r = self.s.get('https://www.cloudflare.com/api/v4/zones/' + request_id + '/dns_records', params=get_data) + return_data = json.loads( r.text ) + + new_headers = { + 'X-Requested-With': 'XMLHttpRequest', + 'Referer': 'https://www.cloudflare.com/a/setup/' + domain + '/step/2', + 'X-ATOK': self.atok, + } + self.s.headers.update( dict( new_headers.items() + self.global_headers.items() ) ) + r = self.s.delete('https://www.cloudflare.com/api/v4/zones/' + request_id, ) + + get_data = { + 'status': 'initializing,pending', + 'per_page': '50', + 'page': '1', + } + new_headers = { + 'X-Requested-With': 'XMLHttpRequest', + 'Referer': 'https://www.cloudflare.com/a/add-site', + 'X-ATOK': self.atok, + } + self.s.headers.update( dict( new_headers.items() + self.global_headers.items() ) ) + r = self.s.get('https://www.cloudflare.com/api/v4/zones', params=get_data) + + return return_data['result'] + + def get_spreadsheet( self, domain ): + dns_data = self.get_domain_dns( domain ) + if dns_data: + filename = domain.replace( ".", "_" ) + ".csv" + + with open( filename, 'wb' ) as csvfile: + dns_writer = csv.writer(csvfile, delimiter=',', quotechar='|', quoting=csv.QUOTE_MINIMAL) + dns_writer.writerow( [ "name", "type", "content" ] ) + for record in dns_data: + dns_writer.writerow( [ record["name"], record["type"], record["content"] ] ) + + self.statusmsg( "Spreadsheet created at " + os.getcwd() + "/" + filename ) + + def print_banner( self ): + if self.verbose: - print """ - + + print (""" + `..--------..` + .-:///::------::///:.` + `-//:-.`````````````.-://:.` ` ` + .://-.```````````````````.-://-` : `- . + `-//:.........................-://. /. -: `:` `` + `://--------:::://////:::--------://-::.::`:- .:. + ``.---..` `///::::::///////////////////:::::::///::::::--:.`.-. + .://::::///::///::///////////////////////////:::///:-----::--:-` ` + `:/:-...--:://////////////////////////////////////////----------.--.` + `:/:..-:://////////////////////////////////////////////-----------.```` + .//-::////////////////////////////////////:::::////////-...--------...` + -/////////////////////////////////////////////::::----:. `.-::::::-..`` + ``.--:////////////////////////////////////////////////::-..```-///::::///:-` + `.:///::::://////////////////////////////////////:::::::::::::::-----......-:/:. + `-//:-----::::://///////////////////////////////:///////////////////:-::::---..-//:` + `:/:---://+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//+++//::--//: + `//:-/+oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo+++oooo+//://. + :///ossssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssosssssso+//: + `//+sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss+/- + `//+ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo+++++/. + `````````````````````````````````````````````````````````````````````````````````````` + Cloudflare DNS Enumeration Tool v1.2 + By mandatory - """ + + """) + + + def pprint( self, input_dict ): - print json.dumps(input_dict, sort_keys=True, indent=4, separators=(',', ': ')) + + print(json.dumps(input_dict, sort_keys=True, indent=4, separators=(',', ': '))) + + def statusmsg( self, msg ): + if self.verbose: - print "[ STATUS ] " + msg + + print ("[ STATUS ]") + msg + + def errormsg( self, msg ): + if self.verbose: - print "[ ERROR ] " + msg + + print ("[ ERROR ]") + msg + + def successmsg( self, msg ): + if self.verbose: - print "[ SUCCESS ] " + msg + + print ("[ SUCCESS ]") + msg + + def find_between_r( self, s, first, last ): + try: + start = s.rindex( first ) + len( first ) + end = s.rindex( last, start ) + return s[start:end] + except ValueError: + return "" + + def find_between( s, first, last ): + try: + start = s.index( first ) + len( first ) + end = s.index( last, start ) + return s[start:end] + except ValueError: + return "" + + def get_cookie_from_file( self, cookie_file ): + return_dict = {} + with open( cookie_file ) as tmp: + data = tmp.readlines() + tmp_data = [] + for i, item in enumerate(data): + if " " in data[i]: + pew = data[i].split( " " ) + return_dict[ pew[5] ] = pew[6] + + return return_dict + + if __name__ == "__main__": + if len( sys.argv ) < 3: - print "Usage: " + sys.argv[0] + " username@email.com password domain.com" + + print ("Usage: " + sys.argv[0] + " username@email.com password domain.com") + else: + cloud = cloudflare_enum() + cloud.print_banner() + cloud.log_in( sys.argv[1], sys.argv[2] ) + cloud.get_spreadsheet( sys.argv[3] )