-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcfenum.py
232 lines (193 loc) · 7.58 KB
/
cfenum.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
#!/usr/bin/env python3
''' Cloudflare enumeration script that leverages Cloudflare tokens.
Requires the email you used to register in Cloudflare and your Global
API key.'''
import logging
import argparse
import os
import sys
import json
import time
import CloudFlare
logging.basicConfig(
level=logging.INFO,
format='%(message)s'
)
def parsing():
''' Parser options
-d/--domain holds the domain to scan
-o/--output the file/path to store the file
'''
parser = argparse.ArgumentParser(
formatter_class=argparse.ArgumentDefaultsHelpFormatter
)
parser.add_argument('-d', '--domain',
help='The domain to scan.',
required=True
)
parser.add_argument('-o', '--output',
help='The file to store the subdomains found.',
dest='output_file',
required=True
)
parser.add_argument('--cf-email',
help='The email used to register the CF account.',
dest='cf_email'
)
parser.add_argument('--cf-token',
help='The Global API token.',
dest='cf_token'
)
return parser
def get_tokens(args):
''' Ensure that the user has set the proper tokens. If not, bail
out. Tokens should be set either as env variables or passed in
through CLI arguments. If they are not set, log and exit.
'''
if args.cf_email and args.cf_token:
cf_email = args.cf_email
cf_token = args.cf_token
elif 'CF_EMAIL' in os.environ and 'CF_TOKEN' in os.environ:
cf_email = os.environ['CF_EMAIL']
cf_token = os.environ['CF_TOKEN']
else:
logging.warning('[!] Cloudfare email and Global API token should be set'
' either as environment vars (CF_EMAIL'
', CF_TOKEN) or through the cli'
'options (--cf-email,'
'--cf-token),'
'exiting.')
sys.exit(1)
return(cf_email, cf_token)
class CFEnum:
''' Class to handle all the interactions with Cloudflare API.'''
cloudflare_api = None
zones = None
def __init__(self, email, token):
''' Main class constructor.
:param email [Cloudflare registration email]
:param token [Cloudflare Global API token]
'''
self.cloudflare_api = CloudFlare.CloudFlare(email=email, token=token)
self.zones = self.__get_zones()
def __get_zones(self):
''' Get all the zones of a user.'''
zones = []
page_number = 0
while True:
page_number += 1
try:
results = self.cloudflare_api.zones(
params={'per_page': 20,
'page': page_number})
except CloudFlare.exceptions.CloudFlareAPIError:
logging.warning('[!] Something went wrong while getting'
'zones. Exiting.')
sys.exit(1)
# For unknown reasons, CloudFlare API doesn't return page count.
if not results:
break
for result in results:
zones.append({'domain': result['name'], 'id': result['id'],
'nameservers':[result['original_name_servers'],
result['name_servers']]})
return zones
def __site_exists(self, domain):
''' Ensure that the domain requested is not in some account
already.'''
for zone in self.zones:
if domain in zone['domain']:
logging.info('[!] Domain %s already in account.', domain)
return zone['id']
return None
def __parse_results(self, domain, subdomains):
''' Parse results and return them '''
return {'domain': domain, 'subdomains' : subdomains}
def __create_zone(self, domain):
try:
zone_info = self.cloudflare_api.zones.post(data={
'jump_start': True,
'name': domain
})
return zone_info['id']
except CloudFlare.exceptions.CloudFlareAPIError as exception:
if "banned" in str(exception):
logging.warning('[!] Domain %s can\'t be added to CloudFlare.'
' Exiting.', domain)
else:
logging.warning('[!] API Error while creating zone for %s'
'. Exiting', domain)
sys.exit(1)
except Exception:
logging.warning('[!] Error while creating zone for %s.'
'Exiting', domain)
sys.exit(1)
return None
def __get_subdomains(self, zone_id):
subdomains = []
page_number = 0
while True:
page_number += 1
try:
results = self.cloudflare_api.zones.dns_records.get(
zone_id,
params={'per_page': 50,
'page': page_number})
except CloudFlare.exceptions.CloudFlareError:
logging.warning('[!] Something went wrong while getting'
'zones. Exiting.')
sys.exit(1)
# For unknown reasons, CloudFlare API doesn't return page count.
if not results:
break
for result in results:
subdomains.append({'subdomain': result['name'],
'type': result['type'],
'IP': result['content']})
return subdomains
def __delete_zone(self, zone_id):
logging.info('[*] Cleaning up zone')
try:
self.cloudflare_api.zones.delete(zone_id)
except CloudFlare.exceptions.CloudFlareError:
logging.warning('[!] Something went wrong while deleting'
'zone %s. Exiting.', zone_id)
sys.exit(1)
def get_subdomains(self, domain):
''' If user already has the given domain, return the results.
If user doesn't have it, create zone, fetch results and
remove zone.
'''
zone_id = self.__site_exists(domain)
if zone_id:
subdomains = self.__get_subdomains(zone_id)
else:
logging.info('[*] %s not in account. Creating it',
domain)
zone_id = self.__create_zone(domain)
time.sleep(30)
subdomains = self.__get_subdomains(zone_id)
logging.info('[*] Cleaning up zone created for %s', domain)
self.__delete_zone(zone_id)
return self.__parse_results(domain, subdomains)
def write_to_json(filename, data):
''' Write results to JSON file. '''
try:
with open(filename, 'w') as outfile:
json.dump(data, outfile)
file_path = os.path.abspath(outfile.name)
logging.info('[+] Results written to JSON file : %s',
file_path)
except IOError:
logging.warning('[+] Failed to write results to JSON file : %s',
file_path)
sys.exit(1)
def main():
''' Main functionality here to avoid globbing. '''
parser = parsing()
args = parser.parse_args()
tokens = get_tokens(args)
cloudfare_enum = CFEnum(tokens[0], tokens[1])
write_to_json(args.output_file, cloudfare_enum.get_subdomains(args.domain))
if __name__ == '__main__':
main()