├── 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]) --------------------------------------------------------------------------------