Skip to content

Commit dfc6dd1

Browse files
authored
Merge pull request #35 from initstring/updated-oct
big ol chunka updates
2 parents bb1a4b6 + 6098424 commit dfc6dd1

File tree

6 files changed

+93
-21
lines changed

6 files changed

+93
-21
lines changed

README.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@ Multi-cloud OSINT tool. Enumerate public resources in AWS, Azure, and Google Clo
44
Currently enumerates the following:
55

66
**Amazon Web Services**:
7-
- Open S3 Buckets
8-
- Protected S3 Buckets
7+
- Open / Protected S3 Buckets
98
- awsapps (WorkMail, WorkDocs, Connect, etc.)
109

1110
**Microsoft Azure**:
@@ -16,8 +15,8 @@ Currently enumerates the following:
1615
- Web Apps
1716

1817
**Google Cloud Platform**
19-
- Open GCP Buckets
20-
- Protected GCP Buckets
18+
- Open / Protected GCP Buckets
19+
- Open / Protected Firebase Realtime Databases
2120
- Google App Engine sites
2221
- Cloud Functions (enumerates project/regions with existing functions, then brute forces actual function names)
2322

@@ -69,10 +68,10 @@ optional arguments:
6968
-kf KEYFILE, --keyfile KEYFILE
7069
Input file with a single keyword per line.
7170
-m MUTATIONS, --mutations MUTATIONS
72-
Mutations. Default: cloud_enum/mutations.txt.
71+
Mutations. Default: enum_tools/fuzz.txt
7372
-b BRUTE, --brute BRUTE
7473
List to brute-force Azure container names. Default:
75-
cloud_enum/brute.txt.
74+
enum_tools/fuzz.txt
7675
-t THREADS, --threads THREADS
7776
Threads for HTTP brute-force. Default = 5
7877
-ns NAMESERVER, --nameserver NAMESERVER
@@ -82,6 +81,8 @@ optional arguments:
8281
--disable-aws Disable Amazon checks.
8382
--disable-azure Disable Azure checks.
8483
--disable-gcp Disable Google checks.
84+
-qs, --quickscan Disable all mutations and second-level scans
85+
8586
```
8687

8788
# Thanks

cloud_enum.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ def parse_arguments():
7979
parser.add_argument('--disable-gcp', action='store_true',
8080
help='Disable Google checks.')
8181

82+
parser.add_argument('-qs', '--quickscan', action='store_true',
83+
help='Disable all mutations and second-level scans')
84+
8285
args = parser.parse_args()
8386

8487
# Ensure mutations file is readable
@@ -128,7 +131,10 @@ def print_status(args):
128131
Print a short pre-run status message
129132
"""
130133
print("Keywords: {}".format(', '.join(args.keyword)))
131-
print("Mutations: {}".format(args.mutations))
134+
if args.quickscan:
135+
print("Mutations: NONE! (Using quickscan)")
136+
else:
137+
print("Mutations: {}".format(args.mutations))
132138
print("Brute-list: {}".format(args.brute))
133139
print("")
134140

@@ -209,8 +215,11 @@ def main():
209215
# Give our Windows friends a chance at pretty colors
210216
check_windows()
211217

212-
# First, build a sort base list of target names
213-
mutations = read_mutations(args.mutations)
218+
# First, build a sorted base list of target names
219+
if args.quickscan:
220+
mutations = []
221+
else:
222+
mutations = read_mutations(args.mutations)
214223
names = build_names(args.keyword, mutations)
215224

216225
# All the work is done in the individual modules

enum_tools/azure_checks.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,7 @@ def run_all(names, args):
280280

281281
valid_accounts = check_storage_accounts(names, args.threads,
282282
args.nameserver)
283-
if valid_accounts:
283+
if valid_accounts and not args.quickscan:
284284
brute_force_containers(valid_accounts, args.brute, args.threads)
285285

286286
check_azure_websites(names, args.nameserver, args.threads)

enum_tools/fuzz.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ export
105105
files
106106
fileshare
107107
filestore
108+
firebase
108109
firestore
109110
functions
110111
gateway
@@ -182,6 +183,7 @@ qa
182183
repo
183184
reports
184185
resources
186+
rtdb
185187
s3
186188
saas
187189
screenshots

enum_tools/gcp_checks.py

Lines changed: 62 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
# Known GCP domain names
1616
GCP_URL = 'storage.googleapis.com'
17+
FBRTDB_URL = 'firebaseio.com'
1718
APPSPOT_URL = 'appspot.com'
1819
FUNC_URL = 'cloudfunctions.net'
1920

@@ -66,6 +67,57 @@ def check_gcp_buckets(names, threads):
6667
# Stop the time
6768
utils.stop_timer(start_time)
6869

70+
def print_fbrtdb_response(reply):
71+
"""
72+
Parses the HTTP reply of a brute-force attempt
73+
74+
This function is passed into the class object so we can view results
75+
in real-time.
76+
"""
77+
if reply.status_code == 404:
78+
pass
79+
elif reply.status_code == 200:
80+
utils.printc(" OPEN GOOGLE FIREBASE RTDB: {}\n"
81+
.format(reply.url), 'green')
82+
elif reply.status_code == 401:
83+
utils.printc(" Protected Google Firebase RTDB: {}\n"
84+
.format(reply.url), 'orange')
85+
elif reply.status_code == 402:
86+
utils.printc(" Payment required on Google Firebase RTDB: {}\n"
87+
.format(reply.url), 'orange')
88+
else:
89+
print(" Unknown status codes being received from {}:\n"
90+
" {}: {}"
91+
.format(reply.url, reply.status_code, reply.reason))
92+
93+
def check_fbrtdb(names, threads):
94+
"""
95+
Checks for Google Firebase RTDB
96+
"""
97+
print("[+] Checking for Google Firebase Realtime Databases")
98+
99+
# Start a counter to report on elapsed time
100+
start_time = utils.start_timer()
101+
102+
# Initialize the list of correctly formatted urls
103+
candidates = []
104+
105+
# Take each mutated keyword craft a url with the correct format
106+
for name in names:
107+
# Firebase RTDB names cannot include a period. We'll exlcude
108+
# those from the global candidates list
109+
if '.' not in name:
110+
candidates.append('{}.{}/.json'.format(name, FBRTDB_URL))
111+
112+
# Send the valid names to the batch HTTP processor
113+
utils.get_url_batch(candidates, use_ssl=True,
114+
callback=print_fbrtdb_response,
115+
threads=threads,
116+
redir=False)
117+
118+
# Stop the time
119+
utils.stop_timer(start_time)
120+
69121
def print_appspot_response(reply):
70122
"""
71123
Parses the HTTP reply of a brute-force attempt
@@ -75,12 +127,12 @@ def print_appspot_response(reply):
75127
"""
76128
if reply.status_code == 404:
77129
pass
78-
elif (str(reply.status_code)[0] == 5):
130+
elif str(reply.status_code)[0] == 5:
79131
utils.printc(" Google App Engine app with a 50x error: {}\n"
80132
.format(reply.url), 'orange')
81133
elif (reply.status_code == 200
82-
or reply.status_code == 302
83-
or reply.status_code == 404):
134+
or reply.status_code == 302
135+
or reply.status_code == 404):
84136
utils.printc(" Google App Engine app: {}\n"
85137
.format(reply.url), 'green')
86138
else:
@@ -156,7 +208,7 @@ def print_functions_response2(reply):
156208
" {}: {}"
157209
.format(reply.url, reply.status_code, reply.reason))
158210

159-
def check_functions(names, brute_list, threads):
211+
def check_functions(names, brute_list, quickscan, threads):
160212
"""
161213
Checks for Google Cloud Functions running on cloudfunctions.net
162214
@@ -197,6 +249,10 @@ def check_functions(names, brute_list, threads):
197249
utils.stop_timer(start_time)
198250
return
199251

252+
# Also bail out if doing a quick scan
253+
if quickscan:
254+
return
255+
200256
# If we did find something, we'll use the brute list. This will allow people
201257
# to provide a separate fuzzing list if they choose.
202258
print("[*] Brute-forcing function names in {} project/region combos"
@@ -234,5 +290,6 @@ def run_all(names, args):
234290
print(BANNER)
235291

236292
check_gcp_buckets(names, args.threads)
293+
check_fbrtdb(names, args.threads)
237294
check_appspot(names, args.threads)
238-
check_functions(names, args.brute, args.threads)
295+
check_functions(names, args.brute, args.quickscan, args.threads)

enum_tools/utils.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,16 @@ def get_url_batch(url_list, use_ssl=False, callback='', threads=5, redir=True):
5454
else:
5555
proto = 'http://'
5656

57-
# Start a requests object
58-
session = FuturesSession(executor=ThreadPoolExecutor(max_workers=threads))
59-
6057
# Using the async requests-futures module, work in batches based on
6158
# the 'queue' list created above. Call each URL, sending the results
6259
# back to the callback function.
6360
for batch in queue:
61+
# I used to initialize the session object outside of this loop, BUT
62+
# there were a lot of errors that looked related to pool cleanup not
63+
# happening. Putting it in here fixes the issue.
64+
# There is an unresolved discussion here:
65+
# https://github.com/ross/requests-futures/issues/20
66+
session = FuturesSession(executor=ThreadPoolExecutor(max_workers=threads+5))
6467
batch_pending = {}
6568
batch_results = {}
6669

@@ -76,9 +79,9 @@ def get_url_batch(url_list, use_ssl=False, callback='', threads=5, redir=True):
7679
# Timeout is set due to observation of some large jobs simply
7780
# hanging forever with no exception raised.
7881
batch_results[url] = batch_pending[url].result(timeout=30)
79-
except requests.exceptions.ConnectionError:
80-
print(" [!] Connection error on {}. Investigate if there"
81-
" are many of these.".format(url))
82+
except requests.exceptions.ConnectionError as error_msg:
83+
print(" [!] Connection error on {}:".format(url))
84+
print(error_msg)
8285
except TimeoutError:
8386
print(" [!] Timeout on {}. Investigate if there are"
8487
" many of these".format(url))

0 commit comments

Comments
 (0)