├── LICENSE
├── README.md
├── maps_api_scanner.py
└── maps_api_scanner_python3.py
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Ozgur Alp
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Google Maps API Scanner
2 | Used for determining whether a leaked/found Google Maps API Key is vulnerable to unauthorized access by other applications or not.
3 |
4 | ***Blog Post #1:*** https://medium.com/bugbountywriteup/unauthorized-google-maps-api-key-usage-cases-and-why-you-need-to-care-1ccb28bf21e
5 |
6 | ***Blog Post #2:*** https://medium.com/bugbountywriteup/google-maps-api-not-the-key-bugs-that-i-found-over-the-years-781840fc82aa
7 |
8 | ***Usage:***
9 | - Download `maps_api_scanner.py` file and run as: `python maps_api_scanner.py`.
10 | - Paste API key wanted to test when asked.
11 | - Script will return `API key is vulnerable for XXX API!` message and the PoC link/code if determines any unauthorized access within this API key within any API's.
12 | - If you want to use `python3`, download `maps_api_scanner_python3.py` file and run as: `python3 maps_api_scanner_python3.py`.
13 |
14 | ***Checked APIs:***
15 | - Staticmap API
16 | - Streetview API
17 | - Embed (Basic-Free) API
18 | - Embed (Advanced-Paid) API
19 | - Directions API
20 | - Geocode API
21 | - Distance Matrix API
22 | - Find Place From Text API
23 | - Autocomplete API
24 | - Elevation API
25 | - Timezone API
26 | - Roads API
27 |
28 | ***Not Checked APIs:***
29 | - JavaScript API
30 |
31 | ***Notes:***
32 | - Because JavaScript API needs manual confirmation from a web browser directly, checks are not conducted for that API. If the script didn't found any vulnerable endpoints above or JavaScript API also wanted to be tested, manual checks can be conducted on this API within going to https://developers.google.com/maps/documentation/javascript/tutorial URL & copying HTML code and changing 'key' parameter with the one wanted to test. After opening file on the browser, if loaded without errors, then the API key is also vulnerable for JavaScript API.
33 | - For Staticmap, Streetview and Embed API's, if used from another domain instead of just testing from browser; whether referer checks are enabled or not on the server-side for the key, script still could return it as vulnerable due to a server-side vulnerability. If you cannot reproduce the vulnerability via browser while the script says so, please read the "Blog Post #2" for more information & a better understanding about what is going on.
34 | - If you find any Google Maps API's which are not mentioned in this document/script, ping me via Twitter with details so I can also add them.
35 |
--------------------------------------------------------------------------------
/maps_api_scanner.py:
--------------------------------------------------------------------------------
1 | import requests
2 | import warnings
3 | import json
4 |
5 | vulnerable_apis = []
6 | warnings.filterwarnings("ignore")
7 | apikey = raw_input("Please enter the Google Maps API key you wanted to test: ")
8 | url = "https://maps.googleapis.com/maps/api/staticmap?center=45%2C10&zoom=7&size=400x400&key="+apikey
9 | response = requests.get(url, verify=False)
10 | if response.status_code == 200:
11 | print "API key is \033[1;31;40m vulnerable \033[0m for Staticmap API! Here is the PoC link which can be used directly via browser:"
12 | print url
13 | vulnerable_apis.append("Staticmap")
14 | else:
15 | print "API key is not vulnerable for Staticmap API."
16 | print "Reason: "+ response.content
17 |
18 | url = "https://maps.googleapis.com/maps/api/streetview?size=400x400&location=40.720032,-73.988354&fov=90&heading=235&pitch=10&key="+apikey
19 | response = requests.get(url, verify=False)
20 | if response.status_code == 200:
21 | print "API key is \033[1;31;40m vulnerable \033[0m for Streetview API! Here is the PoC link which can be used directly via browser:"
22 | print url
23 | vulnerable_apis.append("Streetview")
24 | else:
25 | print "API key is not vulnerable for Streetview API."
26 | print "Reason: "+ response.content
27 |
28 | url = "https://www.google.com/maps/embed/v1/place?q=Seattle&key="+apikey
29 | response = requests.get(url, verify=False)
30 | if response.status_code == 200:
31 | print "API key is \033[1;31;40m vulnerable \033[0m for Embed (Basic-Free) API! Here is the PoC HTML code which can be used directly via browser:"
32 | print ""
33 | vulnerable_apis.append("Embed (Basic-Free)")
34 | else:
35 | print "API key is not vulnerable for (Basic-Free) Embed API."
36 | print "Reason: "+ response.content
37 |
38 | url = "https://www.google.com/maps/embed/v1/search?q=record+stores+in+Seattle&key="+apikey
39 | response = requests.get(url, verify=False)
40 | if response.status_code == 200:
41 | print "API key is \033[1;31;40m vulnerable \033[0m for Embed (Advanced-Paid) API! Here is the PoC HTML code which can be used directly via browser:"
42 | print ""
43 | vulnerable_apis.append("Embed (Advanced-Paid)")
44 | else:
45 | print "API key is not vulnerable for Embed (Advanced-Paid) API."
46 | if len(response.content.split("\"")) < 77:
47 | print "Reason: "+ response.content
48 | else:
49 | print "Reason: "+ response.content.split("\"")[77]
50 |
51 | url = "https://maps.googleapis.com/maps/api/directions/json?origin=Disneyland&destination=Universal+Studios+Hollywood4&key="+apikey
52 | response = requests.get(url, verify=False)
53 | if response.text.find("error_message") < 0:
54 | print "API key is \033[1;31;40m vulnerable \033[0m for Directions API! Here is the PoC link which can be used directly via browser:"
55 | print url
56 | vulnerable_apis.append("Directions")
57 | else:
58 | print "API key is not vulnerable for Directions API."
59 | print "Reason: "+ response.json()["error_message"]
60 |
61 | url = "https://maps.googleapis.com/maps/api/geocode/json?latlng=40,30&key="+apikey
62 | response = requests.get(url, verify=False)
63 | if response.text.find("error_message") < 0:
64 | print "API key is \033[1;31;40m vulnerable \033[0m for Geocode API! Here is the PoC link which can be used directly via browser:"
65 | print url
66 | vulnerable_apis.append("Geocode")
67 | else:
68 | print "API key is not vulnerable for Geocode API."
69 | print "Reason: "+ response.json()["error_message"]
70 |
71 | url = "https://maps.googleapis.com/maps/api/distancematrix/json?units=imperial&origins=40.6655101,-73.89188969999998&destinations=40.6905615%2C-73.9976592%7C40.6905615%2C-73.9976592%7C40.6905615%2C-73.9976592%7C40.6905615%2C-73.9976592%7C40.6905615%2C-73.9976592%7C40.6905615%2C-73.9976592%7C40.659569%2C-73.933783%7C40.729029%2C-73.851524%7C40.6860072%2C-73.6334271%7C40.598566%2C-73.7527626%7C40.659569%2C-73.933783%7C40.729029%2C-73.851524%7C40.6860072%2C-73.6334271%7C40.598566%2C-73.7527626&key="+apikey
72 | response = requests.get(url, verify=False)
73 | if response.text.find("error_message") < 0:
74 | print "API key is \033[1;31;40m vulnerable \033[0m for Distance Matrix API! Here is the PoC link which can be used directly via browser:"
75 | print url
76 | vulnerable_apis.append("Distance Matrix")
77 | else:
78 | print "API key is not vulnerable for Distance Matrix API."
79 | print "Reason: "+ response.json()["error_message"]
80 |
81 | url = "https://maps.googleapis.com/maps/api/place/findplacefromtext/json?input=Museum%20of%20Contemporary%20Art%20Australia&inputtype=textquery&fields=photos,formatted_address,name,rating,opening_hours,geometry&key="+apikey
82 | response = requests.get(url, verify=False)
83 | if response.text.find("error_message") < 0:
84 | print "API key is \033[1;31;40m vulnerable \033[0m for Find Place From Text API! Here is the PoC link which can be used directly via browser:"
85 | print url
86 | vulnerable_apis.append("Find Place From Text")
87 | else:
88 | print "API key is not vulnerable for Find Place From Text API."
89 | print "Reason: "+ response.json()["error_message"]
90 |
91 | url = "https://maps.googleapis.com/maps/api/place/autocomplete/json?input=Bingh&types=%28cities%29&key="+apikey
92 | response = requests.get(url, verify=False)
93 | if response.text.find("error_message") < 0:
94 | print "API key is \033[1;31;40m vulnerable \033[0m for Autocomplete API! Here is the PoC link which can be used directly via browser:"
95 | print url
96 | vulnerable_apis.append("Autocomplete")
97 | else:
98 | print "API key is not vulnerable for Autocomplete API."
99 | print "Reason: "+ response.json()["error_message"]
100 |
101 | url = "https://maps.googleapis.com/maps/api/elevation/json?locations=39.7391536,-104.9847034&key="+apikey
102 | response = requests.get(url, verify=False)
103 | if response.text.find("error_message") < 0:
104 | print "API key is \033[1;31;40m vulnerable \033[0m for Elevation API! Here is the PoC link which can be used directly via browser:"
105 | print url
106 | vulnerable_apis.append("Elevation")
107 | else:
108 | print "API key is not vulnerable for Elevation API."
109 | print "Reason: "+ response.json()["error_message"]
110 |
111 | url = "https://maps.googleapis.com/maps/api/timezone/json?location=39.6034810,-119.6822510×tamp=1331161200&key="+apikey
112 | response = requests.get(url, verify=False)
113 | if response.text.find("errorMessage") < 0:
114 | print "API key is \033[1;31;40m vulnerable \033[0m for Timezone API! Here is the PoC link which can be used directly via browser:"
115 | print url
116 | vulnerable_apis.append("Timezone")
117 | else:
118 | print "API key is not vulnerable for Timezone API."
119 | print "Reason: "+ response.json()["errorMessage"]
120 |
121 | url = "https://roads.googleapis.com/v1/nearestRoads?points=60.170880,24.942795|60.170879,24.942796|60.170877,24.942796&key="+apikey
122 | response = requests.get(url, verify=False)
123 | if response.text.find("error") < 0:
124 | print "API key is \033[1;31;40m vulnerable \033[0m for Roads API! Here is the PoC link which can be used directly via browser:"
125 | print url
126 | vulnerable_apis.append("Roads")
127 | else:
128 | print "API key is not vulnerable for Roads API."
129 | print "Reason: "+ response.json()["error"]["message"]
130 |
131 | url = "https://www.googleapis.com/geolocation/v1/geolocate?key="+apikey
132 | postdata = {'considerIp': 'true'}
133 | response = requests.post(url, data=postdata, verify=False)
134 | if response.text.find("error") < 0:
135 | print "API key is \033[1;31;40m vulnerable \033[0mfor Geolocation API! Here is the PoC curl command which can be used from terminal:"
136 | print "curl -i -s -k -X $'POST' -H $'Host: www.googleapis.com' -H $'Content-Length: 22' --data-binary $'{\"considerIp\": \"true\"}' $'"+url+"'"
137 | vulnerable_apis.append("Geolocation")
138 | else:
139 | print "API key is not vulnerable for Geolocation API."
140 | print "Reason: "+ response.json()["error"]["message"]
141 |
142 | print "Because JavaScript API needs manual confirmation from a web browser, tests are not conducted for that API. If the script didn't found any vulnerable endpoints above, to be sure, manual checks can be conducted on this API. For that, go to https://developers.google.com/maps/documentation/javascript/tutorial URL, copy HTML code and change 'key' parameter with the one wanted to test. If loaded without errors on the browser, then it is vulnerable for JavaScript API."
143 | print "Results:"
144 | for i in range (len(vulnerable_apis)):
145 | print "- " + vulnerable_apis[i]
--------------------------------------------------------------------------------
/maps_api_scanner_python3.py:
--------------------------------------------------------------------------------
1 | import requests
2 | import warnings
3 | import json
4 |
5 | vulnerable_apis = []
6 | warnings.filterwarnings("ignore")
7 | apikey = input("Please enter the Google Maps API key you wanted to test: ")
8 | url = "https://maps.googleapis.com/maps/api/staticmap?center=45%2C10&zoom=7&size=400x400&key="+apikey
9 | response = requests.get(url, verify=False)
10 | if response.status_code == 200:
11 | print("API key is \033[1;31;40m vulnerable \033[0m for Staticmap API! Here is the PoC link which can be used directly via browser:")
12 | print(url)
13 | vulnerable_apis.append("Staticmap")
14 | else:
15 | print("API key is not vulnerable for Staticmap API.")
16 | print("Reason: "+ str(response.content))
17 |
18 | url = "https://maps.googleapis.com/maps/api/streetview?size=400x400&location=40.720032,-73.988354&fov=90&heading=235&pitch=10&key="+apikey
19 | response = requests.get(url, verify=False)
20 | if response.status_code == 200:
21 | print("API key is \033[1;31;40m vulnerable \033[0m for Streetview API! Here is the PoC link which can be used directly via browser:")
22 | print(url)
23 | vulnerable_apis.append("Streetview")
24 | else:
25 | print("API key is not vulnerable for Streetview API.")
26 | print("Reason: "+ str(response.content))
27 |
28 | url = "https://www.google.com/maps/embed/v1/place?q=Seattle&key="+apikey
29 | response = requests.get(url, verify=False)
30 | if response.status_code == 200:
31 | print("API key is \033[1;31;40m vulnerable \033[0m for Embed (Basic-Free) API! Here is the PoC HTML code which can be used directly via browser:")
32 | print("")
33 | vulnerable_apis.append("Embed (Basic-Free)")
34 | else:
35 | print("API key is not vulnerable for Embed (Basic-Free) API.")
36 | print("Reason: "+ str(response.content))
37 |
38 | url = "https://www.google.com/maps/embed/v1/search?q=record+stores+in+Seattle&key="+apikey
39 | response = requests.get(url, verify=False)
40 | if response.status_code == 200:
41 | print("API key is \033[1;31;40m vulnerable \033[0m for Embed (Advanced-Paid) API! Here is the PoC HTML code which can be used directly via browser:")
42 | print("")
43 | vulnerable_apis.append("Embed (Advanced-Paid)")
44 | else:
45 | print("API key is not vulnerable for Embed (Advanced-Paid) API.")
46 | if len(response.content.decode().split("\"")) < 77:
47 | print("Reason: "+ str(response.content))
48 | else:
49 | print("Reason: "+ response.content.decode().split("\"")[77])
50 |
51 | url = "https://maps.googleapis.com/maps/api/directions/json?origin=Disneyland&destination=Universal+Studios+Hollywood4&key="+apikey
52 | response = requests.get(url, verify=False)
53 | if response.text.find("error_message") < 0:
54 | print("API key is \033[1;31;40m vulnerable \033[0m for Directions API! Here is the PoC link which can be used directly via browser:")
55 | print(url)
56 | vulnerable_apis.append("Directions")
57 | else:
58 | print("API key is not vulnerable for Directions API.")
59 | print("Reason: "+ str(response.json()["error_message"]))
60 |
61 | url = "https://maps.googleapis.com/maps/api/geocode/json?latlng=40,30&key="+apikey
62 | response = requests.get(url, verify=False)
63 | if response.text.find("error_message") < 0:
64 | print("API key is \033[1;31;40m vulnerable \033[0m for Geocode API! Here is the PoC link which can be used directly via browser:")
65 | print(url)
66 | vulnerable_apis.append("Geocode")
67 | else:
68 | print("API key is not vulnerable for Geocode API.")
69 | print("Reason: "+ response.json()["error_message"])
70 |
71 | url = "https://maps.googleapis.com/maps/api/distancematrix/json?units=imperial&origins=40.6655101,-73.89188969999998&destinations=40.6905615%2C-73.9976592%7C40.6905615%2C-73.9976592%7C40.6905615%2C-73.9976592%7C40.6905615%2C-73.9976592%7C40.6905615%2C-73.9976592%7C40.6905615%2C-73.9976592%7C40.659569%2C-73.933783%7C40.729029%2C-73.851524%7C40.6860072%2C-73.6334271%7C40.598566%2C-73.7527626%7C40.659569%2C-73.933783%7C40.729029%2C-73.851524%7C40.6860072%2C-73.6334271%7C40.598566%2C-73.7527626&key="+apikey
72 | response = requests.get(url, verify=False)
73 | if response.text.find("error_message") < 0:
74 | print("API key is \033[1;31;40m vulnerable \033[0m for Distance Matrix API! Here is the PoC link which can be used directly via browser:")
75 | print(url)
76 | vulnerable_apis.append("Distance Matrix")
77 | else:
78 | print("API key is not vulnerable for Distance Matrix API.")
79 | print("Reason: "+ response.json()["error_message"])
80 |
81 | url = "https://maps.googleapis.com/maps/api/place/findplacefromtext/json?input=Museum%20of%20Contemporary%20Art%20Australia&inputtype=textquery&fields=photos,formatted_address,name,rating,opening_hours,geometry&key="+apikey
82 | response = requests.get(url, verify=False)
83 | if response.text.find("error_message") < 0:
84 | print("API key is \033[1;31;40m vulnerable \033[0m for Find Place From Text API! Here is the PoC link which can be used directly via browser:")
85 | print(url)
86 | vulnerable_apis.append("Find Place From Text")
87 | else:
88 | print("API key is not vulnerable for Find Place From Text API.")
89 | print("Reason: "+ response.json()["error_message"])
90 |
91 | url = "https://maps.googleapis.com/maps/api/place/autocomplete/json?input=Bingh&types=%28cities%29&key="+apikey
92 | response = requests.get(url, verify=False)
93 | if response.text.find("error_message") < 0:
94 | print("API key is \033[1;31;40m vulnerable \033[0m for Autocomplete API! Here is the PoC link which can be used directly via browser:")
95 | print(url)
96 | vulnerable_apis.append("Autocomplete")
97 | else:
98 | print("API key is not vulnerable for Autocomplete API.")
99 | print("Reason: "+ response.json()["error_message"])
100 |
101 | url = "https://maps.googleapis.com/maps/api/elevation/json?locations=39.7391536,-104.9847034&key="+apikey
102 | response = requests.get(url, verify=False)
103 | if response.text.find("error_message") < 0:
104 | print("API key is \033[1;31;40m vulnerable \033[0m for Elevation API! Here is the PoC link which can be used directly via browser:")
105 | print(url)
106 | vulnerable_apis.append("Elevation")
107 | else:
108 | print("API key is not vulnerable for Elevation API.")
109 | print("Reason: "+ response.json()["error_message"])
110 |
111 | url = "https://maps.googleapis.com/maps/api/timezone/json?location=39.6034810,-119.6822510×tamp=1331161200&key="+apikey
112 | response = requests.get(url, verify=False)
113 | if response.text.find("errorMessage") < 0:
114 | print("API key is \033[1;31;40m vulnerable \033[0m for Timezone API! Here is the PoC link which can be used directly via browser:")
115 | print(url)
116 | vulnerable_apis.append("Timezone")
117 | else:
118 | print("API key is not vulnerable for Timezone API.")
119 | print("Reason: "+ response.json()["errorMessage"])
120 |
121 | url = "https://roads.googleapis.com/v1/nearestRoads?points=60.170880,24.942795|60.170879,24.942796|60.170877,24.942796&key="+apikey
122 | response = requests.get(url, verify=False)
123 | if response.text.find("error") < 0:
124 | print("API key is \033[1;31;40m vulnerable \033[0m for Roads API! Here is the PoC link which can be used directly via browser:")
125 | print(url)
126 | vulnerable_apis.append("Roads")
127 | else:
128 | print("API key is not vulnerable for Roads API.")
129 | print("Reason: "+ response.json()["error"]["message"])
130 |
131 | url = "https://www.googleapis.com/geolocation/v1/geolocate?key="+apikey
132 | postdata = {'considerIp': 'true'}
133 | response = requests.post(url, data=postdata, verify=False)
134 | if response.text.find("error") < 0:
135 | print("API key is \033[1;31;40m vulnerable \033[0m for Geolocation API! Here is the PoC curl command which can be used from terminal:")
136 | print("curl -i -s -k -X $'POST' -H $'Host: www.googleapis.com' -H $'Content-Length: 22' --data-binary $'{\"considerIp\": \"true\"}' $'"+url+"'")
137 | vulnerable_apis.append("Geolocation")
138 | else:
139 | print("API key is not vulnerable for Geolocation API.")
140 | print("Reason: "+ response.json()["error"]["message"])
141 |
142 | print("Because JavaScript API needs manual confirmation from a web browser, tests are not conducted for that API. If the script didn't found any vulnerable endpoints above, to be sure, manual checks can be conducted on this API. For that, go to https://developers.google.com/maps/documentation/javascript/tutorial URL, copy HTML code and change 'key' parameter with the one wanted to test. If loaded without errors on the browser, then it is vulnerable for JavaScript API.")
143 | print ("Results:")
144 | for i in range (len(vulnerable_apis)):
145 | print ("- " + vulnerable_apis[i])
--------------------------------------------------------------------------------