Files
Mobile-Security-Framework-M…/mobsf/StaticAnalyzer/views/common/firebase.py
T
Ajin Abraham 4b8bab5a98 [SECURITY] Improve SSRF checks, strict path check for well_known_path (#2510)
* Improved SSRF checks (credential checks, length check, port check, path, query, and params check, ipv6, ipv4 coverage, handle possible decimal or hex IP bypasses)
* Add additional strict path check for Applink well known path
* Moved `valid_host` to `security.py`
* Update `security.md`
* Bump dependencies
* Fix docker build
2025-03-28 17:38:23 -07:00

223 lines
7.4 KiB
Python

"""Firebase Analysis."""
import logging
from urllib.parse import urlparse
from mobsf.MobSF.utils import (
append_scan_status,
upstream_proxy,
)
from mobsf.MobSF.security import valid_host
import requests
logger = logging.getLogger(__name__)
HIGH = 'high'
WARNING = 'warning'
INFO = 'info'
SECURE = 'secure'
FIREBASE_FINDINGS = {
'firebase_db_open': {
'title': 'Open Firebase database',
'severity': HIGH,
'description': (
'The Firebase database at %s is exposed'
' to internet without any authentication'),
},
'firebase_db_exists': {
'title': 'App talks to a Firebase database',
'severity': INFO,
'description': 'The app talks to Firebase database at %s',
},
'firebase_db_check_failed': {
'title': 'Firebase DB check failed',
'severity': INFO,
'description': (
'Failed to check Firebase DB URL. Error: %s'),
},
'firebase_remote_config_enabled': {
'title': 'Firebase Remote Config enabled',
'severity': WARNING,
'description': (
'The Firebase Remote Config at %s is enabled.'
' Ensure that the configurations are not sensitive.'
' This is indicated by the response: %s'),
},
'firebase_remote_config_disabled': {
'title': 'Firebase Remote Config disabled',
'severity': SECURE,
'description': (
'Firebase Remote Config is disabled for %s.'
' This is indicated by the response: %s'),
},
'firebase_remote_config_failed': {
'title': 'Firebase Remote Config check failed',
'severity': INFO,
'description': (
'Failed to check for Firebase Remote Config.'
' Please verify this manually. Error: %s'),
},
}
def firebase_analysis(checksum, code_an_dic):
"""Firebase Analysis."""
findings = []
logger.info('Starting Firebase Analysis')
# Check for Firebase Database
db_finds = firebase_db_check(
checksum, code_an_dic)
if db_finds:
findings.extend(db_finds)
# Check for Firebase Remote Config
config_finds = firebase_remote_config(
checksum, code_an_dic)
if config_finds:
findings.extend(config_finds)
return findings
def open_firebase(checksum, url):
# Detect Open Firebase Database
try:
if not valid_host(url):
logger.warning('Invalid Host: %s', url)
return url, False
purl = urlparse(url)
if not purl.netloc.lower().endswith('.firebaseio.com'):
logger.warning('Invalid Firebase URL')
return url, False
base_url = f'{purl.scheme}://{purl.netloc}/.json'
proxies, verify = upstream_proxy('https')
headers = {
'User-Agent': ('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1)'
' AppleWebKit/537.36 (KHTML, like Gecko) '
'Chrome/39.0.2171.95 Safari/537.36')}
resp = requests.get(
base_url,
timeout=5,
headers=headers,
proxies=proxies,
verify=verify,
allow_redirects=False)
if resp.status_code == 200:
return base_url, True
except Exception as exp:
msg = 'Open Firebase DB detection failed'
logger.warning(msg)
append_scan_status(checksum, msg, repr(exp))
return url, False
def firebase_db_check(checksum, code_an_dic):
logger.info('Looking for Firebase URL(s)')
findings = []
try:
urls = list(set(code_an_dic['urls_list']))
for url in urls:
if '.firebaseio.com' not in url:
continue
returl, is_open = open_firebase(checksum, url)
if is_open:
rule = FIREBASE_FINDINGS['firebase_db_open']
findings.append({
'title': rule['title'],
'severity': rule['severity'],
'description': rule['description'] % returl,
})
else:
rule = FIREBASE_FINDINGS['firebase_db_exists']
findings.append({
'title': rule['title'],
'severity': rule['severity'],
'description': rule['description'] % returl,
})
except Exception as exp:
msg = 'Failed to check for Firebase DB URL'
logger.warning(msg)
append_scan_status(checksum, msg, repr(exp))
rule = FIREBASE_FINDINGS['firebase_db_check_failed']
findings.append({
'title': rule['title'],
'severity': rule['severity'],
'description': rule['description'] % repr(exp),
})
return findings
def firebase_remote_config(checksum, code_an_dic):
"""Check for Firebase Remote Config."""
url = None
findings = []
try:
creds = code_an_dic.get('firebase_creds')
if not creds:
return None
google_api_key = creds.get('google_api_key')
google_app_id = creds.get('google_app_id')
if not (google_app_id and google_api_key):
return None
project_id = google_app_id.split(':')[1]
if not project_id.isnumeric():
return None
# https://docs.emergetools.com/docs/firebase-remote-config-api-exposed
# https://blog.deesee.xyz/android/automation/2019/08/03/
# firebase-remote-config-dump.html
logger.info('Checking for Firebase Remote Config')
url = (f'https://firebaseremoteconfig.googleapis.com/v1/projects/'
f'{project_id}/namespaces/firebase:fetch?key={google_api_key}')
body = {
'appId': google_app_id,
'appInstanceId': 'required_but_unused_value',
}
proxies, verify = upstream_proxy('https')
response = requests.post(
url,
timeout=5,
json=body,
proxies=proxies,
verify=verify,
allow_redirects=False)
if response.status_code == 200:
resp = response.json()
if resp.get('state') == 'NO_TEMPLATE':
rule = FIREBASE_FINDINGS['firebase_remote_config_disabled']
findings.append({
'title': rule['title'],
'severity': rule['severity'],
'description': rule['description'] % (url, resp),
})
else:
rule = FIREBASE_FINDINGS['firebase_remote_config_enabled']
findings.append({
'title': rule['title'],
'severity': rule['severity'],
'description': rule['description'] % (url, resp),
})
else:
rule = FIREBASE_FINDINGS['firebase_remote_config_disabled']
response_msg = f'The response code is {response.status_code}'
findings.append({
'title': rule['title'],
'severity': rule['severity'],
'description': rule['description'] % (url, response_msg),
})
except Exception as exp:
msg = 'Failed to check for Firebase Remote Config'
logger.warning(msg)
append_scan_status(checksum, msg, repr(exp))
rule = FIREBASE_FINDINGS['firebase_remote_config_failed']
findings.append({
'title': rule['title'],
'severity': rule['severity'],
'description': rule['description'] % repr(exp),
})
return findings