From 5b7c5c00751da45282a9a39ad65c86beadf73b18 Mon Sep 17 00:00:00 2001 From: Ajin Abraham Date: Sun, 4 Aug 2024 12:07:38 -0700 Subject: [PATCH] [EFR][HOTFIX] Realtime Scan status and logs (#2416) * Realtime Scan Status in UI and PDF reports * Scan Status REST API & tests * Fixes #2414 * Address #2413 * Code QA * Dependency and version bump --- .../DynamicAnalyzer/views/android/analysis.py | 1 + .../views/android/dynamic_analyzer.py | 1 + mobsf/DynamicAnalyzer/views/android/report.py | 2 +- mobsf/DynamicAnalyzer/views/common/shared.py | 8 +- mobsf/DynamicAnalyzer/views/ios/analysis.py | 4 +- mobsf/DynamicAnalyzer/views/ios/report.py | 2 +- .../views/MalwareDomainCheck.py | 30 +- mobsf/MalwareAnalyzer/views/Trackers.py | 43 ++- mobsf/MalwareAnalyzer/views/VirusTotal.py | 110 ++++-- mobsf/MalwareAnalyzer/views/android/apkid.py | 25 +- .../views/android/permissions.py | 10 +- mobsf/MalwareAnalyzer/views/android/quark.py | 14 +- mobsf/MobSF/init.py | 2 +- mobsf/MobSF/security.py | 2 + mobsf/MobSF/urls.py | 6 +- mobsf/MobSF/utils.py | 38 ++ mobsf/MobSF/views/api/api_static_analysis.py | 22 +- mobsf/MobSF/views/authorization.py | 8 +- mobsf/MobSF/views/home.py | 27 ++ mobsf/MobSF/views/scanning.py | 24 +- mobsf/StaticAnalyzer/models.py | 11 +- mobsf/StaticAnalyzer/tests.py | 15 + mobsf/StaticAnalyzer/views/android/app.py | 13 +- .../views/android/cert_analysis.py | 61 ++- .../views/android/code_analysis.py | 50 ++- .../StaticAnalyzer/views/android/converter.py | 32 +- .../views/android/db_interaction.py | 41 +- .../views/android/icon_analysis.py | 28 +- mobsf/StaticAnalyzer/views/android/jar_aar.py | 68 +++- .../android/{ => kb}/android_manifest_desc.py | 0 .../views/android/{ => kb}/dvm_permissions.py | 0 .../views/android/manifest_analysis.py | 18 +- .../views/android/manifest_utils.py | 27 +- .../views/android/network_security.py | 32 +- .../StaticAnalyzer/views/android/playstore.py | 25 +- .../views/android/rules/android_rules.yaml | 4 +- mobsf/StaticAnalyzer/views/android/so.py | 31 +- .../views/android/static_analyzer.py | 230 ++++++----- mobsf/StaticAnalyzer/views/android/strings.py | 46 ++- .../views/android/{ => views}/find.py | 0 .../android/{ => views}/manifest_view.py | 0 .../views/android/{ => views}/source_tree.py | 0 .../views/android/{ => views}/view_source.py | 0 mobsf/StaticAnalyzer/views/android/xapk.py | 26 +- mobsf/StaticAnalyzer/views/common/a.py | 34 +- .../views/common/binary/lib_analysis.py | 35 +- mobsf/StaticAnalyzer/views/common/pdf.py | 4 +- .../views/common/shared_func.py | 100 +++-- .../views/ios/app_transport_security.py | 6 - mobsf/StaticAnalyzer/views/ios/appstore.py | 22 +- .../views/ios/binary_analysis.py | 28 +- .../views/ios/binary_rule_matcher.py | 16 +- mobsf/StaticAnalyzer/views/ios/classdump.py | 45 ++- .../StaticAnalyzer/views/ios/code_analysis.py | 24 +- .../views/ios/db_interaction.py | 42 +- mobsf/StaticAnalyzer/views/ios/dylib.py | 32 +- .../StaticAnalyzer/views/ios/file_analysis.py | 19 +- .../StaticAnalyzer/views/ios/icon_analysis.py | 29 +- .../views/ios/{ => kb}/permission_analysis.py | 14 +- .../views/ios/plist_analysis.py | 28 +- .../views/ios/static_analyzer.py | 95 +++-- mobsf/StaticAnalyzer/views/ios/strings.py | 19 +- .../views/ios/{ => views}/view_source.py | 0 mobsf/StaticAnalyzer/views/sast_engine.py | 21 +- .../views/windows/db_interaction.py | 26 +- mobsf/StaticAnalyzer/views/windows/windows.py | 107 ++++-- mobsf/templates/base/base_layout.html | 20 +- mobsf/templates/general/apidocs.html | 359 +++++++++++------- mobsf/templates/general/home.html | 61 +-- mobsf/templates/pdf/android_report.html | 34 +- mobsf/templates/pdf/ios_report.html | 31 ++ mobsf/templates/pdf/windows_report.html | 34 +- .../android_binary_analysis.html | 43 ++- .../android_source_analysis.html | 36 +- .../static_analysis/ios_binary_analysis.html | 36 +- .../static_analysis/ios_source_analysis.html | 38 +- .../windows_binary_analysis.html | 36 +- poetry.lock | 40 +- pyproject.toml | 2 +- scripts/update_android_permissions.py | 2 +- 80 files changed, 1840 insertions(+), 815 deletions(-) rename mobsf/StaticAnalyzer/views/android/{ => kb}/android_manifest_desc.py (100%) rename mobsf/StaticAnalyzer/views/android/{ => kb}/dvm_permissions.py (100%) rename mobsf/StaticAnalyzer/views/android/{ => views}/find.py (100%) rename mobsf/StaticAnalyzer/views/android/{ => views}/manifest_view.py (100%) rename mobsf/StaticAnalyzer/views/android/{ => views}/source_tree.py (100%) rename mobsf/StaticAnalyzer/views/android/{ => views}/view_source.py (100%) rename mobsf/StaticAnalyzer/views/ios/{ => kb}/permission_analysis.py (88%) rename mobsf/StaticAnalyzer/views/ios/{ => views}/view_source.py (100%) diff --git a/mobsf/DynamicAnalyzer/views/android/analysis.py b/mobsf/DynamicAnalyzer/views/android/analysis.py index 7b10630034..b9d9ec9f79 100644 --- a/mobsf/DynamicAnalyzer/views/android/analysis.py +++ b/mobsf/DynamicAnalyzer/views/android/analysis.py @@ -44,6 +44,7 @@ def run_analysis(apk_dir, md5_hash, package): log_line = log_line.split(clip_tag2)[1] clipboard.append(log_line) urls, domains, emails = extract_urls_domains_emails( + md5_hash, data['traffic'].lower()) # Tar dump and fetch files all_files = get_app_files(apk_dir, package) diff --git a/mobsf/DynamicAnalyzer/views/android/dynamic_analyzer.py b/mobsf/DynamicAnalyzer/views/android/dynamic_analyzer.py index 3cde526eda..f2c14203c9 100755 --- a/mobsf/DynamicAnalyzer/views/android/dynamic_analyzer.py +++ b/mobsf/DynamicAnalyzer/views/android/dynamic_analyzer.py @@ -121,6 +121,7 @@ def dynamic_analyzer(request, checksum, api=False): try: identifier = None activities = None + deeplinks = None exported_activities = None if api: reinstall = request.POST.get('re_install', '1') diff --git a/mobsf/DynamicAnalyzer/views/android/report.py b/mobsf/DynamicAnalyzer/views/android/report.py index a51c345681..6c779b6182 100644 --- a/mobsf/DynamicAnalyzer/views/android/report.py +++ b/mobsf/DynamicAnalyzer/views/android/report.py @@ -72,7 +72,7 @@ def view_report(request, checksum, api=False): deps = dependency_analysis(package, app_dir) analysis_result = run_analysis(app_dir, checksum, package) domains = analysis_result['domains'] - trk = Trackers.Trackers(app_dir, tools_dir) + trk = Trackers.Trackers(checksum, app_dir, tools_dir) trackers = trk.get_trackers_domains_or_deps(domains, deps) generate_download(app_dir, checksum, download_dir, package) images = get_screenshots(checksum, download_dir) diff --git a/mobsf/DynamicAnalyzer/views/common/shared.py b/mobsf/DynamicAnalyzer/views/common/shared.py index 6bd3f26fdd..bfc8d9875b 100644 --- a/mobsf/DynamicAnalyzer/views/common/shared.py +++ b/mobsf/DynamicAnalyzer/views/common/shared.py @@ -23,7 +23,7 @@ logger = logging.getLogger(__name__) -def extract_urls_domains_emails(data): +def extract_urls_domains_emails(checksum, data): """Extract URLs, Domains and Emails.""" # URL Extraction urls = re.findall(URL_REGEX, data.lower()) @@ -32,8 +32,10 @@ def extract_urls_domains_emails(data): else: urls = [] # Domain Extraction and Malware Check - logger.info('Performing Malware Check on extracted Domains') - domains = MalwareDomainCheck().scan(urls) + logger.info('Performing Malware check on extracted domains') + domains = MalwareDomainCheck().scan( + checksum, + urls) # Email Etraction Regex emails = set() for email in EMAIL_REGEX.findall(data.lower()): diff --git a/mobsf/DynamicAnalyzer/views/ios/analysis.py b/mobsf/DynamicAnalyzer/views/ios/analysis.py index 550e604fb6..070b848834 100644 --- a/mobsf/DynamicAnalyzer/views/ios/analysis.py +++ b/mobsf/DynamicAnalyzer/views/ios/analysis.py @@ -46,7 +46,9 @@ def run_analysis(app_dir, bundle_id, checksum): domains = {} # Collect Log data data = get_logs_data(app_dir, bundle_id) - urls, domains, emails = extract_urls_domains_emails(data) + urls, domains, emails = extract_urls_domains_emails( + checksum, + data) # App data files analysis pfiles = get_app_files(app_dir, f'{checksum}-app-container') analysis_result['sqlite'] = pfiles['sqlite'] diff --git a/mobsf/DynamicAnalyzer/views/ios/report.py b/mobsf/DynamicAnalyzer/views/ios/report.py index 503e842458..6305db635a 100644 --- a/mobsf/DynamicAnalyzer/views/ios/report.py +++ b/mobsf/DynamicAnalyzer/views/ios/report.py @@ -67,7 +67,7 @@ def ios_view_report(request, bundle_id, api=False): return print_n_send_error_response(request, msg, api) api_analysis = ios_api_analysis(app_dir) dump_analaysis = run_analysis(app_dir, bundle_id, checksum) - trk = Trackers.Trackers(app_dir, tools_dir) + trk = Trackers.Trackers(checksum, app_dir, tools_dir) trackers = trk.get_trackers_domains_or_deps( dump_analaysis['domains'], None) screenshots = get_screenshots(checksum, download_dir) diff --git a/mobsf/MalwareAnalyzer/views/MalwareDomainCheck.py b/mobsf/MalwareAnalyzer/views/MalwareDomainCheck.py index 212e3e5e2c..63ec4169af 100644 --- a/mobsf/MalwareAnalyzer/views/MalwareDomainCheck.py +++ b/mobsf/MalwareAnalyzer/views/MalwareDomainCheck.py @@ -15,6 +15,7 @@ import IP2Location from mobsf.MobSF.utils import ( + append_scan_status, is_internet_available, settings_enabled, update_local_db, @@ -34,27 +35,6 @@ def __init__(self): self.domainlist = None self.IP2Loc = IP2Location.IP2Location() - def update_malware_db(self): - """Check for update in malware DB.""" - try: - mal_db = self.malwaredomainlist - resp = update_local_db('Malware', settings.MALWARE_DB_URL, mal_db) - if not resp: - return - # DB needs update - # Check2: DB Syntax Changed - line = resp.decode('utf-8', 'ignore').split('\n')[0] - lst = line.split('",') - if len(lst) == 10: - # DB Format is not changed. Let's update DB - logger.info('Updating Malware Database') - with open(mal_db, 'wb') as wfp: - wfp.write(resp) - else: - logger.warning('Unable to Update Malware DB') - except Exception: - logger.exception('[ERROR] Malware DB Update') - def update_maltrail_db(self): """Check for update in maltrail DB.""" try: @@ -150,7 +130,7 @@ def malware_check(self): or details_dict['ip'].startswith(domain)): self.result[domain] = details_dict except Exception: - logger.exception('[ERROR] Performing Malware Check') + logger.exception('[ERROR] Performing Malware check') def maltrail_check(self): try: @@ -178,10 +158,12 @@ def update(self): logger.warning('Internet not available. ' 'Skipping Malware Database Update.') - def scan(self, urls): + def scan(self, checksum, urls): if not settings_enabled('DOMAIN_MALWARE_SCAN'): - logger.info('Domain Malware Check disabled in settings') + logger.info('Domain Malware check disabled in settings') return self.result + msg = 'Performing Malware check on extracted domains' + append_scan_status(checksum, msg) self.domainlist = get_domains(urls) if self.domainlist: self.update() diff --git a/mobsf/MalwareAnalyzer/views/Trackers.py b/mobsf/MalwareAnalyzer/views/Trackers.py index 7827425e9b..f3a51e9a9f 100644 --- a/mobsf/MalwareAnalyzer/views/Trackers.py +++ b/mobsf/MalwareAnalyzer/views/Trackers.py @@ -14,6 +14,7 @@ from tldextract import extract from mobsf.MobSF.utils import ( + append_scan_status, find_java_binary, is_file_exists, is_internet_available, @@ -24,7 +25,8 @@ class Trackers: - def __init__(self, apk_dir, tools_dir): + def __init__(self, checksum, apk_dir, tools_dir): + self.checksum = checksum self.apk = None self.apk_dir = apk_dir self.tracker_db = os.path.join( @@ -61,17 +63,28 @@ def _update_tracker_db(self): is_db_format_good = True if is_db_format_good: # DB Format is not changed. Let's update DB - logger.info('Updating Trackers Database....') + msg = 'Updating Trackers Database....' + logger.info(msg) + append_scan_status(self.checksum, msg) with open(self.tracker_db, 'wb') as wfp: wfp.write(resp) else: - logger.info('Trackers Database format from ' - 'reports.exodus-privacy.eu.org has changed.' - ' Database is not updated. ' - 'Please report to: https://github.com/MobSF/' - 'Mobile-Security-Framework-MobSF/issues') - except Exception: - logger.exception('[ERROR] Trackers DB Update') + desc = ( + 'Trackers Database format from ' + 'reports.exodus-privacy.eu.org has changed.' + ' Database is not updated. ' + 'Please report to: https://github.com/MobSF/' + 'Mobile-Security-Framework-MobSF/issues' + ) + logger.info(desc) + append_scan_status( + self.checksum, + 'Tracker Database format changed', + desc) + except Exception as exp: + msg = '[ERROR] Trackers DB Update' + logger.exception(msg) + append_scan_status(self.checksum, msg, repr(exp)) def _compile_signatures(self): """ @@ -214,7 +227,9 @@ def detect_runtime_trackers(self, items, deps=False): def get_trackers(self): """Get Trackers.""" - logger.info('Detecting Trackers') + msg = 'Detecting Trackers' + logger.info(msg) + append_scan_status(self.checksum, msg) trackers = self.detect_trackers() tracker_dict = {'detected_trackers': len(trackers), 'total_trackers': self.nb_trackers_signature, @@ -235,7 +250,9 @@ def get_trackers_domains_or_deps(self, domains, deps): 'detected_trackers': 0, 'total_trackers': 0, 'trackers': []} - logger.info('Detecting Trackers from Domains') + msg = 'Detecting Trackers from Domains' + logger.info(msg) + append_scan_status(self.checksum, msg) # Extract Trackers from Domains x_domains = set() for d in domains: @@ -244,7 +261,9 @@ def get_trackers_domains_or_deps(self, domains, deps): trackers = self.detect_runtime_trackers(x_domains) # Extract Trackers from Runtime dependencies if deps: - logger.info('Detecting Trackers from Runtime dependencies') + msg = 'Detecting Trackers from Runtime dependencies' + logger.info(msg) + append_scan_status(self.checksum, msg) runtime = self.detect_runtime_trackers(deps, True) for i in runtime: if i not in trackers: diff --git a/mobsf/MalwareAnalyzer/views/VirusTotal.py b/mobsf/MalwareAnalyzer/views/VirusTotal.py index 8924b003ee..f9b4fa777b 100755 --- a/mobsf/MalwareAnalyzer/views/VirusTotal.py +++ b/mobsf/MalwareAnalyzer/views/VirusTotal.py @@ -5,6 +5,7 @@ from django.conf import settings from mobsf.MobSF.utils import ( + append_scan_status, file_size, get_config_loc, upstream_proxy, @@ -15,9 +16,15 @@ class VirusTotal: - base_url = settings.VIRUS_TOTAL_BASE_URL + API_KEY_ERROR = 'VirusTotal Permission denied, wrong api key?' + API_KEY_ERROR_SHORT = 'VirusTotal API error' + API_CONN_ERROR = 'VirusTotal Connection Error' - def get_report(self, file_hash): + def __init__(self, checksum): + self.base_url = settings.VIRUS_TOTAL_BASE_URL + self.checksum = checksum + + def get_report(self): """ Get Report from VT. @@ -25,6 +32,7 @@ def get_report(self, file_hash): :return: json response / None """ try: + file_hash = self.checksum url = self.base_url + 'report' params = { 'apikey': settings.VT_API_KEY, @@ -32,8 +40,10 @@ def get_report(self, file_hash): headers = {'Accept-Encoding': 'gzip, deflate'} try: proxies, verify = upstream_proxy('https') - except Exception: - logger.exception('Setting upstream proxy') + except Exception as exp: + msg = 'Setting upstream proxy' + logger.exception(msg) + append_scan_status(file_hash, msg, repr(exp)) try: response = requests.get( url, @@ -42,20 +52,28 @@ def get_report(self, file_hash): proxies=proxies, verify=verify) if response.status_code == 403: - logger.error( - 'VirusTotal Permission denied, wrong api key?') + logger.error(self.API_KEY_ERROR) + append_scan_status( + file_hash, + self.API_KEY_ERROR, + self.API_KEY_ERROR_SHORT) return None - except Exception: - logger.error( - 'VirusTotal ConnectionError, check internet connectivity') + except Exception as exp: + logger.error(self.API_CONN_ERROR) + append_scan_status( + file_hash, + self.API_CONN_ERROR, + repr(exp)) return None try: json_response = response.json() return json_response except ValueError: return None - except Exception: - logger.exception('VirusTotal get_report') + except Exception as exp: + msg = 'Failed to get report from VirusTotal' + logger.exception(msg) + append_scan_status(file_hash, msg, repr(exp)) return None def upload_file(self, file_path): @@ -75,8 +93,10 @@ def upload_file(self, file_path): headers = {'apikey': settings.VT_API_KEY} try: proxies, verify = upstream_proxy('https') - except Exception: - logger.exception('Setting upstream proxy') + except Exception as exp: + msg = 'Setting upstream proxy' + logger.exception(msg) + append_scan_status(self.checksum, msg, repr(exp)) try: response = requests.post( url, @@ -85,56 +105,74 @@ def upload_file(self, file_path): proxies=proxies, verify=verify) if response.status_code == 403: - logger.error( - 'VirusTotal Permission denied, wrong api key?') + logger.error(self.API_KEY_ERROR) + append_scan_status( + self.checksum, + self.API_KEY_ERROR, + self.API_KEY_ERROR_SHORT) return None - except Exception: - logger.error( - 'VirusTotal Connection Error, check internet connectivity') + except Exception as exp: + logger.error(self.API_CONN_ERROR) + append_scan_status( + self.checksum, + self.API_CONN_ERROR, + repr(exp)) return None json_response = response.json() return json_response - except Exception: - logger.exception('VirusTotal upload_file') + except Exception as exp: + msg = 'Failed to upload file to VirusTotal' + logger.exception(msg) + append_scan_status(self.checksum, msg, repr(exp)) return None - def get_result(self, file_path, file_hash): + def get_result(self, file_path): """ Get Results from VT. Uploading a file and getting the approval msg from VT or fetching existing report :param file_path: file's path - :param file_hash: file's hash - md5/sha1/sha256 :return: VirusTotal result json / None upon error """ try: - logger.info('VirusTotal: Check for existing report') - report = self.get_report(file_hash) + file_hash = self.checksum + msg = 'VirusTotal: Check for existing report' + logger.info(msg) + append_scan_status(file_hash, msg) + report = self.get_report() # Check for existing report if report: if report['response_code'] == 1: - logger.info('VirusTotal: %s', report['verbose_msg']) + msg = f'VirusTotal: {report["verbose_msg"]}' + logger.info(msg) + append_scan_status(file_hash, msg) return report if settings.VT_UPLOAD: - logger.info('VirusTotal: file upload') + msg = 'VirusTotal: file upload' + logger.info(msg) + append_scan_status(file_hash, msg) upload_response = self.upload_file(file_path) if upload_response: - logger.info('VirusTotal: %s', - upload_response['verbose_msg']) + msg = f'VirusTotal: {upload_response["verbose_msg"]}' + logger.info(msg) + append_scan_status(file_hash, msg) return upload_response else: - logger.info('VirusTotal Scan not performed as file' - ' upload is disabled in %s. ' - 'To enable file upload, ' - 'set VT_UPLOAD to True.', get_config_loc()) - message = ('Scan not performed, VirusTotal file' - f' upload disabled in {get_config_loc()}') + msg = ('VirusTotal Scan not performed as file ' + f'upload is disabled in {get_config_loc()}. ' + 'To enable file upload, set VT_UPLOAD to True.') + logger.info(msg) + append_scan_status(file_hash, msg) + message = ('Scan not performed, VirusTotal file ' + f'upload disabled in {get_config_loc()}') report = { 'verbose_msg': message, 'positives': 0, 'total': 0} return report - except Exception: - logger.exception('VirusTotal get_result') + except Exception as exp: + msg = 'VirusTotal: Error getting result' + logger.exception(msg) + append_scan_status(file_hash, msg, repr(exp)) diff --git a/mobsf/MalwareAnalyzer/views/android/apkid.py b/mobsf/MalwareAnalyzer/views/android/apkid.py index f1fc324a2f..97c20a0069 100644 --- a/mobsf/MalwareAnalyzer/views/android/apkid.py +++ b/mobsf/MalwareAnalyzer/views/android/apkid.py @@ -5,20 +5,23 @@ from django.conf import settings from mobsf.MobSF.utils import ( + append_scan_status, settings_enabled, ) logger = logging.getLogger(__name__) -def apkid_analysis(app_dir, apk_file, apk_name): +def apkid_analysis(checksum, apk_file): """APKID Analysis of DEX files.""" if not settings_enabled('APKID_ENABLED'): return {} try: import apkid - except ImportError: - logger.error('APKiD - Could not import APKiD') + except ImportError as exp: + msg = 'APKiD - Could not import APKiD' + logger.error(msg) + append_scan_status(checksum, msg, repr(exp)) return {} if not os.path.exists(apk_file): logger.error('APKiD - APK not found') @@ -29,7 +32,9 @@ def apkid_analysis(app_dir, apk_file, apk_name): from apkid.output import OutputFormatter from apkid.rules import RulesManager - logger.info('Running APKiD %s', apkid_ver) + msg = f'Running APKiD {apkid_ver}' + logger.info(msg) + append_scan_status(checksum, msg) options = Options( timeout=30, verbose=False, @@ -52,9 +57,15 @@ def apkid_analysis(app_dir, apk_file, apk_name): try: findings = output.build_json_output(res)['files'] except AttributeError: - logger.error('yara-python dependency required by ' - 'APKiD is not installed properly. ' - 'Skipping APKiD analysis!') + msg = ( + 'yara-python dependency required by ' + 'APKiD is not installed properly. ' + 'Skipping APKiD analysis!') + logger.error(msg) + append_scan_status( + checksum, + msg, + 'Missing dependency') findings = {} sanitized = {} for item in findings: diff --git a/mobsf/MalwareAnalyzer/views/android/permissions.py b/mobsf/MalwareAnalyzer/views/android/permissions.py index 137813e51c..c07e803b0d 100644 --- a/mobsf/MalwareAnalyzer/views/android/permissions.py +++ b/mobsf/MalwareAnalyzer/views/android/permissions.py @@ -2,6 +2,10 @@ # Check against most common malware permissions. import logging +from mobsf.MobSF.utils import ( + append_scan_status, +) + TOP_MALWARE_PERMISSIONS = [ 'android.permission.ACCEPT_HANDOVER', @@ -81,9 +85,11 @@ logger = logging.getLogger(__name__) -def check_malware_permission(perms): +def check_malware_permission(checksum, perms): """Check against most common malware permissions.""" - logger.info('Checking for Malware Permissions') + msg = 'Checking for Malware Permissions' + logger.info(msg) + append_scan_status(checksum, msg) malware_perms = [] other_perms = [] for permission in perms: diff --git a/mobsf/MalwareAnalyzer/views/android/quark.py b/mobsf/MalwareAnalyzer/views/android/quark.py index 976625feec..77eefa7e60 100644 --- a/mobsf/MalwareAnalyzer/views/android/quark.py +++ b/mobsf/MalwareAnalyzer/views/android/quark.py @@ -3,6 +3,7 @@ from pathlib import Path from mobsf.MobSF.utils import ( + append_scan_status, disable_print, enable_print, settings_enabled, @@ -11,7 +12,7 @@ logger = logging.getLogger(__name__) -def quark_analysis(app_dir, apk_file): +def quark_analysis(checksum, app_dir, apk_file): """QUARK Analysis of APK files.""" if not settings_enabled('QUARK_ENABLED'): return [] @@ -29,8 +30,9 @@ def quark_analysis(app_dir, apk_file): from quark import config from quark.freshquark import download from quark.report import Report - - logger.info('Running Quark %s', quark_ver) + msg = f'Running Quark {quark_ver}' + logger.info(msg) + append_scan_status(checksum, msg) json_report = {} try: # freshquark: update quark rules @@ -51,8 +53,10 @@ def quark_analysis(app_dir, apk_file): report.quark.apkinfo.find_method.cache_clear() report.quark.apkinfo.upperfunc.cache_clear() report.quark.apkinfo.get_wrapper_smali.cache_clear() - except Exception: - logger.exception('Quark APK Analysis') + except Exception as exp: + msg = 'Quark APK Analysis' + logger.exception(msg) + append_scan_status(checksum, msg, repr(exp)) return _convert_report(json_report, app_dir) diff --git a/mobsf/MobSF/init.py b/mobsf/MobSF/init.py index a79123b752..d848d7cc28 100644 --- a/mobsf/MobSF/init.py +++ b/mobsf/MobSF/init.py @@ -10,7 +10,7 @@ logger = logging.getLogger(__name__) -VERSION = '4.0.5' +VERSION = '4.0.6' BANNER = """ __ __ _ ____ _____ _ _ ___ | \/ | ___ | |__/ ___|| ___|_ _| || | / _ \ diff --git a/mobsf/MobSF/security.py b/mobsf/MobSF/security.py index 91763646d0..4258d73366 100644 --- a/mobsf/MobSF/security.py +++ b/mobsf/MobSF/security.py @@ -86,6 +86,8 @@ def get_executable_hashes(): 'BinSkim.exe', 'BinScope.exe', 'nuget.exe', + 'where.exe', + 'wkhtmltopdf.exe', ] for sbin in system_bins: bin_path = which(sbin) diff --git a/mobsf/MobSF/urls.py b/mobsf/MobSF/urls.py index 4167cb29af..8f0d87354f 100755 --- a/mobsf/MobSF/urls.py +++ b/mobsf/MobSF/urls.py @@ -38,7 +38,7 @@ shared_func, suppression, ) -from mobsf.StaticAnalyzer.views.android import ( +from mobsf.StaticAnalyzer.views.android.views import ( find, manifest_view, source_tree, @@ -47,7 +47,7 @@ from mobsf.StaticAnalyzer.views.windows import windows from mobsf.StaticAnalyzer.views.android import static_analyzer as android_sa from mobsf.StaticAnalyzer.views.ios import static_analyzer as ios_sa -from mobsf.StaticAnalyzer.views.ios import view_source as io_view_source +from mobsf.StaticAnalyzer.views.ios.views import view_source as io_view_source from . import settings @@ -85,6 +85,7 @@ # Static Analysis re_path(r'^api/v1/upload$', api_sz.api_upload), re_path(r'^api/v1/scan$', api_sz.api_scan), + re_path(r'^api/v1/scan_logs$', api_sz.api_scan_logs), re_path(r'^api/v1/delete_scan$', api_sz.api_delete_scan), re_path(r'^api/v1/download_pdf$', api_sz.api_pdf_report), re_path(r'^api/v1/report_json$', api_sz.api_json_report), @@ -195,6 +196,7 @@ name='scans_paginated'), re_path(r'^delete_scan/$', home.delete_scan, name='delete_scan'), re_path(r'^search$', home.search), + re_path(r'^status/$', home.scan_status, name='status'), re_path(r'^error/$', home.error, name='error'), re_path(r'^not_found/$', home.not_found), re_path(r'^zip_format/$', home.zip_format), diff --git a/mobsf/MobSF/utils.py b/mobsf/MobSF/utils.py index 57a606cc8d..b74bebc406 100755 --- a/mobsf/MobSF/utils.py +++ b/mobsf/MobSF/utils.py @@ -31,6 +31,9 @@ import requests from django.shortcuts import render +from django.utils import timezone + +from mobsf.StaticAnalyzer.models import RecentScansDB from . import settings @@ -908,3 +911,38 @@ def valid_host(host): return True except Exception: return False + + +def append_scan_status(checksum, status, exception=None): + """Append Scan Status to Database.""" + try: + db_obj = RecentScansDB.objects.get(MD5=checksum) + if status == 'init': + db_obj.SCAN_LOGS = [] + db_obj.save() + return + current_logs = python_dict(db_obj.SCAN_LOGS) + current_logs.append({ + 'timestamp': timezone.now().strftime('%Y-%m-%d %H:%M:%S'), + 'status': status, + 'exception': exception}) + db_obj.SCAN_LOGS = current_logs + db_obj.save() + except RecentScansDB.DoesNotExist: + # Expected to fail for iOS Dynamic Analysis Report Generation + # Calls MalwareScan and TrackerScan with different checksum + pass + except Exception: + logger.exception('Appending Scan Status to Database') + + +def get_scan_logs(checksum): + """Get the scan logs for the given checksum.""" + try: + db_entry = RecentScansDB.objects.filter(MD5=checksum) + if db_entry.exists(): + return python_list(db_entry[0].SCAN_LOGS) + except Exception: + msg = 'Fetching scan logs from the DB failed.' + logger.exception(msg) + return [] diff --git a/mobsf/MobSF/views/api/api_static_analysis.py b/mobsf/MobSF/views/api/api_static_analysis.py index 3aa824afe3..879ad69d04 100755 --- a/mobsf/MobSF/views/api/api_static_analysis.py +++ b/mobsf/MobSF/views/api/api_static_analysis.py @@ -8,14 +8,15 @@ RecentScansDB, ) from mobsf.MobSF.utils import ( + get_scan_logs, is_md5, ) from mobsf.MobSF.views.helpers import request_method from mobsf.MobSF.views.home import RecentScans, Upload, delete_scan from mobsf.MobSF.views.api.api_middleware import make_api_response -from mobsf.StaticAnalyzer.views.android import view_source +from mobsf.StaticAnalyzer.views.android.views import view_source from mobsf.StaticAnalyzer.views.android.static_analyzer import static_analyzer -from mobsf.StaticAnalyzer.views.ios import view_source as ios_view_source +from mobsf.StaticAnalyzer.views.ios.views import view_source as ios_view_source from mobsf.StaticAnalyzer.views.ios.static_analyzer import static_analyzer_ios from mobsf.StaticAnalyzer.views.common.shared_func import compare_apps from mobsf.StaticAnalyzer.views.common.suppression import ( @@ -92,6 +93,23 @@ def api_scan(request): return response +@request_method(['POST']) +@csrf_exempt +def api_scan_logs(request): + """POST - Get Scan logs.""" + if 'hash' not in request.POST: + return make_api_response( + {'error': 'Missing Parameters'}, 422) + resp = get_scan_logs(request.POST['hash']) + if not resp: + return make_api_response( + {'error': 'No scan logs found'}, 400) + response = make_api_response({ + 'logs': resp, + }, 200) + return response + + @request_method(['POST']) @csrf_exempt def api_delete_scan(request): diff --git a/mobsf/MobSF/views/authorization.py b/mobsf/MobSF/views/authorization.py index 15efdcf769..b1d724036e 100644 --- a/mobsf/MobSF/views/authorization.py +++ b/mobsf/MobSF/views/authorization.py @@ -36,6 +36,8 @@ logger = logging.getLogger(__name__) register.filter('md5', get_md5) + + PERM_CAN_SCAN = 'can_scan' PERM_CAN_SUPPRESS = 'can_suppress' PERM_CAN_DELETE = 'can_delete' @@ -47,12 +49,6 @@ class Permissions(Enum): DELETE = f'StaticAnalyzer.{PERM_CAN_DELETE}' -class DjangoPermissions(Enum): - SCAN = (PERM_CAN_SCAN, 'Scan Files') - SUPPRESS = (PERM_CAN_SUPPRESS, 'Suppress Findings') - DELETE = (PERM_CAN_DELETE, 'Delete Scans') - - MAINTAINER_GROUP = 'Maintainer' VIEWER_GROUP = 'Viewer' diff --git a/mobsf/MobSF/views/home.py b/mobsf/MobSF/views/home.py index f3a79cd32b..f5345c12fd 100755 --- a/mobsf/MobSF/views/home.py +++ b/mobsf/MobSF/views/home.py @@ -12,6 +12,7 @@ from django.conf import settings from django.core.paginator import Paginator from django.http import HttpResponse, HttpResponseRedirect +from django.views.decorators.http import require_http_methods from django.utils import timezone from django.shortcuts import ( redirect, @@ -29,6 +30,7 @@ is_safe_path, key, print_n_send_error_response, + python_dict, ) from mobsf.MobSF.views.helpers import FileType from mobsf.MobSF.views.scanning import Scanning @@ -39,6 +41,10 @@ StaticAnalyzerIOS, StaticAnalyzerWindows, ) +from mobsf.DynamicAnalyzer.views.common.shared import ( + invalid_params, + send_response, +) from mobsf.MobSF.views.authentication import ( login_required, ) @@ -344,6 +350,27 @@ def search(request): return HttpResponseRedirect('/not_found/') return print_n_send_error_response(request, 'Invalid Scan Hash') +# AJAX + + +@login_required +@require_http_methods(['POST']) +def scan_status(request, api=False): + """Get Current Status of a scan in progress.""" + try: + scan_hash = request.POST['hash'] + if not is_md5(scan_hash): + return invalid_params(api) + robj = RecentScansDB.objects.filter(MD5=scan_hash) + if not robj.exists(): + data = {'status': 'failed', 'error': 'scan hash not found'} + return send_response(data, api) + data = {'status': 'ok', 'logs': python_dict(robj[0].SCAN_LOGS)} + except Exception as exp: + logger.exception('Fetching Scan Status') + data = {'status': 'failed', 'message': str(exp)} + return send_response(data, api) + @login_required def download(request): diff --git a/mobsf/MobSF/views/scanning.py b/mobsf/MobSF/views/scanning.py index 590cf15c55..68ed6a5b3e 100644 --- a/mobsf/MobSF/views/scanning.py +++ b/mobsf/MobSF/views/scanning.py @@ -77,7 +77,7 @@ def scan_apk(self): self.data['hash'] = md5 self.data['scan_type'] = 'apk' add_to_recent_scan(self.data) - logger.info('Performing Static Analysis of Android APK') + logger.info('Android APK uploaded') return self.data def scan_xapk(self): @@ -86,7 +86,7 @@ def scan_xapk(self): self.data['hash'] = md5 self.data['scan_type'] = 'xapk' add_to_recent_scan(self.data) - logger.info('Performing Static Analysis of Android XAPK base APK') + logger.info('Android XAPK uploaded') return self.data def scan_apks(self): @@ -95,7 +95,7 @@ def scan_apks(self): self.data['hash'] = md5 self.data['scan_type'] = 'apks' add_to_recent_scan(self.data) - logger.info('Performing Static Analysis of Android Split APK') + logger.info('Android Split APK uploaded') return self.data def scan_aab(self): @@ -104,7 +104,7 @@ def scan_aab(self): self.data['hash'] = md5 self.data['scan_type'] = 'aab' add_to_recent_scan(self.data) - logger.info('Performing Static Analysis of Android App Bundle') + logger.info('Android App Bundle uploaded') return self.data def scan_jar(self): @@ -113,7 +113,7 @@ def scan_jar(self): self.data['hash'] = md5 self.data['scan_type'] = 'jar' add_to_recent_scan(self.data) - logger.info('Performing Static Analysis of Java JAR') + logger.info('Java JAR uploaded') return self.data def scan_aar(self): @@ -122,7 +122,7 @@ def scan_aar(self): self.data['hash'] = md5 self.data['scan_type'] = 'aar' add_to_recent_scan(self.data) - logger.info('Performing Static Analysis of Android AAR') + logger.info('Android AAR uploaded') return self.data def scan_so(self): @@ -131,7 +131,7 @@ def scan_so(self): self.data['hash'] = md5 self.data['scan_type'] = 'so' add_to_recent_scan(self.data) - logger.info('Performing Static Analysis of Shared Object') + logger.info('Shared Object Library uploaded') return self.data def scan_zip(self): @@ -140,7 +140,7 @@ def scan_zip(self): self.data['hash'] = md5 self.data['scan_type'] = 'zip' add_to_recent_scan(self.data) - logger.info('Performing Static Analysis of Android/iOS Source Code') + logger.info('Android/iOS Source code ZIP uploaded') return self.data def scan_ipa(self): @@ -150,7 +150,7 @@ def scan_ipa(self): self.data['scan_type'] = 'ipa' self.data['analyzer'] = 'static_analyzer_ios' add_to_recent_scan(self.data) - logger.info('Performing Static Analysis of iOS IPA') + logger.info('iOS IPA uploaded') return self.data def scan_dylib(self): @@ -160,7 +160,7 @@ def scan_dylib(self): self.data['scan_type'] = 'dylib' self.data['analyzer'] = 'static_analyzer_ios' add_to_recent_scan(self.data) - logger.info('Performing Static Analysis of iOS IPA') + logger.info('iOS dylib uploaded') return self.data def scan_a(self): @@ -170,7 +170,7 @@ def scan_a(self): self.data['scan_type'] = 'a' self.data['analyzer'] = 'static_analyzer_ios' add_to_recent_scan(self.data) - logger.info('Performing Static Analysis of Static Library') + logger.info('Static Library uploaded') return self.data def scan_appx(self): @@ -180,5 +180,5 @@ def scan_appx(self): self.data['scan_type'] = 'appx' self.data['analyzer'] = 'static_analyzer_windows' add_to_recent_scan(self.data) - logger.info('Performing Static Analysis of Windows APP') + logger.info('Windows APPX uploaded') return self.data diff --git a/mobsf/StaticAnalyzer/models.py b/mobsf/StaticAnalyzer/models.py index cd5f61b9c5..f3f5fc23d0 100755 --- a/mobsf/StaticAnalyzer/models.py +++ b/mobsf/StaticAnalyzer/models.py @@ -1,10 +1,14 @@ from datetime import datetime +from enum import Enum from django.db import models -from mobsf.MobSF.views.authorization import ( - DjangoPermissions, -) + +class DjangoPermissions(Enum): + SCAN = ('can_scan', 'Scan Files') + SUPPRESS = ('can_suppress', 'Suppress Findings') + DELETE = ('can_delete', 'Delete Scans') + P = DjangoPermissions @@ -23,6 +27,7 @@ class Meta: VERSION_NAME = models.CharField(max_length=50, default='') MD5 = models.CharField(max_length=32, default='', primary_key=True) TIMESTAMP = models.DateTimeField(default=datetime.now) + SCAN_LOGS = models.TextField(default=[]) class StaticAnalyzerAndroid(models.Model): diff --git a/mobsf/StaticAnalyzer/tests.py b/mobsf/StaticAnalyzer/tests.py index aeee70840a..bb7fbcf7be 100755 --- a/mobsf/StaticAnalyzer/tests.py +++ b/mobsf/StaticAnalyzer/tests.py @@ -241,6 +241,21 @@ def api_test(): logger.error('Scan List API Test with custom http header 2') return True logger.info('[OK] Scan List API tests completed') + # Scan logs tests + logger.info('Running Scan Logs API tests') + for upl in uploaded: + resp = http_client.post( + '/api/v1/scan_logs', + {'hash': upl['hash']}, + HTTP_AUTHORIZATION=auth) + if resp.status_code == 200: + logs = json.loads(resp.content.decode('utf-8')) + if 'logs' in logs and len(logs['logs']) > 0: + logger.info('[OK] Scan Logs API test: %s', upl['hash']) + else: + logger.error('Scan Logs API test: %s', upl['hash']) + return True + logger.info('[OK] Static Analysis API test completed') # PDF Tests logger.info('Running PDF Generation API Test') if platform.system() in ['Darwin', 'Linux']: diff --git a/mobsf/StaticAnalyzer/views/android/app.py b/mobsf/StaticAnalyzer/views/android/app.py index acb29140cd..76b2b230ed 100644 --- a/mobsf/StaticAnalyzer/views/android/app.py +++ b/mobsf/StaticAnalyzer/views/android/app.py @@ -8,16 +8,23 @@ from mobsf.StaticAnalyzer.tools.androguard4 import ( apk, ) +from mobsf.MobSF.utils import ( + append_scan_status, +) logger = logging.getLogger(__name__) -def parse_apk(app_path): +def parse_apk(checksum, app_path): """Androguard APK.""" try: - logger.info('Parsing APK with androguard') + msg = 'Parsing APK with androguard' + logger.info(msg) + append_scan_status(checksum, msg) return apk.APK(app_path) - except Exception: + except Exception as exp: + msg = 'Failed to parse APK with androguard' + append_scan_status(checksum, msg, repr(exp)) return None diff --git a/mobsf/StaticAnalyzer/views/android/cert_analysis.py b/mobsf/StaticAnalyzer/views/android/cert_analysis.py index d8f2843c59..c23dbda582 100755 --- a/mobsf/StaticAnalyzer/views/android/cert_analysis.py +++ b/mobsf/StaticAnalyzer/views/android/cert_analysis.py @@ -21,6 +21,7 @@ from django.utils.html import escape from mobsf.MobSF.utils import ( + append_scan_status, find_java_binary, gen_sha256_hash, ) @@ -41,10 +42,12 @@ } -def get_hardcoded_cert_keystore(files): +def get_hardcoded_cert_keystore(checksum, files): """Returns the hardcoded certificate keystore.""" try: - logger.info('Getting Hardcoded Certificates/Keystores') + msg = 'Getting Hardcoded Certificates/Keystores' + logger.info(msg) + append_scan_status(checksum, msg) findings = [] certz = [] key_store = [] @@ -64,8 +67,10 @@ def get_hardcoded_cert_keystore(files): desc = 'Hardcoded Keystore found.' findings.append({'finding': desc, 'files': key_store}) return findings - except Exception: - logger.exception('Getting Hardcoded Certificates/Keystores') + except Exception as exp: + msg = 'Getting Hardcoded Certificates/Keystores' + append_scan_status(checksum, msg, repr(exp)) + logger.exception(msg) def get_cert_details(data): @@ -123,7 +128,7 @@ def get_pub_key_details(data): return certlist -def get_signature_versions(app_path, tools_dir, signed): +def get_signature_versions(checksum, app_path, tools_dir, signed): """Get signature versions using apksigner.""" v1, v2, v3, v4 = False, False, False, False try: @@ -146,12 +151,14 @@ def get_signature_versions(app_path, tools_dir, signed): v3 = True if re.findall(r'\(APK Signature Scheme v4\): true', out): v4 = True - except Exception: - logger.exception('Failed to get signature versions') + except Exception as exp: + msg = 'Failed to get signature versions' + logger.exception(msg) + append_scan_status(checksum, msg, repr(exp)) return v1, v2, v3, v4 -def apksigtool_cert(apk_path, tools_dir): +def apksigtool_cert(checksum, apk_path, tools_dir): """Get Human readable certificate with apksigtool.""" certlist = [] certs = [] @@ -188,7 +195,11 @@ def apksigtool_cert(apk_path, tools_dir): certlist.append('Binary is signed') else: certlist.append('Binary is not signed') - v1, v2, v3, v4 = get_signature_versions(apk_path, tools_dir, signed) + v1, v2, v3, v4 = get_signature_versions( + checksum, + apk_path, + tools_dir, + signed) certlist.append(f'v1 signature: {v1}') certlist.append(f'v2 signature: {v2}') certlist.append(f'v3 signature: {v3}') @@ -196,8 +207,10 @@ def apksigtool_cert(apk_path, tools_dir): certlist.extend(certs) certlist.extend(pub_keys) certlist.append(f'Found {certs_no} unique certificates') - except Exception: - logger.exception('Failed to parse code signing certificate') + except Exception as exp: + msg = 'Failed to parse code signing certificate' + logger.exception(msg) + append_scan_status(checksum, msg, repr(exp)) certlist.append('Missing certificate') return { 'cert_data': '\n'.join(certlist), @@ -210,7 +223,7 @@ def apksigtool_cert(apk_path, tools_dir): } -def get_cert_data(a, app_path, tools_dir): +def get_cert_data(checksum, a, app_path, tools_dir): """Get Human readable certificate.""" certlist = [] signed = False @@ -220,7 +233,11 @@ def get_cert_data(a, app_path, tools_dir): else: certlist.append('Binary is not signed') certlist.append('Missing certificate') - v1, v2, v3, v4 = get_signature_versions(app_path, tools_dir, signed) + v1, v2, v3, v4 = get_signature_versions( + checksum, + app_path, + tools_dir, + signed) certlist.append(f'v1 signature: {v1}') certlist.append(f'v2 signature: {v2}') certlist.append(f'v3 signature: {v3}') @@ -254,7 +271,9 @@ def get_cert_data(a, app_path, tools_dir): def cert_info(a, app_dic, man_dict): """Return certificate information.""" try: - logger.info('Reading Code Signing Certificate') + msg = 'Reading Code Signing Certificate' + logger.info(msg) + append_scan_status(app_dic['md5'], msg) manifestfile = None manidat = '' files = [] @@ -262,12 +281,16 @@ def cert_info(a, app_dic, man_dict): if a: cert_data = get_cert_data( - a, app_dic['app_path'], app_dic['tools_dir']) + app_dic['md5'], + a, app_dic['app_path'], + app_dic['tools_dir']) else: logger.warning('androguard certificate parsing failed,' ' switching to apksigtool') cert_data = apksigtool_cert( - app_dic['app_path'], app_dic['tools_dir']) + app_dic['md5'], + app_dic['app_path'], + app_dic['tools_dir']) cert_path = os.path.join(app_dic['app_dir'], 'META-INF/') if os.path.exists(cert_path): @@ -359,6 +382,8 @@ def cert_info(a, app_dic, man_dict): 'certificate_findings': findings, 'certificate_summary': summary, } - except Exception: - logger.exception('Reading Code Signing Certificate') + except Exception as exp: + msg = 'Reading Code Signing Certificate' + logger.exception(msg) + append_scan_status(app_dic['md5'], msg, repr(exp)) return {} diff --git a/mobsf/StaticAnalyzer/views/android/code_analysis.py b/mobsf/StaticAnalyzer/views/android/code_analysis.py index 58a0e0b396..6a2edd1c78 100755 --- a/mobsf/StaticAnalyzer/views/android/code_analysis.py +++ b/mobsf/StaticAnalyzer/views/android/code_analysis.py @@ -11,6 +11,7 @@ import yaml from mobsf.MobSF.utils import ( + append_scan_status, filename_from_path, get_android_src_dir, settings_enabled, @@ -26,8 +27,8 @@ logger = logging.getLogger(__name__) -def get_perm_rules(perm_rules, android_permissions): - """Get applicablepermission rules.""" +def get_perm_rules(checksum, perm_rules, android_permissions): + """Get applicable permission rules.""" try: if not settings_enabled('PERM_MAPPING_ENABLED'): return None @@ -47,8 +48,10 @@ def get_perm_rules(perm_rules, android_permissions): tmp.write(rules) tmp.close() return tmp - except Exception: - logger.error('Getting Permission Rules') + except Exception as exp: + msg = 'Getting Permission Rules' + logger.error(msg) + append_scan_status(checksum, msg, repr(exp)) return None @@ -60,7 +63,7 @@ def permission_transform(perm_mappings): return mappings -def code_analysis(app_dir, typ, manifest_file, android_permissions): +def code_analysis(checksum, app_dir, typ, manifest_file, android_permissions): """Perform the code analysis.""" try: root = Path(settings.BASE_DIR) / 'StaticAnalyzer' / 'views' @@ -78,35 +81,48 @@ def code_analysis(app_dir, typ, manifest_file, android_permissions): app_dir = Path(app_dir) src = get_android_src_dir(app_dir, typ).as_posix() + '/' skp = settings.SKIP_CLASS_PATH - logger.info('Code Analysis Started on - %s', - filename_from_path(src)) + msg = f'Code Analysis Started on - {filename_from_path(src)}' + logger.info(msg) + append_scan_status(checksum, msg) # Code Analysis code_findings = scan( + checksum, code_rules.as_posix(), {'.java', '.kt'}, [src], skp) - logger.info('Android SAST Completed') + msg = 'Android SAST Completed' + logger.info(msg) + append_scan_status(checksum, msg) # API Analysis - logger.info('Android API Analysis Started') + msg = 'Android API Analysis Started' + logger.info(msg) + append_scan_status(checksum, msg) api_findings = scan( + checksum, api_rules.as_posix(), {'.java', '.kt'}, [src], skp) # Permission Mapping - rule_file = get_perm_rules(perm_rules, android_permissions) + rule_file = get_perm_rules(checksum, perm_rules, android_permissions) if rule_file: - logger.info('Android Permission Mapping Started') + msg = 'Android Permission Mapping Started' + logger.info(msg) + append_scan_status(checksum, msg) perm_mappings = permission_transform(scan( + checksum, rule_file.name, {'.java', '.kt'}, [src], {})) - logger.info('Android Permission Mapping Completed') + msg = 'Android Permission Mapping Completed' + logger.info(msg) + append_scan_status(checksum, msg) os.unlink(rule_file.name) # NIAP Scan niap_findings = niap_scan( + checksum, niap_rules.as_posix(), {'.java', '.xml'}, [src], @@ -131,7 +147,9 @@ def code_analysis(app_dir, typ, manifest_file, android_permissions): url_list.extend(urls) url_n_file.extend(urls_nf) email_n_file.extend(emails_nf) - logger.info('Finished Code Analysis, Email and URL Extraction') + msg = 'Finished Code Analysis, Email and URL Extraction' + logger.info(msg) + append_scan_status(checksum, msg) code_an_dic = { 'api': api_findings, 'perm_mappings': perm_mappings, @@ -142,5 +160,7 @@ def code_analysis(app_dir, typ, manifest_file, android_permissions): 'emails': email_n_file, } return code_an_dic - except Exception: - logger.exception('Performing Code Analysis') + except Exception as exp: + msg = 'Failed to perform code analysis' + logger.exception(msg) + append_scan_status(checksum, msg, repr(exp)) diff --git a/mobsf/StaticAnalyzer/views/android/converter.py b/mobsf/StaticAnalyzer/views/android/converter.py index 423d0e4929..06e3747101 100755 --- a/mobsf/StaticAnalyzer/views/android/converter.py +++ b/mobsf/StaticAnalyzer/views/android/converter.py @@ -13,6 +13,7 @@ from django.conf import settings from mobsf.MobSF.utils import ( + append_scan_status, filename_from_path, find_java_binary, is_file_exists, @@ -29,12 +30,14 @@ def get_dex_files(app_dir): return glob.glob(glob_pattern) -def dex_2_smali(app_dir, tools_dir): +def dex_2_smali(checksum, app_dir, tools_dir): """Run dex2smali.""" try: if not settings_enabled('DEX2SMALI_ENABLED'): return - logger.info('DEX -> SMALI') + msg = 'Converting DEX to Smali' + logger.info(msg) + append_scan_status(checksum, msg) dexes = get_dex_files(app_dir) for dex_path in dexes: try: @@ -61,17 +64,20 @@ def dex_2_smali(app_dir, tools_dir): except Exception: # Fixes a bug #2014 pass - except Exception: - logger.exception('Converting DEX to SMALI') + except Exception as exp: + msg = 'Failed to convert DEX to Smali' + logger.exception(msg) + append_scan_status(checksum, msg, repr(exp)) -def apk_2_java(app_path, app_dir, tools_dir): +def apk_2_java(checksum, app_path, app_dir, tools_dir): """Run jadx.""" try: - logger.info('APK -> JAVA') + msg = 'Decompiling APK to Java with jadx' + logger.info(msg) + append_scan_status(checksum, msg) args = [] output = os.path.join(app_dir, 'java_source/') - logger.info('Decompiling to Java with jadx') if os.path.exists(output): # ignore WinError3 in Windows @@ -101,7 +107,11 @@ def apk_2_java(app_path, app_dir, tools_dir): stdout=fnull, stderr=subprocess.STDOUT, timeout=settings.JADX_TIMEOUT) - except subprocess.TimeoutExpired: - logger.warning('Decompiling with jadx timed out') - except Exception: - logger.exception('Decompiling to JAVA') + except subprocess.TimeoutExpired as exp: + msg = 'Decompiling with jadx timed out' + logger.warning(msg) + append_scan_status(checksum, msg, repr(exp)) + except Exception as exp: + msg = 'Decompiling with jadx failed' + logger.exception(msg) + append_scan_status(checksum, msg, repr(exp)) diff --git a/mobsf/StaticAnalyzer/views/android/db_interaction.py b/mobsf/StaticAnalyzer/views/android/db_interaction.py index 7bdf5a157b..c4f44d9456 100755 --- a/mobsf/StaticAnalyzer/views/android/db_interaction.py +++ b/mobsf/StaticAnalyzer/views/android/db_interaction.py @@ -4,7 +4,12 @@ from django.conf import settings from django.db.models import QuerySet -from mobsf.MobSF.utils import python_dict, python_list +from mobsf.MobSF.utils import ( + append_scan_status, + get_scan_logs, + python_dict, + python_list, +) from mobsf.MobSF.views.home import update_scan_timestamp from mobsf.StaticAnalyzer.models import StaticAnalyzerAndroid from mobsf.StaticAnalyzer.models import RecentScansDB @@ -22,7 +27,8 @@ def get_context_from_db_entry(db_entry: QuerySet) -> dict: """Return the context for APK/ZIP from DB.""" try: - logger.info('Analysis is already Done. Fetching data from the DB...') + msg = 'Analysis is already Done. Fetching data from the DB...' + logger.info(msg) package = db_entry[0].PACKAGE_NAME code = process_suppression( python_dict(db_entry[0].CODE_ANALYSIS), @@ -81,10 +87,12 @@ def get_context_from_db_entry(db_entry: QuerySet) -> dict: 'trackers': python_dict(db_entry[0].TRACKERS), 'playstore_details': python_dict(db_entry[0].PLAYSTORE_DETAILS), 'secrets': python_list(db_entry[0].SECRETS), + 'logs': get_scan_logs(db_entry[0].MD5), } return context except Exception: - logger.exception('Fetching from DB') + msg = 'Fetching data from the DB failed.' + logger.exception(msg) def get_context_from_analysis(app_dic, @@ -153,10 +161,13 @@ def get_context_from_analysis(app_dic, 'trackers': trackers, 'playstore_details': app_dic['playstore'], 'secrets': code_an_dic['secrets'], + 'logs': get_scan_logs(app_dic['md5']), } return context - except Exception: - logger.exception('Rendering to Template') + except Exception as exp: + msg = 'Rendering to Template failed.' + logger.exception(msg) + append_scan_status(app_dic['md5'], msg, repr(exp)) def save_or_update(update_type, @@ -226,8 +237,10 @@ def save_or_update(update_type, else: StaticAnalyzerAndroid.objects.filter( MD5=app_dic['md5']).update(**values) - except Exception: - logger.exception('Updating DB') + except Exception as exp: + msg = 'Failed to Save/Update Database' + logger.exception(msg) + append_scan_status(app_dic['md5'], msg, repr(exp)) try: values = { 'APP_NAME': app_dic['real_name'], @@ -236,18 +249,24 @@ def save_or_update(update_type, } RecentScansDB.objects.filter( MD5=app_dic['md5']).update(**values) - except Exception: - logger.exception('Updating RecentScansDB') + except Exception as exp: + msg = 'Updating RecentScansDB table failed' + logger.exception(msg) + append_scan_status(app_dic['md5'], msg, repr(exp)) def save_get_ctx(app, man, m_anal, code, cert, elf, apkid, quark, trk, rscn): # SAVE TO DB if rscn: - logger.info('Updating Database...') + msg = 'Updating Database...' + logger.info(msg) + append_scan_status(app['md5'], msg) action = 'update' update_scan_timestamp(app['md5']) else: - logger.info('Saving to Database') + msg = 'Saving to Database' + logger.info(msg) + append_scan_status(app['md5'], msg) action = 'save' save_or_update( action, diff --git a/mobsf/StaticAnalyzer/views/android/icon_analysis.py b/mobsf/StaticAnalyzer/views/android/icon_analysis.py index 225b89fe7e..44fc28f2bd 100755 --- a/mobsf/StaticAnalyzer/views/android/icon_analysis.py +++ b/mobsf/StaticAnalyzer/views/android/icon_analysis.py @@ -14,6 +14,7 @@ from django.conf import settings from mobsf.MobSF.utils import ( + append_scan_status, find_java_binary, is_file_exists, is_path_traversal, @@ -86,7 +87,10 @@ def get_icon_from_src(app_dic, icon_from_mfst): if not res_path: return - icon_file = find_icon_path_zip(res_path, icon_from_mfst) + icon_file = find_icon_path_zip( + app_dic['md5'], + res_path, + icon_from_mfst) if icon_file and Path(icon_file).exists(): dwd = Path(settings.DWD_DIR) out = dwd / (app_dic['md5'] + '-icon' + Path(icon_file).suffix) @@ -94,7 +98,7 @@ def get_icon_from_src(app_dic, icon_from_mfst): app_dic['icon_path'] = out.name -def find_icon_path_zip(res_dir, icon_paths_from_manifest): +def find_icon_path_zip(checksum, res_dir, icon_paths_from_manifest): """ Find icon. @@ -104,7 +108,9 @@ def find_icon_path_zip(res_dir, icon_paths_from_manifest): """ global KNOWN_MIPMAP_SIZES try: - logger.info('Guessing icon path') + msg = 'Guessing icon path' + logger.info(msg) + append_scan_status(checksum, msg) for icon_path in icon_paths_from_manifest: if icon_path.startswith('@'): path_array = icon_path.strip('@').split(os.sep) @@ -135,8 +141,10 @@ def find_icon_path_zip(res_dir, icon_paths_from_manifest): # If didn't find, try the default name.. returns empty if not find return guess_icon_path(res_dir) - except Exception: - logger.exception('Guessing icon path') + except Exception as exp: + msg = 'Failed to find icon path' + logger.exception(msg) + append_scan_status(checksum, msg, repr(exp)) # PNG icon lookup functions above ^ # SVG/XML icon lookup functions below @@ -148,7 +156,9 @@ def get_icon_src(a, app_dic, res_dir): path is a full path (not relative to resource folder) """ try: - logger.info('Fetching icon path') + msg = 'Fetching icon path' + logger.info(msg) + append_scan_status(app_dic['md5'], msg) icon_src = '' app_dir = Path(app_dic['app_dir']) icon_resolution = 0xFFFE - 1 @@ -204,8 +214,10 @@ def get_icon_src(a, app_dic, res_dir): logger.warning('Cannot find icon file') icon_src = '' return icon_src - except Exception: - logger.exception('Fetching icon function') + except Exception as exp: + msg = 'Failed to fetch icon path' + logger.exception(msg) + append_scan_status(app_dic['md5'], msg, repr(exp)) def get_icon_apk(apk, app_dic): diff --git a/mobsf/StaticAnalyzer/views/android/jar_aar.py b/mobsf/StaticAnalyzer/views/android/jar_aar.py index fa9364c5f3..197b92e913 100644 --- a/mobsf/StaticAnalyzer/views/android/jar_aar.py +++ b/mobsf/StaticAnalyzer/views/android/jar_aar.py @@ -9,6 +9,7 @@ import mobsf.MalwareAnalyzer.views.VirusTotal as VirusTotal from mobsf.MalwareAnalyzer.views.android import permissions from mobsf.MobSF.utils import ( + append_scan_status, file_size, print_n_send_error_response, ) @@ -62,10 +63,11 @@ def common_analysis(request, app_dic, rescan, api, analysis_type): + checksum = app_dic['md5'] app_dic['app_file'] = f'{app_dic["md5"]}.{analysis_type}' # NEW FILENAME app_dic['app_path'] = (app_dic['app_dir'] / app_dic['app_file']).as_posix() app_dic['app_dir'] = app_dic['app_dir'].as_posix() + '/' - db_entry = StaticAnalyzerAndroid.objects.filter(MD5=app_dic['md5']) + db_entry = StaticAnalyzerAndroid.objects.filter(MD5=checksum) if db_entry.exists() and not rescan: context = get_context_from_db_entry(db_entry) else: @@ -74,22 +76,34 @@ def common_analysis(request, app_dic, rescan, api, analysis_type): request, 'Permission Denied', False) + append_scan_status(checksum, 'init') + # Analysis Starts here app_dic['size'] = f'{str(file_size(app_dic["app_path"]))}MB' - app_dic['sha1'], app_dic['sha256'] = hash_gen(app_dic['app_path']) - app_dic['files'] = unzip(app_dic['app_path'], app_dic['app_dir']) + app_dic['sha1'], app_dic['sha256'] = hash_gen( + checksum, + app_dic['app_path']) + app_dic['files'] = unzip( + checksum, + app_dic['app_path'], + app_dic['app_dir']) logger.info('%s Extracted', analysis_type.upper()) if not app_dic['files']: return print_n_send_error_response( request, f'{analysis_type.upper()} file is invalid or corrupt', api) - app_dic['certz'] = get_hardcoded_cert_keystore(app_dic['files']) + app_dic['certz'] = get_hardcoded_cert_keystore( + checksum, + app_dic['files']) app_dic['playstore'] = {'error': True} # Parse APK with Androguard - apk = parse_apk(app_dic['app_path']) + apk = parse_apk( + checksum, + app_dic['app_path']) if analysis_type == 'aar': # AAR has manifest and sometimes certificate mani_file, ns, mani_xml = get_manifest( + checksum, app_dic['app_path'], app_dic['app_dir'], app_dic['tools_dir'], @@ -98,8 +112,12 @@ def common_analysis(request, app_dic, rescan, api, analysis_type): app_dic['manifest_file'] = mani_file app_dic['ns'] = ns app_dic['parsed_xml'] = mani_xml - man_data_dic = manifest_data(app_dic['parsed_xml'], ns) + man_data_dic = manifest_data( + checksum, + app_dic['parsed_xml'], + ns) man_an_dic = manifest_analysis( + checksum, app_dic['parsed_xml'], ns, man_data_dic, @@ -109,6 +127,7 @@ def common_analysis(request, app_dic, rescan, api, analysis_type): # Malware Permission check mal_perms = permissions.check_malware_permission( + checksum, man_data_dic['perm']) man_an_dic['malware_permissions'] = mal_perms @@ -160,42 +179,46 @@ def common_analysis(request, app_dic, rescan, api, analysis_type): } app_dic['real_name'] = '' elf_dict = library_analysis( + checksum, app_dic['app_dir'], - app_dic['md5'], 'elf') tracker = Trackers.Trackers( + checksum, app_dic['app_dir'], app_dic['tools_dir']) tracker_res = tracker.get_trackers() - apk_2_java( + checksum, app_dic['app_path'], app_dic['app_dir'], app_dic['tools_dir']) - code_an_dic = code_analysis( + checksum, app_dic['app_dir'], 'apk', app_dic['manifest_file'], man_data_dic['perm']) - obfuscated_check(app_dic['app_dir'], code_an_dic) + obfuscated_check( + checksum, + app_dic['app_dir'], + code_an_dic) quark_results = [] # Get the strings and metadata get_strings_metadata( + checksum, apk, app_dic['app_dir'], elf_dict['elf_strings'], 'apk', ['.java'], code_an_dic) - # Firebase DB Check code_an_dic['firebase'] = firebase_analysis( + checksum, code_an_dic['urls_list']) # Domain Extraction and Malware Check - logger.info( - 'Performing Malware Check on extracted Domains') code_an_dic['domains'] = MalwareDomainCheck().scan( + checksum, code_an_dic['urls_list']) app_dic['zipped'] = analysis_type @@ -217,10 +240,9 @@ def common_analysis(request, app_dic, rescan, api, analysis_type): context['dynamic_analysis_done'] = False context['virus_total'] = None if settings.VT_ENABLED: - vt = VirusTotal.VirusTotal() + vt = VirusTotal.VirusTotal(checksum) context['virus_total'] = vt.get_result( - app_dic['app_path'], - app_dic['md5']) + app_dic['app_path']) template = 'static_analysis/android_binary_analysis.html' if api: return context @@ -236,9 +258,11 @@ def aar_analysis(request, app_dic, rescan, api): return common_analysis(request, app_dic, rescan, api, 'aar') -def obfuscated_check(src, code_an_dic): +def obfuscated_check(checksum, src, code_an_dic): """Check if JAR/AAR is obfuscated.""" - logger.info('Checking for Obfuscation') + msg = 'Checking for Obfuscation' + logger.info(msg) + append_scan_status(checksum, msg) metadata = { 'cvss': 0, 'cwe': '', @@ -258,7 +282,7 @@ def obfuscated_check(src, code_an_dic): continue out = app_dir / f'{j.name}_out' if not out.exists(): - unzip(j, out) + unzip(checksum, j, out) # Search all class files for i in app_dir.rglob('*.class'): if not i.is_file(): @@ -271,8 +295,10 @@ def obfuscated_check(src, code_an_dic): 'metadata': metadata, } return - except Exception: - logger.exception('Obfuscation Check') + except Exception as exp: + msg = 'Obfuscation Check Failed' + logger.exception(msg) + append_scan_status(checksum, msg, repr(exp)) metadata['description'] = ( 'The binary might be obfuscated.' ' LocalVariableTable is absent in class file.') diff --git a/mobsf/StaticAnalyzer/views/android/android_manifest_desc.py b/mobsf/StaticAnalyzer/views/android/kb/android_manifest_desc.py similarity index 100% rename from mobsf/StaticAnalyzer/views/android/android_manifest_desc.py rename to mobsf/StaticAnalyzer/views/android/kb/android_manifest_desc.py diff --git a/mobsf/StaticAnalyzer/views/android/dvm_permissions.py b/mobsf/StaticAnalyzer/views/android/kb/dvm_permissions.py similarity index 100% rename from mobsf/StaticAnalyzer/views/android/dvm_permissions.py rename to mobsf/StaticAnalyzer/views/android/kb/dvm_permissions.py diff --git a/mobsf/StaticAnalyzer/views/android/manifest_analysis.py b/mobsf/StaticAnalyzer/views/android/manifest_analysis.py index e8868ca011..877c02b526 100755 --- a/mobsf/StaticAnalyzer/views/android/manifest_analysis.py +++ b/mobsf/StaticAnalyzer/views/android/manifest_analysis.py @@ -7,14 +7,17 @@ from concurrent.futures import ThreadPoolExecutor from mobsf.MobSF.utils import ( + append_scan_status, is_number, upstream_proxy, valid_host, ) from mobsf.StaticAnalyzer.views.android import ( - android_manifest_desc, network_security, ) +from mobsf.StaticAnalyzer.views.android.kb import ( + android_manifest_desc, +) logger = logging.getLogger(__name__) @@ -177,11 +180,13 @@ def get_browsable_activities(node, ns): logger.exception('Getting Browsable Activities') -def manifest_analysis(mfxml, ns, man_data_dic, src_type, app_dir): +def manifest_analysis(checksum, mfxml, ns, man_data_dic, src_type, app_dir): """Analyse manifest file.""" # pylint: disable=C0301 try: - logger.info('Manifest Analysis Started') + msg = 'Manifest Analysis Started' + logger.info(msg) + append_scan_status(checksum, msg) exp_count = dict.fromkeys(['act', 'ser', 'bro', 'cnt'], 0) applications = mfxml.getElementsByTagName('application') data_tag = mfxml.getElementsByTagName('data') @@ -801,11 +806,14 @@ def manifest_analysis(mfxml, ns, man_data_dic, src_type, app_dir): 'browsable_activities': browsable_activities, 'permissions': permissions, 'network_security': network_security.analysis( + checksum, app_dir, do_netsec, debuggable, src_type), } return man_an_dic - except Exception: - logger.exception('Performing Manifest Analysis') + except Exception as exp: + msg = 'Error Performing Manifest Analysis' + logger.exception(msg) + append_scan_status(checksum, msg, repr(exp)) diff --git a/mobsf/StaticAnalyzer/views/android/manifest_utils.py b/mobsf/StaticAnalyzer/views/android/manifest_utils.py index 475d22a036..cee30a9130 100644 --- a/mobsf/StaticAnalyzer/views/android/manifest_utils.py +++ b/mobsf/StaticAnalyzer/views/android/manifest_utils.py @@ -14,12 +14,13 @@ from django.conf import settings from mobsf.MobSF.utils import ( + append_scan_status, find_java_binary, is_file_exists, ) # pylint: disable=E0401 -from .dvm_permissions import DVM_PERMISSIONS +from .kb.dvm_permissions import DVM_PERMISSIONS logger = logging.getLogger(__name__) @@ -119,7 +120,7 @@ def bs4_xml_parser(xml_str): return None -def get_manifest(app_path, app_dir, tools_dir, typ): +def get_manifest(checksum, app_path, app_dir, tools_dir, typ): """Get the manifest file.""" try: ns = 'android' @@ -133,7 +134,9 @@ def get_manifest(app_path, app_dir, tools_dir, typ): logger.warning('apktool failed to extract ' 'AndroidManifest.xml') return manifest_file, ns, get_fallback() - logger.info('Parsing AndroidManifest.xml') + msg = 'Parsing AndroidManifest.xml' + logger.info(msg) + append_scan_status(checksum, msg) xml_str = mfile.read_text('utf-8', 'ignore') ns = get_xml_namespace(xml_str) if ns and ns == 'xmlns': @@ -146,15 +149,19 @@ def get_manifest(app_path, app_dir, tools_dir, typ): logger.warning('Parsing AndroidManifest.xml failed') return manifest_file, ns, minidom.parseString( bs4_xml_parser(xml_str)) - except Exception: - logger.exception('Parsing Error') + except Exception as exp: + msg = 'Parsing AndroidManifest.xml failed' + logger.exception(msg) + append_scan_status(checksum, msg, repr(exp)) return manifest_file, ns, get_fallback() -def manifest_data(mfxml, ns): +def manifest_data(checksum, mfxml, ns): """Extract manifest data.""" try: - logger.info('Extracting Manifest Data') + msg = 'Extracting Manifest Data' + logger.info(msg) + append_scan_status(checksum, msg) svc = [] act = [] brd = [] @@ -290,5 +297,7 @@ def manifest_data(mfxml, ns): } return man_data_dic - except Exception: - logger.exception('Extracting Manifest Data') + except Exception as exp: + msg = 'Extracting Manifest Data' + logger.exception(msg) + append_scan_status(checksum, msg, repr(exp)) diff --git a/mobsf/StaticAnalyzer/views/android/network_security.py b/mobsf/StaticAnalyzer/views/android/network_security.py index 8befc1fc5c..4fe0f69746 100644 --- a/mobsf/StaticAnalyzer/views/android/network_security.py +++ b/mobsf/StaticAnalyzer/views/android/network_security.py @@ -5,6 +5,7 @@ from pathlib import Path from mobsf.MobSF.utils import ( + append_scan_status, is_path_traversal, ) @@ -15,7 +16,7 @@ SECURE = 'secure' -def read_netsec_config(app_dir, config, src_type): +def read_netsec_config(checksum, app_dir, config, src_type): """Read the manifest file.""" msg = 'Reading Network Security config' try: @@ -31,7 +32,9 @@ def read_netsec_config(app_dir, config, src_type): if not is_path_traversal(config): netsec_file = xml_dir / f'{config}.xml' if netsec_file.exists(): - logger.info('%s from %s.xml', msg, config) + desc = f'{msg} from {config}.xml' + logger.info(desc) + append_scan_status(checksum, desc) return netsec_file.read_text('utf8', 'ignore') # Couldn't find the file defined in manifest xmls = Path(xml_dir).glob('*.xml') @@ -41,14 +44,17 @@ def read_netsec_config(app_dir, config, src_type): break if not config_file: return None - logger.info('%s from %s', msg, config_file.name) + desc = f'{msg} from {config_file.name}' + logger.info(desc) + append_scan_status(checksum, desc) return config_file.read_text('utf8', 'ignore') - except Exception: + except Exception as exp: logger.exception(msg) + append_scan_status(checksum, msg, repr(exp)) return None -def analysis(app_dir, config, is_debuggable, src_type): +def analysis(checksum, app_dir, config, is_debuggable, src_type): """Perform Network Security Analysis.""" try: netsec = { @@ -57,10 +63,16 @@ def analysis(app_dir, config, is_debuggable, src_type): } if not config: return netsec - netsec_conf = read_netsec_config(app_dir, config, src_type) + netsec_conf = read_netsec_config( + checksum, + app_dir, + config, + src_type) if not netsec_conf: return netsec - logger.info('Parsing Network Security config') + msg = 'Parsing Network Security config' + logger.info(msg) + append_scan_status(checksum, msg) parsed = minidom.parseString(netsec_conf) finds = [] summary = {HIGH: 0, WARNING: 0, INFO: 0, SECURE: 0} @@ -275,6 +287,8 @@ def analysis(app_dir, config, is_debuggable, src_type): summary[HIGH] += 1 netsec['network_findings'] = finds netsec['network_summary'] = summary - except Exception: - logger.exception('Performing Network Security Analysis') + except Exception as exp: + msg = 'Performing Network Security Analysis' + logger.exception(msg) + append_scan_status(checksum, msg, repr(exp)) return netsec diff --git a/mobsf/StaticAnalyzer/views/android/playstore.py b/mobsf/StaticAnalyzer/views/android/playstore.py index b07d997048..936e3f7d38 100644 --- a/mobsf/StaticAnalyzer/views/android/playstore.py +++ b/mobsf/StaticAnalyzer/views/android/playstore.py @@ -9,15 +9,20 @@ from django.conf import settings -from mobsf.MobSF.utils import upstream_proxy +from mobsf.MobSF.utils import ( + append_scan_status, + upstream_proxy, +) logger = logging.getLogger(__name__) -def get_app_details(package_id): +def get_app_details(checksum, package_id): """Get App Details form PlayStore.""" try: - logger.info('Fetching Details from Play Store: %s', package_id) + msg = f'Fetching Details from Play Store: {package_id}' + logger.info(msg) + append_scan_status(checksum, msg) det = app(package_id) det.pop('descriptionHTML', None) det.pop('comments', None) @@ -27,16 +32,18 @@ def get_app_details(package_id): if 'androidVersionText' not in det: det['androidVersionText'] = '' except Exception: - det = app_search(package_id) + det = app_search(checksum, package_id) return det -def app_search(app_id): +def app_search(checksum, app_id): """Get App Details from AppMonsta.""" det = {'error': True} if not settings.APPMONSTA_API: return det - logger.info('Fetching Details from AppMonsta: %s', app_id) + msg = f'Fetching Details from AppMonsta: {app_id}' + append_scan_status(checksum, msg) + logger.info(msg) lookup_url = settings.APPMONSTA_URL req_url = '{}{}.json?country={}'.format( lookup_url, app_id, 'US') @@ -73,6 +80,8 @@ def app_search(app_id): det['description'] = description.get_text() det['error'] = False return det - except Exception: - logger.warning('Unable to get app details') + except Exception as exp: + msg = 'Unable to get app details' + append_scan_status(checksum, msg, repr(exp)) + logger.warning(msg) return det diff --git a/mobsf/StaticAnalyzer/views/android/rules/android_rules.yaml b/mobsf/StaticAnalyzer/views/android/rules/android_rules.yaml index 8fa738f1d1..9b8f5957c5 100644 --- a/mobsf/StaticAnalyzer/views/android/rules/android_rules.yaml +++ b/mobsf/StaticAnalyzer/views/android/rules/android_rules.yaml @@ -596,7 +596,7 @@ type: RegexOr pattern: - MODE_WORLD_WRITABLE - - \.getSharedPreferences\(.{0,50}2\) + - \.getSharedPreferences\(.{0,50}?2\) - 'openFileOutput\(\s*".{1,48}"\s*,\s*2\s*\)' severity: high input_case: exact @@ -611,7 +611,7 @@ type: RegexOr pattern: - MODE_WORLD_READABLE - - \.getSharedPreferences\(.{0,50}1\) + - \.getSharedPreferences\(.{0,50}?1\) - 'openFileOutput\(\s*".{1,48}"\s*,\s*1\s*\)' severity: high input_case: exact diff --git a/mobsf/StaticAnalyzer/views/android/so.py b/mobsf/StaticAnalyzer/views/android/so.py index 50e3a07dd2..a5c1219a5b 100644 --- a/mobsf/StaticAnalyzer/views/android/so.py +++ b/mobsf/StaticAnalyzer/views/android/so.py @@ -7,6 +7,7 @@ import mobsf.MalwareAnalyzer.views.Trackers as Trackers import mobsf.MalwareAnalyzer.views.VirusTotal as VirusTotal from mobsf.MobSF.utils import ( + append_scan_status, file_size, print_n_send_error_response, ) @@ -38,10 +39,11 @@ def so_analysis(request, app_dic, rescan, api): + checksum = app_dic['md5'] app_dic['app_file'] = f'{app_dic["md5"]}.so' # NEW FILENAME app_dic['app_path'] = (app_dic['app_dir'] / app_dic['app_file']).as_posix() app_dic['app_dir'] = app_dic['app_dir'].as_posix() + '/' - db_entry = StaticAnalyzerAndroid.objects.filter(MD5=app_dic['md5']) + db_entry = StaticAnalyzerAndroid.objects.filter(MD5=checksum) if db_entry.exists() and not rescan: context = get_context_from_db_entry(db_entry) else: @@ -50,8 +52,12 @@ def so_analysis(request, app_dic, rescan, api): request, 'Permission Denied', False) + append_scan_status(checksum, 'init') + # Analysis starts here app_dic['size'] = f'{str(file_size(app_dic["app_path"]))}MB' - app_dic['sha1'], app_dic['sha256'] = hash_gen(app_dic['app_path']) + app_dic['sha1'], app_dic['sha256'] = hash_gen( + checksum, + app_dic['app_path']) app_dic['files'] = [] app_dic['certz'] = [] app_dic['playstore'] = {'error': True} @@ -99,8 +105,8 @@ def so_analysis(request, app_dic, rescan, api): } app_dic['real_name'] = '' elf_dict = library_analysis( + checksum, app_dic['app_dir'], - app_dic['md5'], 'elf') # File Analysis is used to store symbols from so app_dic['certz'] = get_symbols( @@ -116,32 +122,30 @@ def so_analysis(request, app_dic, rescan, api): 'emails': [], } quark_results = [] - # Get the strings and metadata from shared object get_strings_metadata( + checksum, None, None, elf_dict['elf_strings'], None, None, code_an_dic) - # Firebase DB Check code_an_dic['firebase'] = firebase_analysis( + checksum, code_an_dic['urls_list']) - # Domain Extraction and Malware Check - logger.info( - 'Performing Malware Check on extracted Domains') code_an_dic['domains'] = MalwareDomainCheck().scan( + checksum, code_an_dic['urls_list']) - # Extract Trackers from Domains trk = Trackers.Trackers( - None, app_dic['tools_dir']) + checksum, + None, + app_dic['tools_dir']) trackers = trk.get_trackers_domains_or_deps( code_an_dic['domains'], []) - app_dic['zipped'] = 'so' context = save_get_ctx( app_dic, @@ -160,10 +164,9 @@ def so_analysis(request, app_dic, rescan, api): context['dynamic_analysis_done'] = False context['virus_total'] = None if settings.VT_ENABLED: - vt = VirusTotal.VirusTotal() + vt = VirusTotal.VirusTotal(checksum) context['virus_total'] = vt.get_result( - app_dic['app_path'], - app_dic['md5']) + app_dic['app_path']) template = 'static_analysis/android_binary_analysis.html' if api: return context diff --git a/mobsf/StaticAnalyzer/views/android/static_analyzer.py b/mobsf/StaticAnalyzer/views/android/static_analyzer.py index d7a15860b5..eaa9920805 100755 --- a/mobsf/StaticAnalyzer/views/android/static_analyzer.py +++ b/mobsf/StaticAnalyzer/views/android/static_analyzer.py @@ -22,6 +22,7 @@ from mobsf.MobSF.utils import ( android_component, + append_scan_status, file_size, is_dir_exists, is_file_exists, @@ -139,17 +140,18 @@ def static_analyzer(request, checksum, api=False): request, 'Invalid file extension or file type', api) - app_dic['dir'] = Path(settings.BASE_DIR) # BASE DIR app_dic['app_name'] = filename # APP ORIGINAL NAME app_dic['md5'] = checksum # MD5 - logger.info('Scan Hash: %s', checksum) + msg = f'Scan Hash: {checksum}' + logger.info(msg) # APP DIRECTORY app_dic['app_dir'] = Path(settings.UPLD_DIR) / checksum app_dic['tools_dir'] = app_dic['dir'] / 'StaticAnalyzer' / 'tools' app_dic['tools_dir'] = app_dic['tools_dir'].as_posix() app_dic['icon_path'] = '' - logger.info('Starting Analysis on: %s', app_dic['app_name']) + msg = f'Starting Analysis on: {filename}' + logger.info(msg) if typ == 'xapk': # Handle XAPK # Base APK will have the MD5 of XAPK @@ -167,14 +169,13 @@ def static_analyzer(request, checksum, api=False): raise Exception('Invalid AAB File') typ = 'apk' if typ == 'apk': - app_dic['app_file'] = app_dic['md5'] + '.apk' # NEW FILENAME + app_dic['app_file'] = f'{checksum}.apk' app_dic['app_path'] = ( app_dic['app_dir'] / app_dic['app_file']).as_posix() app_dic['app_dir'] = app_dic['app_dir'].as_posix() + '/' # Check if in DB # pylint: disable=E1101 - db_entry = StaticAnalyzerAndroid.objects.filter( - MD5=app_dic['md5']) + db_entry = StaticAnalyzerAndroid.objects.filter(MD5=checksum) if db_entry.exists() and not rescan: context = get_context_from_db_entry(db_entry) else: @@ -184,23 +185,34 @@ def static_analyzer(request, checksum, api=False): 'Permission Denied', False) # ANALYSIS BEGINS + append_scan_status(checksum, 'init') app_dic['size'] = str( file_size(app_dic['app_path'])) + 'MB' # FILE SIZE - app_dic['sha1'], app_dic[ - 'sha256'] = hash_gen(app_dic['app_path']) + app_dic['sha1'], app_dic['sha256'] = hash_gen( + checksum, + app_dic['app_path']) + msg = 'Extracting APK' + logger.info(msg) + append_scan_status(checksum, msg) app_dic['files'] = unzip( - app_dic['app_path'], app_dic['app_dir']) + checksum, + app_dic['app_path'], + app_dic['app_dir']) logger.info('APK Extracted') if not app_dic['files']: # Can't Analyze APK, bail out. + msg = 'APK file is invalid or corrupt' + append_scan_status(checksum, msg) return print_n_send_error_response( request, - 'APK file is invalid or corrupt', + msg, api) - app_dic['certz'] = get_hardcoded_cert_keystore(app_dic[ - 'files']) + app_dic['certz'] = get_hardcoded_cert_keystore( + checksum, + app_dic['files']) # Manifest XML mani_file, ns, mani_xml = get_manifest( + checksum, app_dic['app_path'], app_dic['app_dir'], app_dic['tools_dir'], @@ -209,16 +221,21 @@ def static_analyzer(request, checksum, api=False): app_dic['manifest_file'] = mani_file app_dic['parsed_xml'] = mani_xml # Parse APK with Androguard - apk = parse_apk(app_dic['app_path']) + apk = parse_apk( + checksum, + app_dic['app_path']) # get app_name app_dic['real_name'] = get_app_name( apk, app_dic['app_dir'], True, ) - # Set Manifest link - man_data_dic = manifest_data(app_dic['parsed_xml'], ns) - + # Manifest data extraction + man_data_dic = manifest_data( + checksum, + app_dic['parsed_xml'], + ns) + # Get App name app_name = app_dic['real_name'] pkg_name = man_data_dic['packagename'] if app_name or pkg_name: @@ -230,70 +247,76 @@ def static_analyzer(request, checksum, api=False): subject = pkg_name msg = f'Performing Static Analysis on: {subject}' logger.info(msg) - + append_scan_status(checksum, msg) app_dic['playstore'] = get_app_details( + checksum, man_data_dic['packagename']) man_an_dic = manifest_analysis( + checksum, app_dic['parsed_xml'], ns, man_data_dic, '', - app_dic['app_dir'], - ) + app_dic['app_dir']) # Malware Permission check mal_perms = permissions.check_malware_permission( + checksum, man_data_dic['perm']) man_an_dic['malware_permissions'] = mal_perms - # Get icon # apktool should run before this get_icon_apk(apk, app_dic) - elf_dict = library_analysis( + checksum, app_dic['app_dir'], - app_dic['md5'], 'elf') cert_dic = cert_info( apk, app_dic, man_data_dic) - apkid_results = apkid.apkid_analysis(app_dic[ - 'app_dir'], app_dic['app_path'], app_dic['app_name']) + apkid_results = apkid.apkid_analysis( + checksum, + app_dic['app_path']) tracker = Trackers.Trackers( - app_dic['app_dir'], app_dic['tools_dir']) + checksum, + app_dic['app_dir'], + app_dic['tools_dir']) tracker_res = tracker.get_trackers() - - apk_2_java(app_dic['app_path'], app_dic['app_dir'], - app_dic['tools_dir']) - - dex_2_smali(app_dic['app_dir'], app_dic['tools_dir']) - + apk_2_java( + checksum, + app_dic['app_path'], + app_dic['app_dir'], + app_dic['tools_dir']) + dex_2_smali( + checksum, + app_dic['app_dir'], + app_dic['tools_dir']) code_an_dic = code_analysis( + checksum, app_dic['app_dir'], 'apk', app_dic['manifest_file'], man_data_dic['perm']) - quark_results = quark.quark_analysis( + checksum, app_dic['app_dir'], app_dic['app_path']) - # Get the strings and metadata get_strings_metadata( + checksum, apk, app_dic['app_dir'], elf_dict['elf_strings'], 'apk', ['.java'], code_an_dic) - # Firebase DB Check code_an_dic['firebase'] = firebase_analysis( + checksum, code_an_dic['urls_list']) # Domain Extraction and Malware Check - logger.info( - 'Performing Malware Check on extracted Domains') code_an_dic['domains'] = MalwareDomainCheck().scan( + checksum, code_an_dic['urls_list']) app_dic['zipped'] = 'apk' @@ -317,10 +340,9 @@ def static_analyzer(request, checksum, api=False): context['virus_total'] = None if settings.VT_ENABLED: - vt = VirusTotal.VirusTotal() + vt = VirusTotal.VirusTotal(checksum) context['virus_total'] = vt.get_result( - app_dic['app_path'], - app_dic['md5']) + app_dic['app_path']) template = 'static_analysis/android_binary_analysis.html' if api: return context @@ -334,25 +356,14 @@ def static_analyzer(request, checksum, api=False): return so_analysis(request, app_dic, rescan, api) elif typ == 'zip': ret = f'/static_analyzer_ios/{checksum}/' - # Check if in DB - # pylint: disable=E1101 - cert_dic = { - 'certificate_info': '', - 'certificate_status': '', - 'description': '', - } - app_dic['strings'] = [] - app_dic['secrets'] = [] - app_dic['zipped'] = '' - # Above fields are only available for APK and not ZIP - app_dic['app_file'] = app_dic['md5'] + '.zip' # NEW FILENAME + app_dic['app_file'] = f'{checksum}.zip' app_dic['app_path'] = ( app_dic['app_dir'] / app_dic['app_file']).as_posix() app_dic['app_dir'] = app_dic['app_dir'].as_posix() + '/' db_entry = StaticAnalyzerAndroid.objects.filter( - MD5=app_dic['md5']) + MD5=checksum) ios_db_entry = StaticAnalyzerIOS.objects.filter( - MD5=app_dic['md5']) + MD5=checksum) if db_entry.exists() and not rescan: context = get_context_from_db_entry(db_entry) elif ios_db_entry.exists() and not rescan: @@ -361,36 +372,57 @@ def static_analyzer(request, checksum, api=False): else: return HttpResponseRedirect(ret) else: - logger.info('Extracting ZIP') + append_scan_status(checksum, 'init') + msg = 'Extracting ZIP' + logger.info(msg) + append_scan_status(checksum, msg) app_dic['files'] = unzip( - app_dic['app_path'], app_dic['app_dir']) + checksum, + app_dic['app_path'], + app_dic['app_dir']) # Check if Valid Directory Structure and get ZIP Type - pro_type, valid = valid_source_code(app_dic['app_dir']) - logger.info('Source code type - %s', pro_type) + pro_type, valid = valid_source_code( + checksum, + app_dic['app_dir']) + msg = f'Source code type - {pro_type}' + logger.info(msg) + append_scan_status(checksum, msg) if valid and pro_type == 'ios': - logger.info('Redirecting to iOS Source Code Analyzer') + msg = 'Redirecting to iOS Source Code Analyzer' + logger.info(msg) + append_scan_status(checksum, msg) if api: return {'type': 'ios'} else: ret += f'?rescan={str(int(rescan))}' return HttpResponseRedirect(ret) - app_dic['certz'] = get_hardcoded_cert_keystore( - app_dic['files']) - app_dic['zipped'] = pro_type + if not has_permission(request, Permissions.SCAN, api): + return print_n_send_error_response( + request, + 'Permission Denied', + False) + # Android ZIP Source Code Analysis Begins if valid and (pro_type in ['eclipse', 'studio']): - if not has_permission(request, Permissions.SCAN, api): - return print_n_send_error_response( - request, - 'Permission Denied', - False) - # ANALYSIS BEGINS + cert_dic = { + 'certificate_info': '', + 'certificate_status': '', + 'description': '', + } + app_dic['strings'] = [] + app_dic['secrets'] = [] + # Above fields are only available for APK and not ZIP + app_dic['zipped'] = pro_type app_dic['size'] = str( file_size(app_dic['app_path'])) + 'MB' # FILE SIZE - app_dic['sha1'], app_dic[ - 'sha256'] = hash_gen(app_dic['app_path']) - + app_dic['sha1'], app_dic['sha256'] = hash_gen( + checksum, + app_dic['app_path']) + app_dic['certz'] = get_hardcoded_cert_keystore( + checksum, + app_dic['files']) # Manifest XML mani_file, ns, mani_xml = get_manifest( + checksum, '', app_dic['app_dir'], app_dic['tools_dir'], @@ -398,17 +430,18 @@ def static_analyzer(request, checksum, api=False): ) app_dic['manifest_file'] = mani_file app_dic['parsed_xml'] = mani_xml - # get app_name app_dic['real_name'] = get_app_name( app_dic['app_path'], app_dic['app_dir'], False, ) - - # Set manifest view link - man_data_dic = manifest_data(app_dic['parsed_xml'], ns) - + # Get manifest data + man_data_dic = manifest_data( + checksum, + app_dic['parsed_xml'], + ns) + # Get app name app_name = app_dic['real_name'] pkg_name = man_data_dic['packagename'] if app_name or pkg_name: @@ -422,50 +455,53 @@ def static_analyzer(request, checksum, api=False): logger.info(msg) app_dic['playstore'] = get_app_details( + checksum, man_data_dic['packagename']) man_an_dic = manifest_analysis( + checksum, app_dic['parsed_xml'], ns, man_data_dic, pro_type, app_dic['app_dir'], ) - # Malware Permission check mal_perms = permissions.check_malware_permission( + checksum, man_data_dic['perm']) man_an_dic['malware_permissions'] = mal_perms - # Get icon - get_icon_from_src(app_dic, man_data_dic['icons']) - + get_icon_from_src( + app_dic, + man_data_dic['icons']) code_an_dic = code_analysis( + checksum, app_dic['app_dir'], pro_type, app_dic['manifest_file'], man_data_dic['perm']) - # Get the strings and metadata get_strings_metadata( + checksum, None, app_dic['app_dir'], None, pro_type, ['.java', '.kt'], code_an_dic) - # Firebase DB Check code_an_dic['firebase'] = firebase_analysis( + checksum, code_an_dic['urls_list']) # Domain Extraction and Malware Check - logger.info( - 'Performing Malware Check on extracted Domains') code_an_dic['domains'] = MalwareDomainCheck().scan( + checksum, code_an_dic['urls_list']) - # Extract Trackers from Domains trk = Trackers.Trackers( - None, app_dic['tools_dir']) + checksum, + None, + app_dic['tools_dir']) trackers = trk.get_trackers_domains_or_deps( code_an_dic['domains'], []) context = save_get_ctx( @@ -507,12 +543,14 @@ def static_analyzer(request, checksum, api=False): err = ('Only APK, JAR, AAR, SO and Zipped ' 'Android/iOS Source code supported now!') logger.error(err) + append_scan_status(checksum, err) raise Exception(err) - except Exception as excep: - logger.exception('Error Performing Static Analysis') - msg = str(excep) - exp = excep.__doc__ - return print_n_send_error_response(request, msg, api, exp) + except Exception as exp: + errmsg = 'Error Performing Static Analysis' + logger.exception(errmsg) + exp = exp.__doc__ + append_scan_status(checksum, errmsg, repr(exp)) + return print_n_send_error_response(request, repr(exp), api, exp) def is_android_source(app_dir): @@ -533,10 +571,12 @@ def is_android_source(app_dir): return None, False -def valid_source_code(app_dir): +def valid_source_code(checksum, app_dir): """Test if this is an valid source code zip.""" try: - logger.info('Detecting source code type') + msg = 'Detecting source code type' + logger.info(msg) + append_scan_status(checksum, msg) ide, is_and = is_android_source(app_dir) if ide: return ide, is_and @@ -561,8 +601,10 @@ def valid_source_code(app_dir): if [f for f in os.listdir(obj) if f.endswith('.xcodeproj')]: return 'ios', True return '', False - except Exception: - logger.exception('Identifying source code from zip') + except Exception as exp: + msg = 'Error identifying source code type from zip' + logger.exception(msg) + append_scan_status(checksum, msg, repr(exp)) def move_to_parent(inside, app_dir): diff --git a/mobsf/StaticAnalyzer/views/android/strings.py b/mobsf/StaticAnalyzer/views/android/strings.py index c34e5df5b3..80a2e7abc9 100755 --- a/mobsf/StaticAnalyzer/views/android/strings.py +++ b/mobsf/StaticAnalyzer/views/android/strings.py @@ -12,14 +12,18 @@ get_entropies, ) from mobsf.MobSF.utils import ( + append_scan_status, get_android_src_dir, ) logger = logging.getLogger(__name__) -def strings_from_so(elf_strings): +def strings_from_so(checksum, elf_strings): """Extract Strings from so file.""" + msg = 'Extracting String data from SO' + logger.info(msg) + append_scan_status(checksum, msg) sos = [] try: for solib in elf_strings: @@ -37,12 +41,14 @@ def strings_from_so(elf_strings): 'urls_nf': so_urls_nf, 'emails_nf': so_emails_nf, }}) - except Exception: - logger.exception('Extracting Data from SO') + except Exception as exp: + msg = 'Failed to extract String data from SO' + logger.exception(msg) + append_scan_status(checksum, msg, repr(exp)) return sos -def strings_from_apk(apk): +def strings_from_apk(checksum, apk): """Extract Strings from an APK.""" dat = [] secrets = [] @@ -50,7 +56,9 @@ def strings_from_apk(apk): urls_nf = [] emails_nf = [] try: - logger.info('Extracting Data from APK') + msg = 'Extracting String data from APK' + logger.info(msg) + append_scan_status(checksum, msg) rsrc = apk.get_android_resources() if rsrc: pkg = rsrc.get_packages_names()[0] @@ -68,8 +76,10 @@ def strings_from_apk(apk): # Extract URLs and Emails from Android String Resources urls, urls_nf, emails_nf = url_n_email_extract( ''.join(dat), 'Android String Resource') - except Exception: - logger.exception('Extracting Data from APK') + except Exception as exp: + msg = 'Failed to extract String data from APK' + logger.exception(msg) + append_scan_status(checksum, msg, repr(exp)) return { 'strings': list(set(dat)), 'urls_list': urls, @@ -79,21 +89,27 @@ def strings_from_apk(apk): } -def strings_from_code(src_dir, typ, exts): +def strings_from_code(checksum, src_dir, typ, exts): """Extract Strings and Secrets from Java/Kotlin code.""" + msg = 'Extracting String data from Code' + logger.info(msg) + append_scan_status(checksum, msg) data = { 'strings': set(), 'secrets': set(), } try: src_dir = get_android_src_dir(Path(src_dir), typ) - data = strings_and_entropies(src_dir, exts) - except Exception: - logger.exception('Extracting Data from Code') + data = strings_and_entropies(checksum, src_dir, exts) + except Exception as exp: + msg = 'Failed to extract String data from Code' + logger.exception(msg) + append_scan_status(checksum, msg, repr(exp)) return data -def get_strings_metadata(apk, app_dir, elf_strings, typ, exts, code_dic): +def get_strings_metadata( + checksum, apk, app_dir, elf_strings, typ, exts, code_dic): """Get Strings, secrets, entropies, URLs, emails.""" strings = { 'strings_apk_res': {}, @@ -106,7 +122,7 @@ def get_strings_metadata(apk, app_dir, elf_strings, typ, exts, code_dic): secrets = [] if apk: # APK - apk_res = strings_from_apk(apk) + apk_res = strings_from_apk(checksum, apk) strings['strings_apk_res'] = apk_res['strings'] urls_list.extend(apk_res['urls_list']) urls_n_files.extend(apk_res['urls_nf']) @@ -114,7 +130,7 @@ def get_strings_metadata(apk, app_dir, elf_strings, typ, exts, code_dic): secrets.extend(apk_res['secrets']) if elf_strings: # ELF (.so) by file - sos = strings_from_so(elf_strings) + sos = strings_from_so(checksum, elf_strings) so_strings = [] for so in sos: for so_file, s in so.items(): @@ -128,7 +144,7 @@ def get_strings_metadata(apk, app_dir, elf_strings, typ, exts, code_dic): if exts: # Source Code - code_res = strings_from_code(app_dir, typ, exts) + code_res = strings_from_code(checksum, app_dir, typ, exts) strings['strings_code'] = list(code_res['strings']) secrets.extend(code_res['secrets']) diff --git a/mobsf/StaticAnalyzer/views/android/find.py b/mobsf/StaticAnalyzer/views/android/views/find.py similarity index 100% rename from mobsf/StaticAnalyzer/views/android/find.py rename to mobsf/StaticAnalyzer/views/android/views/find.py diff --git a/mobsf/StaticAnalyzer/views/android/manifest_view.py b/mobsf/StaticAnalyzer/views/android/views/manifest_view.py similarity index 100% rename from mobsf/StaticAnalyzer/views/android/manifest_view.py rename to mobsf/StaticAnalyzer/views/android/views/manifest_view.py diff --git a/mobsf/StaticAnalyzer/views/android/source_tree.py b/mobsf/StaticAnalyzer/views/android/views/source_tree.py similarity index 100% rename from mobsf/StaticAnalyzer/views/android/source_tree.py rename to mobsf/StaticAnalyzer/views/android/views/source_tree.py diff --git a/mobsf/StaticAnalyzer/views/android/view_source.py b/mobsf/StaticAnalyzer/views/android/views/view_source.py similarity index 100% rename from mobsf/StaticAnalyzer/views/android/view_source.py rename to mobsf/StaticAnalyzer/views/android/views/view_source.py diff --git a/mobsf/StaticAnalyzer/views/android/xapk.py b/mobsf/StaticAnalyzer/views/android/xapk.py index d5b02422d2..ad70bcd417 100644 --- a/mobsf/StaticAnalyzer/views/android/xapk.py +++ b/mobsf/StaticAnalyzer/views/android/xapk.py @@ -10,6 +10,7 @@ from mobsf.StaticAnalyzer.views.common.shared_func import unzip from mobsf.MobSF.utils import ( + append_scan_status, find_java_binary, is_file_exists, is_safe_path, @@ -24,7 +25,10 @@ def handle_xapk(app_dic): checksum = app_dic['md5'] xapk = app_dic['app_dir'] / f'{checksum}.xapk' apk = app_dic['app_dir'] / f'{checksum}.apk' - files = unzip(xapk.as_posix(), app_dic['app_dir']) + files = unzip( + checksum, + xapk.as_posix(), + app_dic['app_dir']) if 'manifest.json' not in files: logger.error('Manifest file not found in XAPK') return False @@ -55,7 +59,7 @@ def handle_split_apk(app_dic): manifest = app_dic['app_dir'] / 'AndroidManifest.xml' if manifest.exists(): return True - for apk in unzip(apks.as_posix(), app_dic['app_dir']): + for apk in unzip(checksum, apks.as_posix(), app_dic['app_dir']): full_path = app_dic['app_dir'] / apk safe_path = is_safe_path(app_dic['app_dir'], full_path) if (not apk.startswith('config.') @@ -78,7 +82,9 @@ def handle_aab(app_dic): manifest = app_dic['app_dir'] / 'AndroidManifest.xml' if manifest.exists(): return True - logger.info('Converting AAB to APK') + msg = 'Converting AAB to APK' + logger.info(msg) + append_scan_status(checksum, msg) if (getattr(settings, 'BUNDLE_TOOL', '') and len(settings.BUNDLE_TOOL) > 0 and is_file_exists(settings.BUNDLE_TOOL)): @@ -101,7 +107,7 @@ def handle_aab(app_dic): # Remove AAB aab_path.unlink() # Extract APK from APKS - for apk_file in unzip(apks.as_posix(), app_dic['app_dir']): + for apk_file in unzip(checksum, apks.as_posix(), app_dic['app_dir']): full_path = app_dic['app_dir'] / apk_file safe_path = is_safe_path(app_dic['app_dir'], full_path) if apk_file == 'universal.apk' and safe_path: @@ -109,8 +115,12 @@ def handle_aab(app_dic): apks.unlink() return True raise Exception('Unable to convert AAB to APK') - except subprocess.TimeoutExpired: - logger.warning('Converting AAB to APK timed out') - except Exception: - logger.exception('Converting AAB to APK') + except subprocess.TimeoutExpired as exp: + msg = 'Converting AAB to APK timed out' + logger.warning(msg) + append_scan_status(checksum, msg, repr(exp)) + except Exception as exp: + msg = 'Failed to convert AAB to APK' + logger.exception(msg) + append_scan_status(checksum, msg, repr(exp)) return None diff --git a/mobsf/StaticAnalyzer/views/common/a.py b/mobsf/StaticAnalyzer/views/common/a.py index 91ae09eb16..0db568806c 100644 --- a/mobsf/StaticAnalyzer/views/common/a.py +++ b/mobsf/StaticAnalyzer/views/common/a.py @@ -11,6 +11,7 @@ from django.shortcuts import render from mobsf.MobSF.utils import ( + append_scan_status, file_size, print_n_send_error_response, ) @@ -45,12 +46,12 @@ logger = logging.getLogger(__name__) -def extract_n_get_files(src, dst): +def extract_n_get_files(checksum, src, dst): """Extract .a archive and get list of files.""" files = [] dst = Path(dst) / 'static_objects' dst.mkdir(parents=True, exist_ok=True) - ar_extract(src, dst.as_posix()) + ar_extract(checksum, src, dst.as_posix()) for i in dst.rglob('*.a'): files.append( os.path.relpath(dst, i.as_posix())) @@ -59,14 +60,13 @@ def extract_n_get_files(src, dst): def a_analysis(request, app_dict, rescan, api): """Independent shared library .a analysis.""" + checksum = app_dict['md5_hash'] app_dir = Path(app_dict['app_dir']) - app_dict['app_file'] = app_dict[ - 'md5_hash'] + '.a' # NEW FILENAME + app_dict['app_file'] = f'{checksum}.a' app_dict['app_path'] = app_dir / app_dict['app_file'] app_dict['app_path'] = app_dict['app_path'].as_posix() # DB - ipa_db = StaticAnalyzerIOS.objects.filter( - MD5=app_dict['md5_hash']) + ipa_db = StaticAnalyzerIOS.objects.filter(MD5=checksum) if ipa_db.exists() and not rescan: context = get_context_from_db_entry(ipa_db) else: @@ -75,13 +75,16 @@ def a_analysis(request, app_dict, rescan, api): request, 'Permission Denied', False) + append_scan_status(checksum, 'init') logger.info('Static Library Analysis Started') app_dict['size'] = str( file_size(app_dict['app_path'])) + 'MB' # FILE SIZE app_dict['sha1'], app_dict['sha256'] = hash_gen( + checksum, app_dict['app_path']) # SHA1 & SHA256 HASHES app_dict['bin_dir'] = app_dict['app_dir'] files = extract_n_get_files( + checksum, app_dict['app_path'], app_dict['app_dir'], ) @@ -127,8 +130,8 @@ def a_analysis(request, app_dict, rescan, api): } # Analyze static library slib = library_analysis( + checksum, app_dict['bin_dir'], - app_dict['md5_hash'], 'ar') bin_dict['bin_info']['arch'] = slib['ar_a'] bin_dict['dylib_analysis'] = slib['ar_analysis'] @@ -137,6 +140,7 @@ def a_analysis(request, app_dict, rescan, api): slib['ar_symbols']) # Binary code analysis on symbols binary_rule_matcher( + checksum, bin_dict['bin_code_analysis'], all_files['special_files'], b'') @@ -146,23 +150,22 @@ def a_analysis(request, app_dict, rescan, api): bin_dict, all_files, slib['ar_strings']) - # Domain Extraction and Malware Check - logger.info('Performing Malware Check on ' - 'extracted Domains') code_dict['domains'] = MalwareDomainCheck().scan( + checksum, code_dict['urls_list']) logger.info('Finished URL and Email Extraction') - # Extract Trackers from Domains trk = Trackers.Trackers( - None, app_dict['tools_dir']) + checksum, + None, + app_dict['tools_dir']) trackers = trk.get_trackers_domains_or_deps( code_dict['domains'], []) - code_dict['api'] = {} code_dict['code_anal'] = {} code_dict['firebase'] = firebase_analysis( + checksum, code_dict['urls_list']) code_dict['trackers'] = trackers context = save_get_ctx( @@ -174,10 +177,9 @@ def a_analysis(request, app_dict, rescan, api): rescan) context['virus_total'] = None if settings.VT_ENABLED: - vt = VirusTotal.VirusTotal() + vt = VirusTotal.VirusTotal(checksum) context['virus_total'] = vt.get_result( - app_dict['app_path'], - app_dict['md5_hash']) + app_dict['app_path']) context['appsec'] = {} context['average_cvss'] = None template = 'static_analysis/ios_binary_analysis.html' diff --git a/mobsf/StaticAnalyzer/views/common/binary/lib_analysis.py b/mobsf/StaticAnalyzer/views/common/binary/lib_analysis.py index 3a37800169..1b04dacd16 100644 --- a/mobsf/StaticAnalyzer/views/common/binary/lib_analysis.py +++ b/mobsf/StaticAnalyzer/views/common/binary/lib_analysis.py @@ -6,6 +6,7 @@ from django.conf import settings from mobsf.MobSF.utils import ( + append_scan_status, settings_enabled, ) from mobsf.StaticAnalyzer.views.common.binary.elf import ( @@ -19,7 +20,7 @@ logger = logging.getLogger(__name__) -def library_analysis(src, checksum, arch): +def library_analysis(checksum, src, arch): """Perform library binary analysis.""" base_dir = Path(settings.UPLD_DIR) / checksum res = { @@ -41,14 +42,18 @@ def library_analysis(src, checksum, arch): elif arch == 'ar': ext = '*.o' res[f'{arch}_a'] = '' - logger.info('Library Binary Analysis Started') + msg = 'Library Binary Analysis Started' + logger.info(msg) + append_scan_status(checksum, msg) # Supports Static Library, Shared objects, Dynamic Library, # from APK, SO, AAR, JAR, IPA, DYLIB, and A for libfile in Path(src).rglob(ext): if '__MACOSX' in libfile.as_posix(): continue rel_path = libfile.relative_to(base_dir).as_posix() - logger.info('Analyzing %s', rel_path) + msg = f'Analyzing {rel_path}' + logger.info(msg) + append_scan_status(checksum, msg) if arch == 'ar': # Handle static library if lief.is_macho(libfile.as_posix()): @@ -77,19 +82,23 @@ def library_analysis(src, checksum, arch): res['framework_analysis'] = [] res['framework_strings'] = [] res['framework_symbols'] = [] - frameworks_analysis(src, base_dir, res) + frameworks_analysis(checksum, src, base_dir, res) if res['framework_strings']: res[f'{arch}_strings'].extend( res['framework_strings']) - except Exception: - logger.exception('Performing Library Binary Analysis') + except Exception as exp: + msg = 'Error Performing Library Binary Analysis' + logger.exception(msg) + append_scan_status(checksum, msg, repr(exp)) return res -def frameworks_analysis(src, base_dir, res): +def frameworks_analysis(checksum, src, base_dir, res): """Binary Analysis on Frameworks.""" try: - logger.info('Framework Binary Analysis Started') + msg = 'Framework Binary Analysis Started' + logger.info(msg) + append_scan_status(checksum, msg) # Supports iOS Frameworks for ffile in Path(src).rglob('*'): parent = ffile.parents[0].name @@ -99,7 +108,9 @@ def frameworks_analysis(src, base_dir, res): if ffile.suffix != '' or ffile.name not in parent: continue # Frameworks/XXX.framework/XXX - logger.info('Analyzing %s', rel_path) + msg = f'Analyzing {rel_path}' + logger.info(msg) + append_scan_status(checksum, msg) chk = MachOChecksec(ffile, rel_path) chksec = chk.checksec() strings = chk.strings() @@ -112,5 +123,7 @@ def frameworks_analysis(src, base_dir, res): if symbols: res['framework_symbols'].append({ rel_path: symbols}) - except Exception: - logger.exception('Performing Framework Binary Analysis') + except Exception as exp: + msg = 'Error Performing Framework Binary Analysis' + logger.exception(msg) + append_scan_status(checksum, msg, repr(exp)) diff --git a/mobsf/StaticAnalyzer/views/common/pdf.py b/mobsf/StaticAnalyzer/views/common/pdf.py index 807bbe450b..4ed183de5f 100644 --- a/mobsf/StaticAnalyzer/views/common/pdf.py +++ b/mobsf/StaticAnalyzer/views/common/pdf.py @@ -93,8 +93,8 @@ def pdf(request, checksum, api=False, jsonres=False): settings.UPLD_DIR, checksum + '/', checksum + ext) - vt = VirusTotal.VirusTotal() - context['virus_total'] = vt.get_result(app_bin, checksum) + vt = VirusTotal.VirusTotal(checksum) + context['virus_total'] = vt.get_result(app_bin) # Get Local Base URL proto = 'file://' host_os = 'nix' diff --git a/mobsf/StaticAnalyzer/views/common/shared_func.py b/mobsf/StaticAnalyzer/views/common/shared_func.py index d62086dcb1..90bfa91fb1 100755 --- a/mobsf/StaticAnalyzer/views/common/shared_func.py +++ b/mobsf/StaticAnalyzer/views/common/shared_func.py @@ -28,6 +28,7 @@ EMAIL_REGEX, STRINGS_REGEX, URL_REGEX, + append_scan_status, is_md5, is_safe_path, print_n_send_error_response, @@ -56,10 +57,12 @@ logger = logging.getLogger(__name__) -def hash_gen(app_path) -> tuple: +def hash_gen(checksum, app_path) -> tuple: """Generate and return sha1 and sha256 as a tuple.""" try: - logger.info('Generating Hashes') + msg = 'Generating Hashes' + logger.info(msg) + append_scan_status(checksum, msg) sha1 = hashlib.sha1() sha256 = hashlib.sha256() block_size = 65536 @@ -72,12 +75,16 @@ def hash_gen(app_path) -> tuple: sha1val = sha1.hexdigest() sha256val = sha256.hexdigest() return sha1val, sha256val - except Exception: - logger.exception('Generating Hashes') + except Exception as exp: + msg = 'Failed to generate Hashes' + logger.exception(msg) + append_scan_status(checksum, msg, repr(exp)) -def unzip(app_path, ext_path): - logger.info('Unzipping') +def unzip(checksum, app_path, ext_path): + msg = 'Unzipping' + logger.info(msg) + append_scan_status(checksum, msg) try: files = [] with zipfile.ZipFile(app_path, 'r') as zipptr: @@ -89,12 +96,18 @@ def unzip(app_path, ext_path): files.append(filename) zipptr.extract(filename, ext_path) return files - except Exception: - logger.exception('Unzipping Error') + except Exception as exp: + msg = 'Unzipping Error' + logger.exception(msg) + append_scan_status(checksum, msg, repr(exp)) if platform.system() == 'Windows': - logger.info('Not yet Implemented.') + msg = 'Unzipping Error. Not yet implemented in Windows' + logger.warning(msg) + append_scan_status(checksum, msg) else: - logger.info('Using the Default OS Unzip Utility.') + msg = 'Unzipping Error. Trying with OS unzip utility' + logger.info(msg) + append_scan_status(checksum, msg) try: unzip_b = shutil.which('unzip') subprocess.call( @@ -104,15 +117,19 @@ def unzip(app_path, ext_path): files_det = ['Length Date Time Name'] files_det = files_det + dat return files_det - except Exception: - logger.exception('Unzipping Error') + except Exception as exp: + msg = 'Unzipping Error with OS unzip utility' + logger.exception(msg) + append_scan_status(checksum, msg, repr(exp)) -def lipo_thin(src, dst): +def lipo_thin(checksum, src, dst): """Thin Fat binary.""" new_src = None try: - logger.info('Thinning Fat binary') + msg = 'Thinning Fat binary' + logger.info(msg) + append_scan_status(checksum, msg) lipo = shutil.which('lipo') out = Path(dst) / (Path(src).stem + '_thin.a') new_src = out.as_posix() @@ -135,8 +152,10 @@ def lipo_thin(src, dst): stderr=subprocess.STDOUT) if out.returncode == 0: break - except Exception: - logger.warning('lipo Fat binary thinning failed') + except Exception as exp: + msg = 'lipo Fat binary thinning failed' + logger.warning(msg) + append_scan_status(checksum, msg, repr(exp)) return new_src @@ -156,10 +175,11 @@ def ar_os(src, dst): return out -def ar_extract(src, dst): +def ar_extract(checksum, src, dst): """Extract AR archive.""" msg = 'Extracting static library archive' logger.info(msg) + append_scan_status(checksum, msg) try: ar = arpy.Archive(src) ar.read_all_headers() @@ -172,28 +192,38 @@ def ar_extract(src, dst): out.write_bytes(val.read()) except Exception: # Possibly dealing with Fat binary, needs Mac host - logger.warning('Failed to extract .a archive') + msg = 'Failed to extract .a archive' + logger.warning(msg) + append_scan_status(checksum, msg) # Use os ar utility plat = platform.system() os_err = 'Possibly a Fat binary. Requires MacOS for Analysis' if plat == 'Windows': logger.warning(os_err) + append_scan_status(checksum, os_err) return - logger.info('Using OS ar utility to handle archive') + msg = 'Using OS ar utility to handle archive' + logger.info(msg) + append_scan_status(checksum, msg) exp = ar_os(src, dst) if len(exp) > 3 and plat == 'Linux': # Can't convert FAT binary in Linux logger.warning(os_err) + append_scan_status(checksum, os_err) return if b'lipo(1)' in exp: - logger.info('Fat binary archive identified') + msg = 'Fat binary archive identified' + logger.info(msg) + append_scan_status(checksum, msg) # Fat binary archive try: - nw_src = lipo_thin(src, dst) + nw_src = lipo_thin(checksum, src, dst) if nw_src: ar_os(nw_src, dst) - except Exception: - logger.exception('Failed to thin fat archive.') + except Exception as exp: + msg = 'Failed to thin fat archive' + logger.exception(msg) + append_scan_status(checksum, msg, repr(exp)) def url_n_email_extract(dat, relative_path): @@ -257,7 +287,7 @@ def get_avg_cvss(findings): return avg_cvss -def open_firebase(url): +def open_firebase(checksum, url): # Detect Open Firebase Database try: invalid = 'Invalid Firebase URL' @@ -279,19 +309,21 @@ def open_firebase(url): allow_redirects=False) if resp.status_code == 200: return base_url, True - except Exception: - logger.warning('Open Firebase DB detection failed.') + 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_analysis(urls): +def firebase_analysis(checksum, urls): # Detect Firebase URL firebase_db = [] logger.info('Detecting Firebase URL(s)') for url in urls: if 'firebaseio.com' not in url: continue - returl, is_open = open_firebase(url) + returl, is_open = open_firebase(checksum, url) fbdic = {'url': returl, 'open': is_open} if fbdic not in firebase_db: firebase_db.append(fbdic) @@ -338,9 +370,11 @@ def is_secret_key(inp): return any(i in inp for i in iden) and not not_str -def strings_and_entropies(src, exts): +def strings_and_entropies(checksum, src, exts): """Get Strings and Entropies.""" - logger.info('Extracting Data from Source Code') + msg = 'Extracting String values and entropies from Code' + logger.info(msg) + append_scan_status(checksum, msg) data = { 'strings': set(), 'secrets': set(), @@ -369,8 +403,10 @@ def strings_and_entropies(src, exts): data['strings'].add(string) if data['strings']: data['secrets'] = get_entropies(data['strings']) - except Exception: - logger.exception('Extracting Data from Code') + except Exception as exp: + msg = 'Failed to extract String values and entropies from Code' + logger.exception(msg) + append_scan_status(checksum, msg, repr(exp)) return data diff --git a/mobsf/StaticAnalyzer/views/ios/app_transport_security.py b/mobsf/StaticAnalyzer/views/ios/app_transport_security.py index fb5b2dde94..bf4086611f 100644 --- a/mobsf/StaticAnalyzer/views/ios/app_transport_security.py +++ b/mobsf/StaticAnalyzer/views/ios/app_transport_security.py @@ -1,9 +1,3 @@ -import logging - - -logger = logging.getLogger(__name__) - - def check_transport_security(p_list): """Check info.plist for insecure connection configurations.""" ats = [] diff --git a/mobsf/StaticAnalyzer/views/ios/appstore.py b/mobsf/StaticAnalyzer/views/ios/appstore.py index 5d4afc5744..b6da627ed2 100644 --- a/mobsf/StaticAnalyzer/views/ios/appstore.py +++ b/mobsf/StaticAnalyzer/views/ios/appstore.py @@ -5,14 +5,19 @@ from django.conf import settings -from mobsf.MobSF.utils import upstream_proxy +from mobsf.MobSF.utils import ( + append_scan_status, + upstream_proxy, +) logger = logging.getLogger(__name__) -def app_search(app_id): +def app_search(checksum, app_id): """IOS Get App Details from App Store.""" - logger.info('Fetching Details from App Store: %s', app_id) + msg = f'Fetching Details from App Store: {app_id}' + logger.info(msg) + append_scan_status(checksum, msg) lookup_url = settings.ITUNES_URL req_url = '{}?bundleId={}&country={}&entity=software'.format( lookup_url, app_id, 'us') @@ -23,8 +28,9 @@ def app_search(app_id): try: det = {} proxies, verify = upstream_proxy('https') - req = requests.get(req_url, headers=headers, - proxies=proxies, verify=verify) + req = requests.get( + req_url, headers=headers, + proxies=proxies, verify=verify) resp = req.json() if resp['results']: det = resp['results'][0] @@ -49,6 +55,8 @@ def app_search(app_id): } logger.warning('Unable to get app details.') return {'error': True} - except Exception: - logger.warning('Unable to get app details') + except Exception as exp: + msg = 'Failed to get app details' + logger.warning(msg) + append_scan_status(checksum, msg, repr(exp)) return {'error': True} diff --git a/mobsf/StaticAnalyzer/views/ios/binary_analysis.py b/mobsf/StaticAnalyzer/views/ios/binary_analysis.py index 7416ee52cd..b9b2489bfd 100755 --- a/mobsf/StaticAnalyzer/views/ios/binary_analysis.py +++ b/mobsf/StaticAnalyzer/views/ios/binary_analysis.py @@ -21,6 +21,9 @@ from mobsf.StaticAnalyzer.views.ios.binary_rule_matcher import ( binary_rule_matcher, ) +from mobsf.MobSF.utils import ( + append_scan_status, +) logger = logging.getLogger(__name__) @@ -69,7 +72,7 @@ def ipa_macho_analysis(binary): return {} -def binary_analysis(src, tools_dir, app_dir, executable_name): +def binary_analysis(checksum, src, tools_dir, app_dir, executable_name): """Binary Analysis of IPA.""" bin_dict = { 'checksec': {}, @@ -81,7 +84,9 @@ def binary_analysis(src, tools_dir, app_dir, executable_name): } try: binary_findings = {} - logger.info('Starting Binary Analysis') + msg = 'Starting Binary Analysis' + logger.info(msg) + append_scan_status(checksum, msg) dirs = Path(src).glob('*') dot_app_dir = '' bin_name = '' @@ -100,18 +105,23 @@ def binary_analysis(src, tools_dir, app_dir, executable_name): # Bin Path - Dir/Payload/x.app/x bin_path = bin_dir / bin_name if not (bin_path.exists() and bin_path.is_file()): - logger.warning( - 'MobSF Cannot find binary in %s', bin_path.as_posix()) - logger.warning('Skipping Binary analysis') + msg = ( + f'MobSF Cannot find binary in {bin_path.as_posix()}. ' + 'Skipping Binary Analysis.') + logger.warning(msg) + append_scan_status(checksum, 'Skipping binary analysis', msg) else: macho = ipa_macho_analysis(bin_path) bin_info = get_bin_info(bin_path) bin_type = detect_bin_type(macho['libraries']) classdump = get_class_dump( + checksum, tools_dir, bin_path, - app_dir, bin_type) + app_dir, + bin_type) binary_rule_matcher( + checksum, binary_findings, macho['symbols'], classdump) bin_dict['checksec'] = macho['checksec'] @@ -122,6 +132,8 @@ def binary_analysis(src, tools_dir, app_dir, executable_name): logger.info('Running strings against the Binary') bin_dict['strings'] = strings_on_binary( bin_path.as_posix()) - except Exception: - logger.exception('IPA Binary Analysis') + except Exception as exp: + msg = 'Failed to run IPA Binary Analysis' + logger.exception(msg) + append_scan_status(checksum, msg, repr(exp)) return bin_dict diff --git a/mobsf/StaticAnalyzer/views/ios/binary_rule_matcher.py b/mobsf/StaticAnalyzer/views/ios/binary_rule_matcher.py index 94b46a08f6..d4497b0e4c 100644 --- a/mobsf/StaticAnalyzer/views/ios/binary_rule_matcher.py +++ b/mobsf/StaticAnalyzer/views/ios/binary_rule_matcher.py @@ -6,6 +6,9 @@ from mobsf.StaticAnalyzer.views.ios.rules import ( ipa_rules, ) +from mobsf.MobSF.utils import ( + append_scan_status, +) logger = logging.getLogger(__name__) @@ -26,7 +29,7 @@ def _add_bfindings(findings, desc, detailed_desc, rule): 'masvs': rule['masvs']} -def binary_rule_matcher(findings, symbols, classdump): +def binary_rule_matcher(checksum, findings, symbols, classdump): """Static Analysis Rule Matcher.""" try: data = classdump + '\n'.join(symbols).encode('utf-8') @@ -53,6 +56,11 @@ def binary_rule_matcher(findings, symbols, classdump): rule) else: - logger.error('Binary rule Error\n%s', rule) - except Exception: - logger.exception('Error in Binary Rule Processing') + pre = 'Binary Rule Error' + msg = f'{pre}\n{rule}' + logger.error(msg) + append_scan_status(checksum, pre, msg) + except Exception as exp: + msg = 'Error in Binary Rule Processing' + logger.exception(msg) + append_scan_status(checksum, msg, repr(exp)) diff --git a/mobsf/StaticAnalyzer/views/ios/classdump.py b/mobsf/StaticAnalyzer/views/ios/classdump.py index 8caadc83ae..923f3b865b 100644 --- a/mobsf/StaticAnalyzer/views/ios/classdump.py +++ b/mobsf/StaticAnalyzer/views/ios/classdump.py @@ -9,20 +9,24 @@ from django.conf import settings -from mobsf.MobSF.utils import is_file_exists +from mobsf.MobSF.utils import ( + append_scan_status, + is_file_exists, +) logger = logging.getLogger(__name__) -def classdump_mac(clsdmp_bin, tools_dir, ipa_bin): +def classdump_mac(checksum, clsdmp_bin, tools_dir, ipa_bin): """Run Classdump for Objective-C/Swift.""" if clsdmp_bin == 'class-dump-swift': - logger.info('Running class-dump-swift against binary') external = settings.CLASSDUMP_SWIFT_BINARY else: - logger.info('Running class-dump against binary') external = settings.CLASSDUMP_BINARY + msg = f'Running {clsdmp_bin} against binary' + logger.info(msg) + append_scan_status(checksum, msg) if (len(external) > 0 and is_file_exists(external)): class_dump_bin = external @@ -37,7 +41,7 @@ def classdump_mac(clsdmp_bin, tools_dir, ipa_bin): stderr=subprocess.DEVNULL) -def classdump_linux(tools_dir, ipa_bin): +def classdump_linux(checksum, tools_dir, ipa_bin): """Run Classdump on Linux.""" try: if (len(settings.JTOOL_BINARY) > 0 @@ -47,7 +51,9 @@ def classdump_linux(tools_dir, ipa_bin): jtool_bin = os.path.join(tools_dir, 'jtool.ELF64') if not os.access(jtool_bin, os.X_OK): os.chmod(jtool_bin, stat.S_IEXEC) - logger.info('Running jtool against the binary for dumping classes') + msg = 'Running jtool against the binary for dumping classes' + logger.info(msg) + append_scan_status(checksum, msg) args = [jtool_bin, '-arch', 'arm', '-d', 'objc', '-v', ipa_bin] # timeout to handle possible deadlock from jtool1 return subprocess.check_output( @@ -58,22 +64,26 @@ def classdump_linux(tools_dir, ipa_bin): return b'' -def get_class_dump(tools_dir, bin_path, app_dir, bin_type): +def get_class_dump(checksum, tools_dir, bin_path, app_dir, bin_type): """Running Classdump on binary.""" try: bin_path = bin_path.as_posix() cdump = b'' - logger.info('Dumping classes') + msg = 'Dumping Classes from the binary' + logger.info(msg) + append_scan_status(checksum, msg) if platform.system() == 'Darwin': if bin_type == 'Swift': try: cdump = classdump_mac( + checksum, 'class-dump-swift', tools_dir, bin_path, ) except Exception: cdump = classdump_mac( + checksum, 'class-dump', tools_dir, bin_path, @@ -81,32 +91,41 @@ def get_class_dump(tools_dir, bin_path, app_dir, bin_type): else: try: cdump = classdump_mac( + checksum, 'class-dump', tools_dir, bin_path, ) except Exception: cdump = classdump_mac( + checksum, 'class-dump-swift', tools_dir, bin_path, ) if b'Source: (null)' in cdump: # Run failsafe if classdump failed - logger.info('Running fail safe class-dump-swift') + msg = 'Running fail safe class-dump-swift' + logger.info(msg) + append_scan_status(checksum, msg) cdump = classdump_mac( + checksum, 'class-dump-swift', tools_dir, bin_path, ) elif platform.system() == 'Linux': - cdump = classdump_linux(tools_dir, bin_path) + cdump = classdump_linux(checksum, tools_dir, bin_path) else: # Platform not supported - logger.warning('class-dump is not supported in this platform') + msg = 'class-dump is not supported in this platform' + logger.warning(msg) + append_scan_status(checksum, msg) with open(os.path.join(app_dir, 'classdump.txt'), 'wb') as flip: flip.write(cdump) return cdump - except Exception: - logger.error('class-dump/class-dump-swift failed on this binary') + except Exception as exp: + msg = 'Failed to dump classes from the binary' + logger.exception(msg) + append_scan_status(checksum, msg, repr(exp)) return cdump diff --git a/mobsf/StaticAnalyzer/views/ios/code_analysis.py b/mobsf/StaticAnalyzer/views/ios/code_analysis.py index 39de03388f..d29a923a36 100755 --- a/mobsf/StaticAnalyzer/views/ios/code_analysis.py +++ b/mobsf/StaticAnalyzer/views/ios/code_analysis.py @@ -11,6 +11,9 @@ url_n_email_extract, ) from mobsf.StaticAnalyzer.views.sast_engine import scan +from mobsf.MobSF.utils import ( + append_scan_status, +) logger = logging.getLogger(__name__) @@ -35,7 +38,7 @@ def merge_findings(swift, objc): return code_analysis -def ios_source_analysis(src): +def ios_source_analysis(checksum, src): """IOS Objective-C and Swift Code Analysis.""" try: logger.info('Starting iOS Source Code and PLIST Analysis') @@ -54,6 +57,7 @@ def ios_source_analysis(src): # Code and API Analysis objc_findings = scan( + checksum, objective_c_rules.as_posix(), {'.m'}, [src], @@ -61,6 +65,7 @@ def ios_source_analysis(src): if objc_findings: source_types.add(_SourceType.objc) swift_findings = scan( + checksum, swift_rules.as_posix(), {'.swift'}, [src], @@ -70,6 +75,7 @@ def ios_source_analysis(src): code_findings = merge_findings(swift_findings, objc_findings) # API Analysis api_findings = scan( + checksum, api_rules.as_posix(), {'.m', '.swift'}, [src], @@ -100,9 +106,12 @@ def ios_source_analysis(src): urls_list = list(set(url_list)) # Domain Extraction and Malware Check - logger.info('Performing Malware Check on extracted Domains') - domains = MalwareDomainCheck().scan(urls_list) - logger.info('Finished Code Analysis, Email and URL Extraction') + domains = MalwareDomainCheck().scan( + checksum, + urls_list) + msg = 'Finished Code Analysis, Email and URL Extraction' + logger.info(msg) + append_scan_status(checksum, msg) code_analysis_dict = { 'api': api_findings, 'code_anal': code_findings, @@ -113,6 +122,7 @@ def ios_source_analysis(src): 'source_type': source_type, } return code_analysis_dict - - except Exception: - logger.exception('iOS Source Code Analysis') + except Exception as exp: + msg = 'iOS Source Analysis Failed' + logger.exception(msg) + append_scan_status(checksum, msg, repr(exp)) diff --git a/mobsf/StaticAnalyzer/views/ios/db_interaction.py b/mobsf/StaticAnalyzer/views/ios/db_interaction.py index fe2f530841..541b989f96 100755 --- a/mobsf/StaticAnalyzer/views/ios/db_interaction.py +++ b/mobsf/StaticAnalyzer/views/ios/db_interaction.py @@ -3,7 +3,12 @@ from django.conf import settings -from mobsf.MobSF.utils import python_dict, python_list +from mobsf.MobSF.utils import ( + append_scan_status, + get_scan_logs, + python_dict, + python_list, +) from mobsf.MobSF.views.home import update_scan_timestamp from mobsf.StaticAnalyzer.models import StaticAnalyzerIOS from mobsf.StaticAnalyzer.models import RecentScansDB @@ -17,7 +22,8 @@ def get_context_from_db_entry(db_entry): """Return the context for IPA/ZIP from DB.""" try: - logger.info('Analysis is already Done. Fetching data from the DB...') + msg = 'Analysis is already Done. Fetching data from the DB...' + logger.info(msg) bundle_id = db_entry[0].BUNDLE_ID code = process_suppression( python_dict(db_entry[0].CODE_ANALYSIS), @@ -66,11 +72,12 @@ def get_context_from_db_entry(db_entry): 'appstore_details': python_dict(db_entry[0].APPSTORE_DETAILS), 'secrets': python_list(db_entry[0].SECRETS), 'trackers': python_dict(db_entry[0].TRACKERS), - + 'logs': get_scan_logs(db_entry[0].MD5), } return context except Exception: - logger.exception('Fetching from DB') + msg = 'Fetching data from the DB failed.' + logger.exception(msg) def get_context_from_analysis(app_dict, @@ -128,10 +135,13 @@ def get_context_from_analysis(app_dict, 'appstore_details': app_dict['appstore'], 'secrets': app_dict['secrets'], 'trackers': code_dict['trackers'], + 'logs': get_scan_logs(app_dict['md5_hash']), } return context - except Exception: - logger.exception('Rendering to Template') + except Exception as exp: + msg = 'Rendering to Template' + logger.exception(msg) + append_scan_status(app_dict['md5_hash'], msg, repr(exp)) def save_or_update(update_type, @@ -190,8 +200,10 @@ def save_or_update(update_type, else: StaticAnalyzerIOS.objects.filter( MD5=app_dict['md5_hash']).update(**values) - except Exception: - logger.exception('Updating DB') + except Exception as exp: + msg = 'Failed to Save/Update Database' + logger.exception(msg) + append_scan_status(app_dict['md5_hash'], msg, repr(exp)) try: values = { 'APP_NAME': info_dict['bin_name'], @@ -200,19 +212,25 @@ def save_or_update(update_type, } RecentScansDB.objects.filter( MD5=app_dict['md5_hash']).update(**values) - except Exception: - logger.exception('Updating RecentScansDB') + except Exception as exp: + msg = 'Updating RecentScansDB table failed' + logger.exception(msg) + append_scan_status(app_dict['md5_hash'], msg, repr(exp)) def save_get_ctx(app_dict, pdict, code_dict, bin_dict, all_files, rescan): # Saving to DB logger.info('Connecting to DB') if rescan: - logger.info('Updating Database...') + msg = 'Updating Database...' + logger.info(msg) + append_scan_status(app_dict['md5_hash'], msg) action = 'update' update_scan_timestamp(app_dict['md5_hash']) else: - logger.info('Saving to Database') + msg = 'Saving to Database' + logger.info(msg) + append_scan_status(app_dict['md5_hash'], msg) action = 'save' save_or_update( action, diff --git a/mobsf/StaticAnalyzer/views/ios/dylib.py b/mobsf/StaticAnalyzer/views/ios/dylib.py index bf1dd2d7a2..78ab19e331 100644 --- a/mobsf/StaticAnalyzer/views/ios/dylib.py +++ b/mobsf/StaticAnalyzer/views/ios/dylib.py @@ -10,6 +10,7 @@ from django.shortcuts import render from mobsf.MobSF.utils import ( + append_scan_status, file_size, print_n_send_error_response, ) @@ -48,14 +49,14 @@ def dylib_analysis(request, app_dict, rescan, api): """Independent Dylib (.dylib) analysis.""" + checksum = app_dict['md5_hash'] app_dir = Path(app_dict['app_dir']) - app_dict['app_file'] = app_dict[ - 'md5_hash'] + '.dylib' # NEW FILENAME + app_dict['app_file'] = f'{checksum}.dylib' app_dict['app_path'] = app_dir / app_dict['app_file'] app_dict['app_path'] = app_dict['app_path'].as_posix() # DB ipa_db = StaticAnalyzerIOS.objects.filter( - MD5=app_dict['md5_hash']) + MD5=checksum) if ipa_db.exists() and not rescan: context = get_context_from_db_entry(ipa_db) else: @@ -64,10 +65,14 @@ def dylib_analysis(request, app_dict, rescan, api): request, 'Permission Denied', False) - logger.info('iOS DYLIB Analysis Started') + append_scan_status(checksum, 'init') + msg = 'Dylib Analysis Started' + logger.info(msg) + append_scan_status(checksum, msg) app_dict['size'] = str( file_size(app_dict['app_path'])) + 'MB' # FILE SIZE app_dict['sha1'], app_dict['sha256'] = hash_gen( + checksum, app_dict['app_path']) # SHA1 & SHA256 HASHES app_dict['bin_dir'] = app_dict['app_dir'] # Get Files @@ -107,8 +112,8 @@ def dylib_analysis(request, app_dict, rescan, api): } # Analyze dylib dy = library_analysis( + checksum, app_dict['bin_dir'], - app_dict['md5_hash'], 'macho') bin_dict['dylib_analysis'] = dy['macho_analysis'] bin_dict['framework_analysis'] = {} @@ -117,6 +122,7 @@ def dylib_analysis(request, app_dict, rescan, api): dy['macho_symbols']) # Binary code analysis on dylib symbols binary_rule_matcher( + checksum, bin_dict['bin_code_analysis'], all_files['special_files'], b'') @@ -126,23 +132,22 @@ def dylib_analysis(request, app_dict, rescan, api): bin_dict, all_files, dy['macho_strings']) - # Domain Extraction and Malware Check - logger.info('Performing Malware Check on ' - 'extracted Domains') code_dict['domains'] = MalwareDomainCheck().scan( + checksum, code_dict['urls_list']) logger.info('Finished URL and Email Extraction') - # Extract Trackers from Domains trk = Trackers.Trackers( - None, app_dict['tools_dir']) + checksum, + None, + app_dict['tools_dir']) trackers = trk.get_trackers_domains_or_deps( code_dict['domains'], []) - code_dict['api'] = {} code_dict['code_anal'] = {} code_dict['firebase'] = firebase_analysis( + checksum, code_dict['urls_list']) code_dict['trackers'] = trackers context = save_get_ctx( @@ -154,10 +159,9 @@ def dylib_analysis(request, app_dict, rescan, api): rescan) context['virus_total'] = None if settings.VT_ENABLED: - vt = VirusTotal.VirusTotal() + vt = VirusTotal.VirusTotal(checksum) context['virus_total'] = vt.get_result( - app_dict['app_path'], - app_dict['md5_hash']) + app_dict['app_path']) context['appsec'] = {} context['average_cvss'] = None template = 'static_analysis/ios_binary_analysis.html' diff --git a/mobsf/StaticAnalyzer/views/ios/file_analysis.py b/mobsf/StaticAnalyzer/views/ios/file_analysis.py index 5615aef42c..40a924938c 100644 --- a/mobsf/StaticAnalyzer/views/ios/file_analysis.py +++ b/mobsf/StaticAnalyzer/views/ios/file_analysis.py @@ -8,15 +8,22 @@ from django.utils.html import escape -from mobsf.StaticAnalyzer.views.ios.plist_analysis import convert_bin_xml +from mobsf.StaticAnalyzer.views.ios.plist_analysis import ( + convert_bin_xml, +) +from mobsf.MobSF.utils import ( + append_scan_status, +) logger = logging.getLogger(__name__) -def ios_list_files(src, md5_hash, binary_form, mode): +def ios_list_files(md5_hash, src, binary_form, mode): """List iOS files.""" try: - logger.info('Get Files, BIN Plist -> XML, and Normalize') + msg = 'iOS File Analysis and Normalization' + logger.info(msg) + append_scan_status(md5_hash, msg) # Multi function, Get Files, BIN Plist -> XML, normalize + to x filez = [] certz = [] @@ -81,5 +88,7 @@ def ios_list_files(src, md5_hash, binary_form, mode): return {'files_short': filez, 'files_long': full_paths, 'special_files': sfiles} - except Exception: - logger.exception('iOS List Files') + except Exception as exp: + msg = 'iOS File Analysis' + logger.exception(msg) + append_scan_status(md5_hash, msg, repr(exp)) diff --git a/mobsf/StaticAnalyzer/views/ios/icon_analysis.py b/mobsf/StaticAnalyzer/views/ios/icon_analysis.py index ec12d6a671..1f50264994 100644 --- a/mobsf/StaticAnalyzer/views/ios/icon_analysis.py +++ b/mobsf/StaticAnalyzer/views/ios/icon_analysis.py @@ -11,7 +11,10 @@ from django.conf import settings -from mobsf.MobSF.utils import is_dir_exists +from mobsf.MobSF.utils import ( + append_scan_status, + is_dir_exists, +) logger = logging.getLogger(__name__) @@ -21,7 +24,9 @@ def get_icon_from_ipa(app_dict, binary): try: md5 = app_dict['md5_hash'] bin_dir = app_dict['bin_dir'] - logger.info('Fetching icon path') + msg = 'Fetching IPA icon path' + logger.info(msg) + append_scan_status(md5, msg) bin_path = os.path.join(bin_dir, binary + '.app') if not is_dir_exists(bin_path): logger.warning('Could not find app binary directory') @@ -48,15 +53,19 @@ def get_icon_from_ipa(app_dict, binary): else: shutil.copy2(icon_file, outfile.as_posix()) app_dict['icon_path'] = outfile.name - except Exception: - logger.exception('Error Fetching icon') + except Exception as exp: + msg = 'Error Fetching IPA icon' + logger.exception(msg) + append_scan_status(md5, msg, repr(exp)) def get_icon_source(app_dict): - md5 = app_dict['md5_hash'] + checksum = app_dict['md5_hash'] src_dir = app_dict['app_dir'] """Get app icon from iOS ZIP.""" - logger.info('Fetching icon path') + msg = 'Fetching icon path' + logger.info(msg) + append_scan_status(checksum, msg) try: appiconset = [] for dirname, _, files in os.walk(src_dir): @@ -69,8 +78,10 @@ def get_icon_source(app_dict): if not appiconset: return icon_file = appiconset[0] - outfile = Path(settings.DWD_DIR) / f'{md5}-icon.png' + outfile = Path(settings.DWD_DIR) / f'{checksum}-icon.png' shutil.copy2(icon_file, outfile.as_posix()) app_dict['icon_path'] = outfile.name - except Exception: - logger.exception('Error Fetching icon') + except Exception as exp: + msg = 'Error Fetching icon' + logger.exception(msg) + append_scan_status(checksum, msg, repr(exp)) diff --git a/mobsf/StaticAnalyzer/views/ios/permission_analysis.py b/mobsf/StaticAnalyzer/views/ios/kb/permission_analysis.py similarity index 88% rename from mobsf/StaticAnalyzer/views/ios/permission_analysis.py rename to mobsf/StaticAnalyzer/views/ios/kb/permission_analysis.py index d1ddf0b5a7..a4dc620977 100644 --- a/mobsf/StaticAnalyzer/views/ios/permission_analysis.py +++ b/mobsf/StaticAnalyzer/views/ios/kb/permission_analysis.py @@ -12,7 +12,7 @@ 'without using an MDM server.'), 'normal'), 'NFCReaderUsageDescription': ( - 'Access device’s NFC reader.', + 'Access device\'s NFC reader.', 'dangerous'), 'NSAppleMusicUsageDescription': ( 'Access Apple Media Library.', @@ -33,7 +33,7 @@ 'Access the ability to authenticate with Face ID.', 'normal'), 'NSHealthClinicalHealthRecordsShareUsageDescription': ( - 'Access user’s clinical health records.', + 'Access user\'s clinical health records.', 'dangerous'), 'NSHealthShareUsageDescription': ( 'Read Health Data.', @@ -57,22 +57,22 @@ 'Access microphone.', 'dangerous'), 'NSMotionUsageDescription': ( - 'Access the device’s accelerometer.', + 'Access the device\'s accelerometer.', 'dangerous'), 'NSPhotoLibraryUsageDescription': ( - 'Access the user’s photo library.', + 'Access the user\'s photo library.', 'dangerous'), 'NSRemindersUsageDescription': ( - 'Access the user’s reminders.', + 'Access the user\'s reminders.', 'dangerous'), 'NSSiriUsageDescription': ( 'Allow app to send user data to Siri', 'dangerous'), 'NSSpeechRecognitionUsageDescription': ( - 'Allow app to send user data to Apple’s speech recognition servers.', + 'Allow app to send user data to Apple\'s speech recognition servers.', 'normal'), 'NSVideoSubscriberAccountUsageDescription': ( - 'Access the user’s TV provider account.', + 'Access the user\'s TV provider account.', 'normal'), 'NSLocalNetworkUsageDescription': ( 'Allow app to request access to the local network.', diff --git a/mobsf/StaticAnalyzer/views/ios/plist_analysis.py b/mobsf/StaticAnalyzer/views/ios/plist_analysis.py index 34076b1ae3..862a124c9a 100755 --- a/mobsf/StaticAnalyzer/views/ios/plist_analysis.py +++ b/mobsf/StaticAnalyzer/views/ios/plist_analysis.py @@ -20,10 +20,11 @@ ) from mobsf.MobSF.utils import ( + append_scan_status, find_key_in_dict, is_file_exists, ) -from mobsf.StaticAnalyzer.views.ios.permission_analysis import ( +from mobsf.StaticAnalyzer.views.ios.kb.permission_analysis import ( check_permissions, ) from mobsf.StaticAnalyzer.views.ios.app_transport_security import ( @@ -107,10 +108,12 @@ def convert_bin_xml(bin_xml_file): logger.warning('Failed to convert plist') -def plist_analysis(src, is_source): +def plist_analysis(checksum, src, is_source): """Plist Analysis.""" try: - logger.info('iOS Info.plist Analysis Started') + msg = 'iOS Info.plist Analysis Started' + logger.info(msg) + append_scan_status(checksum, msg) plist_info = { 'bin_name': '', 'bin': '', @@ -132,7 +135,9 @@ def plist_analysis(src, is_source): plist_file = None plist_files = [] if is_source: - logger.info('Finding Info.plist in iOS Source') + msg = 'Finding Info.plist in iOS Source' + logger.info(msg) + append_scan_status(checksum, msg) for dirpath, _dirnames, files in os.walk(src): for name in files: if (not any(x in dirpath for x in SKIP_PATH) @@ -143,7 +148,9 @@ def plist_analysis(src, is_source): if name == 'Info.plist' or '-Info.plist' in name: plist_file = os.path.join(dirpath, name) else: - logger.info('Finding Info.plist in iOS Binary') + msg = 'Finding Info.plist in iOS Binary' + logger.info(msg) + append_scan_status(checksum, msg) dirs = os.listdir(src) dot_app_dir = '' for dir_ in dirs: @@ -202,8 +209,10 @@ def plist_analysis(src, is_source): 'ats_summary': get_summary(ats), } return plist_info - except Exception: - logger.exception('Reading from Info.plist') + except Exception as exp: + msg = 'Reading from Info.plist' + logger.exception(msg) + append_scan_status(checksum, msg, repr(exp)) def get_summary(ats): @@ -223,8 +232,11 @@ def get_summary(ats): return summary -def get_plist_secrets(app_dir): +def get_plist_secrets(checksum, app_dir): """Get possible hardcoded secrets from plist files.""" + msg = 'Searching for secrets in plist files' + logger.info(msg) + append_scan_status(checksum, msg) result_list = set() def _remove_tags(data): diff --git a/mobsf/StaticAnalyzer/views/ios/static_analyzer.py b/mobsf/StaticAnalyzer/views/ios/static_analyzer.py index 638c880217..4021d38d44 100755 --- a/mobsf/StaticAnalyzer/views/ios/static_analyzer.py +++ b/mobsf/StaticAnalyzer/views/ios/static_analyzer.py @@ -11,6 +11,7 @@ from django.template.defaulttags import register from mobsf.MobSF.utils import ( + append_scan_status, file_size, is_md5, print_n_send_error_response, @@ -114,7 +115,6 @@ def static_analyzer_ios(request, checksum, api=False): request, 'Invalid file extension or file type', api) - app_dict['directory'] = Path(settings.BASE_DIR) # BASE DIR app_dict['file_name'] = filename # APP ORIGINAL NAME app_dict['md5_hash'] = checksum # MD5 @@ -125,13 +125,11 @@ def static_analyzer_ios(request, checksum, api=False): app_dict['tools_dir'] = tools_dir.as_posix() app_dict['icon_path'] = '' if file_type == 'ipa': - app_dict['app_file'] = app_dict[ - 'md5_hash'] + '.ipa' # NEW FILENAME + app_dict['app_file'] = f'{checksum}.ipa' app_dict['app_path'] = app_dir / app_dict['app_file'] app_dict['app_path'] = app_dict['app_path'].as_posix() # DB - ipa_db = StaticAnalyzerIOS.objects.filter( - MD5=app_dict['md5_hash']) + ipa_db = StaticAnalyzerIOS.objects.filter(MD5=checksum) if ipa_db.exists() and not rescan: context = get_context_from_db_entry(ipa_db) else: @@ -140,14 +138,23 @@ def static_analyzer_ios(request, checksum, api=False): request, 'Permission Denied', False) - logger.info('iOS Binary (IPA) Analysis Started') + append_scan_status(checksum, 'init') + msg = 'iOS Binary (IPA) Analysis Started' + logger.info(msg) + append_scan_status(checksum, msg) app_dict['size'] = str( file_size(app_dict['app_path'])) + 'MB' # FILE SIZE app_dict['sha1'], app_dict['sha256'] = hash_gen( + checksum, app_dict['app_path']) # SHA1 & SHA256 HASHES - logger.info('Extracting IPA') + msg = 'Extracting IPA' + logger.info(msg) + append_scan_status(checksum, msg) # EXTRACT IPA - unzip(app_dict['app_path'], app_dict['app_dir']) + unzip( + checksum, + app_dict['app_path'], + app_dict['app_dir']) # Identify Payload directory dirs = app_dir.glob('**/*') for _dir in dirs: @@ -157,6 +164,7 @@ def static_analyzer_ios(request, checksum, api=False): else: msg = ('IPA is malformed! ' 'MobSF cannot find Payload directory') + append_scan_status(checksum, 'IPA is malformed', msg) return print_n_send_error_response( request, msg, @@ -164,21 +172,31 @@ def static_analyzer_ios(request, checksum, api=False): app_dict['bin_dir'] = app_dict['bin_dir'].as_posix() + '/' # Get Files all_files = ios_list_files( - app_dict['bin_dir'], app_dict['md5_hash'], True, 'ipa') + checksum, + app_dict['bin_dir'], + True, + 'ipa') # Plist files are converted to xml/readable - infoplist_dict = plist_analysis(app_dict['bin_dir'], False) - app_dict['appstore'] = app_search(infoplist_dict.get('id')) + infoplist_dict = plist_analysis( + checksum, + app_dict['bin_dir'], + False) + app_dict['appstore'] = app_search( + checksum, + infoplist_dict.get('id')) app_dict['secrets'] = get_plist_secrets( + checksum, app_dict['bin_dir']) bin_dict = binary_analysis( + checksum, app_dict['bin_dir'], app_dict['tools_dir'], app_dict['app_dir'], infoplist_dict.get('bin')) # Analyze dylibs and frameworks lb = library_analysis( + checksum, app_dict['bin_dir'], - app_dict['md5_hash'], 'macho') bin_dict['dylib_analysis'] = lb['macho_analysis'] bin_dict['framework_analysis'] = lb['framework_analysis'] @@ -192,23 +210,21 @@ def static_analyzer_ios(request, checksum, api=False): bin_dict, all_files, lb['macho_strings']) - # Domain Extraction and Malware Check - logger.info('Performing Malware Check on ' - 'extracted Domains') code_dict['domains'] = MalwareDomainCheck().scan( + checksum, code_dict['urls_list']) - logger.info('Finished URL and Email Extraction') - # Extract Trackers from Domains trk = Trackers.Trackers( - None, app_dict['tools_dir']) + checksum, + None, + app_dict['tools_dir']) trackers = trk.get_trackers_domains_or_deps( code_dict['domains'], []) - code_dict['api'] = {} code_dict['code_anal'] = {} code_dict['firebase'] = firebase_analysis( + checksum, code_dict['urls_list']) code_dict['trackers'] = trackers context = save_get_ctx( @@ -220,10 +236,9 @@ def static_analyzer_ios(request, checksum, api=False): rescan) context['virus_total'] = None if settings.VT_ENABLED: - vt = VirusTotal.VirusTotal() + vt = VirusTotal.VirusTotal(checksum) context['virus_total'] = vt.get_result( - app_dict['app_path'], - app_dict['md5_hash']) + app_dict['app_path']) context['appsec'] = get_ios_dashboard(context, True) context['average_cvss'] = get_avg_cvss( context['binary_analysis']) @@ -238,7 +253,7 @@ def static_analyzer_ios(request, checksum, api=False): return a_analysis(request, app_dict, rescan, api) elif file_type in ('ios', 'zip'): ios_zip_db = StaticAnalyzerIOS.objects.filter( - MD5=app_dict['md5_hash']) + MD5=checksum) if ios_zip_db.exists() and not rescan: context = get_context_from_db_entry(ios_zip_db) else: @@ -253,22 +268,32 @@ def static_analyzer_ios(request, checksum, api=False): app_dict['app_path'] = app_dir / app_dict['app_file'] app_dict['app_path'] = app_dict['app_path'].as_posix() # ANALYSIS BEGINS - Already Unzipped + # append_scan_status init done in android static analyzer app_dict['size'] = str( file_size(app_dict['app_path'])) + 'MB' # FILE SIZE app_dict['sha1'], app_dict['sha256'] = hash_gen( + checksum, app_dict['app_path']) # SHA1 & SHA256 HASHES all_files = ios_list_files( + checksum, app_dict['app_dir'], - app_dict['md5_hash'], False, 'ios') - infoplist_dict = plist_analysis(app_dict['app_dir'], True) - app_dict['appstore'] = app_search(infoplist_dict.get('id')) + infoplist_dict = plist_analysis( + checksum, + app_dict['app_dir'], + True) + app_dict['appstore'] = app_search( + checksum, + infoplist_dict.get('id')) app_dict['secrets'] = get_plist_secrets( + checksum, app_dict['app_dir']) code_analysis_dic = ios_source_analysis( + checksum, app_dict['app_dir']) ios_strs = strings_and_entropies( + checksum, Path(app_dict['app_dir']), ['.swift', '.m', '.h', '.plist']) if ios_strs['secrets']: @@ -277,10 +302,13 @@ def static_analyzer_ios(request, checksum, api=False): get_icon_source(app_dict) # Firebase DB Check code_analysis_dic['firebase'] = firebase_analysis( + checksum, list(set(code_analysis_dic['urls_list']))) # Extract Trackers from Domains trk = Trackers.Trackers( - None, app_dict['tools_dir']) + checksum, + None, + app_dict['tools_dir']) trackers = trk.get_trackers_domains_or_deps( code_analysis_dic['domains'], []) code_analysis_dic['trackers'] = trackers @@ -310,11 +338,14 @@ def static_analyzer_ios(request, checksum, api=False): else: return render(request, template, context) else: - msg = ('File Type not supported, ' + err = ('File Type not supported, ' 'Only IPA, A, DYLIB and ZIP are supported') - return print_n_send_error_response(request, msg, api) + logger.error(err) + append_scan_status(checksum, err) + raise Exception(err) except Exception as exp: - logger.exception('Error Performing Static Analysis') - msg = str(exp) + msg = 'Error Performing Static Analysis' + logger.exception(msg) + append_scan_status(checksum, msg, repr(exp)) exp_doc = exp.__doc__ - return print_n_send_error_response(request, msg, api, exp_doc) + return print_n_send_error_response(request, repr(exp), api, exp_doc) diff --git a/mobsf/StaticAnalyzer/views/ios/strings.py b/mobsf/StaticAnalyzer/views/ios/strings.py index a744d67d31..dc05c9df6c 100644 --- a/mobsf/StaticAnalyzer/views/ios/strings.py +++ b/mobsf/StaticAnalyzer/views/ios/strings.py @@ -9,18 +9,23 @@ from mobsf.StaticAnalyzer.views.common.shared_func import ( url_n_email_extract, ) +from mobsf.MobSF.utils import ( + append_scan_status, +) logger = logging.getLogger(__name__) -def extract_urls_n_email(src, all_files, strings): +def extract_urls_n_email(checksum, src, all_files, strings): """IPA URL and Email Extraction.""" email_n_file = [] url_n_file = [] url_list = [] try: - logger.info('Starting IPA URL and Email Extraction') + msg = 'Extracting URL and Email from IPA' + logger.info(msg) + append_scan_status(checksum, msg) all_files.append({'data': strings, 'name': 'IPA Strings Dump'}) for file in all_files: if isinstance(file, dict): @@ -47,8 +52,10 @@ def extract_urls_n_email(src, all_files, strings): url_list.extend(urls) url_n_file.extend(urls_nf) email_n_file.extend(emails_nf) - except Exception: - logger.exception('IPA URL and Email Extraction') + except Exception as exp: + msg = 'Failed to extract URL and Email from IPA' + logger.exception(msg) + append_scan_status(checksum, msg, repr(exp)) return { 'urls_list': list(set(url_list)), 'urlnfile': url_n_file, @@ -58,6 +65,9 @@ def extract_urls_n_email(src, all_files, strings): def get_strings_metadata(app_dict, bin_dict, all_files, dy_list): """Merge strings metadata.""" + msg = 'Extracting String Metadata' + logger.info(msg) + append_scan_status(app_dict['md5_hash'], msg) # app_dict has secrets from plist secret analysis # bin_dict has strings from binary analysis urls_list = [] @@ -67,6 +77,7 @@ def get_strings_metadata(app_dict, bin_dict, all_files, dy_list): # IPA URL and Email Extract str_meta = extract_urls_n_email( + app_dict['md5_hash'], app_dict['bin_dir'], all_files['files_long'], bin_dict['strings']) diff --git a/mobsf/StaticAnalyzer/views/ios/view_source.py b/mobsf/StaticAnalyzer/views/ios/views/view_source.py similarity index 100% rename from mobsf/StaticAnalyzer/views/ios/view_source.py rename to mobsf/StaticAnalyzer/views/ios/views/view_source.py diff --git a/mobsf/StaticAnalyzer/views/sast_engine.py b/mobsf/StaticAnalyzer/views/sast_engine.py index 75e288ec81..f4a9bca930 100644 --- a/mobsf/StaticAnalyzer/views/sast_engine.py +++ b/mobsf/StaticAnalyzer/views/sast_engine.py @@ -5,13 +5,14 @@ from libsast import Scanner from mobsf.MobSF.utils import ( + append_scan_status, settings_enabled, ) logger = logging.getLogger(__name__) -def scan(rule, extensions, paths, ignore_paths=None): +def scan(checksum, rule, extensions, paths, ignore_paths=None): """The libsast scan.""" try: options = { @@ -23,17 +24,21 @@ def scan(rule, extensions, paths, ignore_paths=None): res = scanner.scan() if res: return format_findings(res['pattern_matcher'], paths[0]) - except Exception: - logger.exception('libsast scan') + except Exception as exp: + msg = 'libsast scan failed' + logger.exception(msg) + append_scan_status(checksum, msg, repr(exp)) return {} -def niap_scan(rule, extensions, paths, apath, ignore_paths=None): +def niap_scan(checksum, rule, extensions, paths, apath, ignore_paths=None): """NIAP scan.""" if not settings_enabled('NIAP_ENABLED'): return {} try: - logger.info('Running NIAP Analyzer') + msg = 'Running NIAP Analyzer' + logger.info(msg) + append_scan_status(checksum, msg) if not apath: apath = '' options = { @@ -46,8 +51,10 @@ def niap_scan(rule, extensions, paths, apath, ignore_paths=None): res = scanner.scan() if res: return res['choice_matcher'] - except Exception: - logger.exception('NIAP scan') + except Exception as exp: + msg = 'NIAP Analyzer Failed' + logger.exception(msg) + append_scan_status(checksum, msg, repr(exp)) return {} diff --git a/mobsf/StaticAnalyzer/views/windows/db_interaction.py b/mobsf/StaticAnalyzer/views/windows/db_interaction.py index 947c82faea..e2e35afa25 100644 --- a/mobsf/StaticAnalyzer/views/windows/db_interaction.py +++ b/mobsf/StaticAnalyzer/views/windows/db_interaction.py @@ -3,7 +3,11 @@ from django.conf import settings -from mobsf.MobSF.utils import python_list +from mobsf.MobSF.utils import ( + append_scan_status, + get_scan_logs, + python_list, +) from mobsf.StaticAnalyzer.models import StaticAnalyzerWindows from mobsf.StaticAnalyzer.models import RecentScansDB @@ -38,6 +42,7 @@ def get_context_from_db_entry(db_entry): 'strings': python_list(db_entry[0].STRINGS), 'binary_analysis': python_list(db_entry[0].BINARY_ANALYSIS), 'binary_warnings': python_list(db_entry[0].BINARY_WARNINGS), + 'logs': get_scan_logs(db_entry[0].MD5), } return context except Exception: @@ -73,10 +78,13 @@ def get_context_from_analysis(app_dic, 'strings': bin_an_dic['strings'], 'binary_analysis': bin_an_dic['results'], 'binary_warnings': bin_an_dic['warnings'], + 'logs': get_scan_logs(app_dic['md5']), } return context - except Exception: - logger.exception('Rendering to Template') + except Exception as exp: + msg = 'Rendering to Template' + logger.exception(msg) + append_scan_status(app_dic['md5'], msg, repr(exp)) def save_or_update(update_type, @@ -116,8 +124,10 @@ def save_or_update(update_type, else: StaticAnalyzerWindows.objects.filter( MD5=app_dic['md5']).update(**values) - except Exception: - logger.exception('Updating DB') + except Exception as exp: + msg = 'Failed to Save/Update Database' + logger.exception(msg) + append_scan_status(app_dic['md5'], msg, repr(exp)) try: values = { 'APP_NAME': bin_an_dic['bin_name'], @@ -126,5 +136,7 @@ def save_or_update(update_type, } RecentScansDB.objects.filter( MD5=app_dic['md5']).update(**values) - except Exception: - logger.exception('Updating RecentScansDB') + except Exception as exp: + msg = 'Updating RecentScansDB table failed' + logger.exception(msg) + append_scan_status(app_dic['md5'], msg, repr(exp)) diff --git a/mobsf/StaticAnalyzer/views/windows/windows.py b/mobsf/StaticAnalyzer/views/windows/windows.py index 97e11a202e..2821ab18c1 100755 --- a/mobsf/StaticAnalyzer/views/windows/windows.py +++ b/mobsf/StaticAnalyzer/views/windows/windows.py @@ -23,6 +23,7 @@ from django.utils.html import escape from mobsf.MobSF.utils import ( + append_scan_status, file_size, get_config_loc, is_md5, @@ -100,12 +101,12 @@ def staticanalyzer_windows(request, checksum, api=False): app_dic['app_name'] = filename # APP ORIGINAL NAME app_dic['md5'] = checksum app_dic['app_dir'] = os.path.join( - settings.UPLD_DIR, app_dic['md5'] + '/') + settings.UPLD_DIR, checksum + '/') app_dic['tools_dir'] = os.path.join( settings.BASE_DIR, 'StaticAnalyzer/tools/windows/') # DB db_entry = StaticAnalyzerWindows.objects.filter( - MD5=app_dic['md5'], + MD5=checksum, ) if db_entry.exists() and not rescan: logger.info( @@ -118,20 +119,28 @@ def staticanalyzer_windows(request, checksum, api=False): request, 'Permission Denied', False) - logger.info('Windows Binary Analysis Started') + append_scan_status(checksum, 'init') + msg = 'Windows Binary Analysis Started' + logger.info(msg) + append_scan_status(checksum, msg) app_dic['app_path'] = os.path.join( - app_dic['app_dir'], app_dic['md5'] + '.appx') + app_dic['app_dir'], checksum + '.appx') # ANALYSIS BEGINS app_dic['size'] = str( file_size(app_dic['app_path'])) + 'MB' # Generate hashes - app_dic['sha1'], app_dic[ - 'sha256'] = hash_gen(app_dic['app_path']) + app_dic['sha1'], app_dic['sha256'] = hash_gen( + checksum, + app_dic['app_path']) # EXTRACT APPX logger.info('Extracting APPX') app_dic['files'] = unzip( - app_dic['app_path'], app_dic['app_dir']) - xml_dic = _parse_xml(app_dic['app_dir']) + checksum, + app_dic['app_path'], + app_dic['app_dir']) + xml_dic = _parse_xml( + checksum, + app_dic['app_dir']) bin_an_dic = _binary_analysis(app_dic) # Saving to db logger.info('Connecting to DB') @@ -141,7 +150,7 @@ def staticanalyzer_windows(request, checksum, api=False): app_dic, xml_dic, bin_an_dic) - update_scan_timestamp(app_dic['md5']) + update_scan_timestamp(checksum) else: logger.info('Saving to Database') save_or_update('save', @@ -155,19 +164,22 @@ def staticanalyzer_windows(request, checksum, api=False): template = 'static_analysis/windows_binary_analysis.html' context['virus_total'] = None if settings.VT_ENABLED: - vt = VirusTotal.VirusTotal() + vt = VirusTotal.VirusTotal(checksum) context['virus_total'] = vt.get_result( - os.path.join(app_dic['app_dir'], app_dic['md5']) + '.appx', - app_dic['md5']) + os.path.join(app_dic['app_dir'], checksum) + '.appx') if api: return context else: return render(request, template, context) except Exception as exception: - logger.exception('Error Performing Static Analysis') - msg = str(exception) - exp_doc = exception.__doc__ - return print_n_send_error_response(request, msg, api, exp_doc) + msg = 'Error Performing Static Analysis' + logger.exception(msg) + append_scan_status(checksum, msg, repr(exception)) + return print_n_send_error_response( + request, + repr(exception), + api, + exception.__doc__) def _get_token(): @@ -182,7 +194,9 @@ def _get_token(): def _binary_analysis(app_dic): """Start binary analsis.""" - logger.info('Starting Binary Analysis') + msg = 'Starting Binary Analysis' + logger.info(msg) + append_scan_status(app_dic['md5'], msg) bin_an_dic = {} # Init optional sections to prevent None-Pointer-Errors @@ -195,7 +209,7 @@ def _binary_analysis(app_dic): bin_an_dic['bin_name'] = file_name.replace('.exe', '') break if not bin_an_dic.get('bin_name'): - logger.exception('No executable in appx.') + logger.error('No executable in appx') bin_path = os.path.join(app_dic['app_dir'], bin_an_dic['bin']) # Execute strings command @@ -223,19 +237,23 @@ def _binary_analysis(app_dic): # Execute binskim analysis if vm is available if platform.system() != 'Windows' or 'CI' in os.environ: if settings.WINDOWS_VM_IP: - logger.info('Windows VM configured.') + msg = 'Windows VM configured' + logger.info(msg) + append_scan_status(app_dic['md5'], msg) global proxy proxy = xmlrpc.client.ServerProxy( # pylint: disable-msg=C0103 'http://{}:{}'.format( settings.WINDOWS_VM_IP, settings.WINDOWS_VM_PORT)) name = _upload_sample(bin_path) - bin_an_dic = binskim(name, bin_an_dic) - bin_an_dic = binscope(name, bin_an_dic) + bin_an_dic = binskim(app_dic['md5'], name, bin_an_dic) + bin_an_dic = binscope(app_dic['md5'], name, bin_an_dic) else: - logger.warning( - 'Windows VM not configured in %s.' - ' Skipping Binskim and Binscope.', get_config_loc()) + msg = ( + f'Windows VM not configured in {get_config_loc()}.' + ' Skipping Binskim and Binscope analysis') + logger.warning(msg) + append_scan_status(app_dic['md5'], msg) warning = { 'rule_id': 'VM', 'status': 'Info', @@ -245,19 +263,22 @@ def _binary_analysis(app_dic): } bin_an_dic['results'].append(warning) else: - logger.info('Running local analysis.') - + msg = 'Running Analysis on Windows host' + logger.info(msg) + append_scan_status(app_dic['md5'], msg) global config config = configparser.ConfigParser() # Switch to settings defined path if available config.read(expanduser('~') + '\\MobSF\\Config\\config.txt') # Run analysis functions - bin_an_dic = binskim(bin_path, + bin_an_dic = binskim(app_dic['md5'], + bin_path, bin_an_dic, run_local=True, app_dir=app_dic['app_dir']) - bin_an_dic = binscope(bin_path, + bin_an_dic = binscope(app_dic['md5'], + bin_path, bin_an_dic, run_local=True, app_dir=app_dic['app_dir']) @@ -278,9 +299,11 @@ def _upload_sample(bin_path): return name -def binskim(name, bin_an_dic, run_local=False, app_dir=None): +def binskim(checksum, name, bin_an_dic, run_local=False, app_dir=None): """Run the binskim analysis.""" - logger.info('Running binskim.') + msg = 'Running binskim.' + logger.info(msg) + append_scan_status(checksum, msg) if run_local: bin_path = os.path.join(app_dir, bin_an_dic['bin']) @@ -434,11 +457,11 @@ def parse_binskim_old(bin_an_dic, output): return bin_an_dic -def binscope(name, bin_an_dic, run_local=False, app_dir=None): +def binscope(checksum, name, bin_an_dic, run_local=False, app_dir=None): """Run the binskim analysis.""" - logger.info( - 'Running binscope. This might take a' - ' while, depending on the binary size.') + msg = 'Running binscope. This might take a while.' + logger.info(msg) + append_scan_status(checksum, msg) if run_local: global config bin_path = os.path.join(app_dir, bin_an_dic['bin']) @@ -531,9 +554,11 @@ def binscope(name, bin_an_dic, run_local=False, app_dir=None): return bin_an_dic -def _parse_xml(app_dir): +def _parse_xml(checksum, app_dir): """Parse the AppxManifest file to get basic information.""" - logger.info('Starting Binary Analysis - XML') + msg = 'Starting Binary Analysis - XML' + logger.info(msg) + append_scan_status(checksum, msg) xml_file = os.path.join(app_dir, 'AppxManifest.xml') xml_dic = { 'version': '', @@ -551,7 +576,9 @@ def _parse_xml(app_dir): } try: - logger.info('Reading AppxManifest') + msg = 'Reading AppxManifest' + logger.info(msg) + append_scan_status(checksum, msg) config = etree.XMLParser( # pylint: disable-msg=E1101 remove_blank_text=True, resolve_entities=False, @@ -574,8 +601,10 @@ def _parse_xml(app_dir): elif (isinstance(child.tag, str) and child.tag.endswith('}Metadata')): xml_dic = parse_xml_metadata(xml_dic, child) - except Exception: - logger.exception('Reading from AppxManifest.xml') + except Exception as exp: + msg = 'Error Reading AppxManifest' + logger.exception(msg) + append_scan_status(checksum, msg, repr(exp)) return xml_dic diff --git a/mobsf/templates/base/base_layout.html b/mobsf/templates/base/base_layout.html index 8d66f74487..1ac24f0360 100644 --- a/mobsf/templates/base/base_layout.html +++ b/mobsf/templates/base/base_layout.html @@ -58,11 +58,19 @@ + + @@ -71,6 +79,16 @@ diff --git a/mobsf/templates/general/apidocs.html b/mobsf/templates/general/apidocs.html index 93496811c4..7e7a4ecd19 100644 --- a/mobsf/templates/general/apidocs.html +++ b/mobsf/templates/general/apidocs.html @@ -29,6 +29,7 @@

Static Analysis

  1. api/v1/upload - Upload a File
  2. api/v1/scan - Scan a File
  3. +
  4. api/v1/scan_logs - Display Live Scan Logs
  5. api/v1/scans - Display Recent Scans
  6. api/v1/delete_scan - Delete a Scan
  7. api/v1/scorecard - App Scorecard
  8. @@ -107,7 +108,7 @@

    Upload File API

    Method: POST

  9. -

    Header: Authorization:<api_key> Or X-Mobsf-Api-Key:<api_key>

    +

    Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>

  10. Data Params

    @@ -163,14 +164,14 @@

    Upload File API

    Sample Call:

    • -
      curl -F 'file=@/Users/ajin/Desktop/diva-beta.apk' http://localhost:8000/api/v1/upload -H "Authorization:{{ api_key}}"
      +                    
      curl -F 'file=@/Users/ajin/Desktop/diva-beta.apk' http://localhost:8000/api/v1/upload -H "Authorization: {{ api_key}}"
                         

    OR

    • -
      curl -F 'file=@/Users/ajin/Desktop/diva-beta.apk' http://localhost:8000/api/v1/upload -H "X-Mobsf-Api-Key:{{ api_key}}"
      +                    
      curl -F 'file=@/Users/ajin/Desktop/diva-beta.apk' http://localhost:8000/api/v1/upload -H "X-Mobsf-Api-Key: {{ api_key}}"
                         
    @@ -187,7 +188,7 @@

    Scan File API

    Method: POST

  11. -

    Header: Authorization:<api_key> Or X-Mobsf-Api-Key:<api_key>

    +

    Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>

  12. Data Params

    @@ -257,14 +258,100 @@

    Scan File API

    Sample Call:

    • -
      curl -X POST --url http://localhost:8000/api/v1/scan --data "hash=82ab8b2193b3cfb1c737e3a786be363a" -H "Authorization:{{ api_key}}"
      +                    
      curl -X POST --url http://localhost:8000/api/v1/scan --data "hash=82ab8b2193b3cfb1c737e3a786be363a" -H "Authorization: {{ api_key}}"
                         

    OR

    • -
      curl -X POST --url http://localhost:8000/api/v1/scan --data "hash=82ab8b2193b3cfb1c737e3a786be363a" -H "X-Mobsf-Api-Key:{{ api_key}}"
      +                    
      curl -X POST --url http://localhost:8000/api/v1/scan --data "hash=82ab8b2193b3cfb1c737e3a786be363a" -H "X-Mobsf-Api-Key: {{ api_key}}"
      +                  
      +
    • +
    +
  13. + +
    +

    Scan Logs API

    +

    API that provides live and latest scan logs.

    +
      +
    • +

      URL: /api/v1/scan_logs

      +
    • +
    • +

      Method: POST

      +
    • +
    • +

      Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>

      +
    • +
    • +

      Data Params

      +
    • +
    + + + + + + + + + + + + + + + +
    Param NameParam ValueRequired
    hashhash of the scanYes
    +
    +
      +
    • +

      Success Response:

      +
        +
      • + Code: 200
        + Content-Type: application/json; charset=utf-8
        + Content: +
      • +
      +
      { "logs": [ { "timestamp": "2024-08-04 00:23:35", "status": "Generating Hashes",
      +                   "exception": null }, { "timestamp": "2024-08-04 00:23:35", "status": "
      +                  Extracting APK", "exception": null }, { "timestamp": "2024-08-04 00:23:35",
      +                   "status": "Unzipping", "exception": null },
      +                  SNIPPED
      +                  
      +
    • +
    • +

      Error Response:

      +
        +
      • + Code: 500 Internal Server Error or 405 Method Not Allowed or 422 Unprocessable Entity
        + Content-Type: application/json; charset=utf-8
        + Content: {"error": <error message> } +
      • +
      +

      OR

      +
        +
      • + Code: 401 Unauthorized
        + Content-Type: application/json; charset=utf-8
        + Content: {"error": "You are unauthorized to make this request." } +
      • +
      +
    • +
    • +

      Sample Call:

      +
        +
      • +
        curl -X POST --url http://localhost:8000/api/v1/scan_logs --data "hash=82ab8b2193b3cfb1c737e3a786be363a" -H "Authorization: {{ api_key}}"
        +                  
        +
      • +
      +

      OR

      +
        +
      • +
        curl -X POST --url http://localhost:8000/api/v1/scan_logs --data "hash=82ab8b2193b3cfb1c737e3a786be363a" -H "X-Mobsf-Api-Key: {{ api_key}}"
                           
      @@ -281,7 +368,7 @@

      Delete Scan API

      Method: POST

    • -

      Header: Authorization:<api_key> Or X-Mobsf-Api-Key:<api_key>

      +

      Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>

    • Data Params

      @@ -337,14 +424,14 @@

      Delete Scan API

      Sample Call:

      • -
        curl -X POST --url http://localhost:8000/api/v1/delete_scan --data "hash=82ab8b2193b3cfb1c737e3a786be363a" -H "Authorization:{{ api_key}}"
        +                    
        curl -X POST --url http://localhost:8000/api/v1/delete_scan --data "hash=82ab8b2193b3cfb1c737e3a786be363a" -H "Authorization: {{ api_key}}"
                           

      OR

      • -
        curl -X POST --url http://localhost:8000/api/v1/delete_scan --data "hash=82ab8b2193b3cfb1c737e3a786be363a" -H "X-Mobsf-Api-Key:{{ api_key}}"
        +                    
        curl -X POST --url http://localhost:8000/api/v1/delete_scan --data "hash=82ab8b2193b3cfb1c737e3a786be363a" -H "X-Mobsf-Api-Key: {{ api_key}}"
                           
      @@ -362,7 +449,7 @@

      App Scorecrd API

      Method: POST

    • -

      Header: Authorization:<api_key> Or X-Mobsf-Api-Key:<api_key>

      +

      Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>

    • Data Params

      @@ -418,14 +505,14 @@

      App Scorecrd API

      Sample Call:

      • -
        curl -X POST --url http://localhost:8000/api/v1/scorecard --data "hash=82ab8b2193b3cfb1c737e3a786be363a" -H "Authorization:{{ api_key}}"
        +                    
        curl -X POST --url http://localhost:8000/api/v1/scorecard --data "hash=82ab8b2193b3cfb1c737e3a786be363a" -H "Authorization: {{ api_key}}"
                           

      OR

      • -
        curl -X POST --url http://localhost:8000/api/v1/scorecard --data "hash=82ab8b2193b3cfb1c737e3a786be363a" -H "X-Mobsf-Api-Key:{{ api_key}}"
        +                    
        curl -X POST --url http://localhost:8000/api/v1/scorecard --data "hash=82ab8b2193b3cfb1c737e3a786be363a" -H "X-Mobsf-Api-Key: {{ api_key}}"
                           
      @@ -444,7 +531,7 @@

      Generate PDF Report API

      Method: POST

    • -

      Header: Authorization:<api_key> Or X-Mobsf-Api-Key:<api_key>

      +

      Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>

    • Data Params

      @@ -500,14 +587,14 @@

      Generate PDF Report API

      Sample Call:

      • -
        curl -X POST --url http://localhost:8000/api/v1/download_pdf --data "hash=82ab8b2193b3cfb1c737e3a786be363a" -H "Authorization:{{ api_key}}"
        +                    
        curl -X POST --url http://localhost:8000/api/v1/download_pdf --data "hash=82ab8b2193b3cfb1c737e3a786be363a" -H "Authorization: {{ api_key}}"
                           

      OR

      • -
        curl -X POST --url http://localhost:8000/api/v1/download_pdf --data "hash=82ab8b2193b3cfb1c737e3a786be363a" -H "X-Mobsf-Api-Key:{{ api_key}}"
        +                    
        curl -X POST --url http://localhost:8000/api/v1/download_pdf --data "hash=82ab8b2193b3cfb1c737e3a786be363a" -H "X-Mobsf-Api-Key: {{ api_key}}"
                           
      @@ -525,7 +612,7 @@

      Generate JSON Report APIMethod: POST

    • -

      Header: Authorization:<api_key> Or X-Mobsf-Api-Key:<api_key>

      +

      Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>

    • Data Params

      @@ -581,14 +668,14 @@

      Generate JSON Report APISample Call:

      • -
        curl -X POST --url http://localhost:8000/api/v1/report_json --data "hash=82ab8b2193b3cfb1c737e3a786be363a" -H "Authorization:{{ api_key}}"
        +                    
        curl -X POST --url http://localhost:8000/api/v1/report_json --data "hash=82ab8b2193b3cfb1c737e3a786be363a" -H "Authorization: {{ api_key}}"
                           

      OR

      • -
        curl -X POST --url http://localhost:8000/api/v1/report_json --data "hash=82ab8b2193b3cfb1c737e3a786be363a" -H "X-Mobsf-Api-Key:{{ api_key}}"
        +                    
        curl -X POST --url http://localhost:8000/api/v1/report_json --data "hash=82ab8b2193b3cfb1c737e3a786be363a" -H "X-Mobsf-Api-Key: {{ api_key}}"
                           
      @@ -606,7 +693,7 @@

      View Source Files API

      Method: POST

    • -

      Header: Authorization:<api_key> Or X-Mobsf-Api-Key:<api_key>

      +

      Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>

    • Data Params

      @@ -672,22 +759,22 @@

      View Source Files API

      Sample Call:

      • -
        curl -X POST --url http://localhost:8000/api/v1/view_source --data "hash=18e244926da1e49c5b8ffc1c30de8abc&type=apk&file=b/a/a/a/a/a.java" -H "Authorization:{{ api_key}}"
        +                    
        curl -X POST --url http://localhost:8000/api/v1/view_source --data "hash=18e244926da1e49c5b8ffc1c30de8abc&type=apk&file=b/a/a/a/a/a.java" -H "Authorization: {{ api_key}}"
                           
      • -
        curl -X POST --url http://localhost:8000/api/v1/view_source --data "hash=6c23c2970551be15f32bbab0b5db0c71&type=ipa&file=classdump.txt" -H "Authorization:{{ api_key}}"
        +                    
        curl -X POST --url http://localhost:8000/api/v1/view_source --data "hash=6c23c2970551be15f32bbab0b5db0c71&type=ipa&file=classdump.txt" -H "Authorization: {{ api_key}}"
                           

      OR

      • -
        curl -X POST --url http://localhost:8000/api/v1/view_source --data "hash=18e244926da1e49c5b8ffc1c30de8abc&type=apk&file=b/a/a/a/a/a.java" -H "X-Mobsf-Api-Key:{{ api_key}}"
        +                    
        curl -X POST --url http://localhost:8000/api/v1/view_source --data "hash=18e244926da1e49c5b8ffc1c30de8abc&type=apk&file=b/a/a/a/a/a.java" -H "X-Mobsf-Api-Key: {{ api_key}}"
                           
      • -
        curl -X POST --url http://localhost:8000/api/v1/view_source --data "hash=6c23c2970551be15f32bbab0b5db0c71&type=ipa&file=classdump.txt" -H "X-Mobsf-Api-Key:{{ api_key}}"
        +                    
        curl -X POST --url http://localhost:8000/api/v1/view_source --data "hash=6c23c2970551be15f32bbab0b5db0c71&type=ipa&file=classdump.txt" -H "X-Mobsf-Api-Key: {{ api_key}}"
                           
      @@ -705,7 +792,7 @@

      Display Recent Scans API

      Method: GET

    • -

      Header: Authorization:<api_key> Or X-Mobsf-Api-Key:<api_key>

      +

      Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>

    • Data Params

      @@ -769,22 +856,22 @@

      Display Recent Scans API

      Sample Call:

      • -
        curl --url "http://localhost:8000/api/v1/scans" -H "Authorization:{{ api_key}}"
        +                    
        curl --url "http://localhost:8000/api/v1/scans" -H "Authorization:  {{ api_key}}"
                           
      • -
        curl --url "http://localhost:8000/api/v1/scans?page=1&page_size=10" -H "Authorization:{{ api_key}}"
        +                    
        curl --url "http://localhost:8000/api/v1/scans?page=1&page_size=10" -H "Authorization:  {{ api_key}}"
                           

      OR

      • -
        curl --url "http://localhost:8000/api/v1/scans" -H "X-Mobsf-Api-Key:{{ api_key}}"
        +                    
        curl --url "http://localhost:8000/api/v1/scans" -H "X-Mobsf-Api-Key: {{ api_key}}"
                           
      • -
        curl --url "http://localhost:8000/api/v1/scans?page=1&page_size=10" -H "X-Mobsf-Api-Key:{{ api_key}}"
        +                    
        curl --url "http://localhost:8000/api/v1/scans?page=1&page_size=10" -H "X-Mobsf-Api-Key: {{ api_key}}"
                           
      @@ -802,7 +889,7 @@

      Compare Apps API

      Method: POST

    • -

      Header: Authorization:<api_key> Or X-Mobsf-Api-Key:<api_key>

      +

      Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>

    • Data Params

      @@ -863,14 +950,14 @@

      Compare Apps API

      Sample Call:

      • -
        curl -X POST --url http://localhost:8000/api/v1/compare --data "hash1=82ab8b2193b3cfb1c737e3a786be363a&hash2=f56c96f2b1f0a7c46eb6fef3a035f3dd" -H "Authorization:{{ api_key}}"
        +                    
        curl -X POST --url http://localhost:8000/api/v1/compare --data "hash1=82ab8b2193b3cfb1c737e3a786be363a&hash2=f56c96f2b1f0a7c46eb6fef3a035f3dd" -H "Authorization:  {{ api_key}}"
                           

      OR

      • -
        curl -X POST --url http://localhost:8000/api/v1/compare --data "hash1=82ab8b2193b3cfb1c737e3a786be363a&hash2=f56c96f2b1f0a7c46eb6fef3a035f3dd" -H "X-Mobsf-Api-Key:{{ api_key}}"
        +                    
        curl -X POST --url http://localhost:8000/api/v1/compare --data "hash1=82ab8b2193b3cfb1c737e3a786be363a&hash2=f56c96f2b1f0a7c46eb6fef3a035f3dd" -H "X-Mobsf-Api-Key: {{ api_key}}"
                           
      @@ -888,7 +975,7 @@

      Suppress by Rule

      Method: POST

    • -

      Header: Authorization:<api_key> Or X-Mobsf-Api-Key:<api_key>

      +

      Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>

    • Data Params

      @@ -954,14 +1041,14 @@

      Suppress by Rule

      Sample Call:

      • -
        curl -X POST --url http://localhost:8000/api/v1/suppress_by_rule --data "hash=82ab8b2193b3cfb1c737e3a786be363a&type=manifest&rule=app_allowbackup" -H "Authorization:{{ api_key}}"
        +                    
        curl -X POST --url http://localhost:8000/api/v1/suppress_by_rule --data "hash=82ab8b2193b3cfb1c737e3a786be363a&type=manifest&rule=app_allowbackup" -H "Authorization:  {{ api_key}}"
                           

      OR

      • -
        curl -X POST --url http://localhost:8000/api/v1/suppress_by_rule --data "hash=82ab8b2193b3cfb1c737e3a786be363a&type=code&rule=android_logging" -H "X-Mobsf-Api-Key:{{ api_key}}"
        +                    
        curl -X POST --url http://localhost:8000/api/v1/suppress_by_rule --data "hash=82ab8b2193b3cfb1c737e3a786be363a&type=code&rule=android_logging" -H "X-Mobsf-Api-Key: {{ api_key}}"
                           
      @@ -979,7 +1066,7 @@

      Suppress by Files

      Method: POST

    • -

      Header: Authorization:<api_key> Or X-Mobsf-Api-Key:<api_key>

      +

      Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>

    • Data Params

      @@ -1045,14 +1132,14 @@

      Suppress by Files

      Sample Call:

      • -
        curl -X POST --url http://localhost:8000/api/v1/suppress_by_files --data "hash=82ab8b2193b3cfb1c737e3a786be363a&type=code&rule=app_allowbackup" -H "Authorization:{{ api_key}}"
        +                    
        curl -X POST --url http://localhost:8000/api/v1/suppress_by_files --data "hash=82ab8b2193b3cfb1c737e3a786be363a&type=code&rule=app_allowbackup" -H "Authorization:  {{ api_key}}"
                           

      OR

      • -
        curl -X POST --url http://localhost:8000/api/v1/suppress_by_files --data "hash=82ab8b2193b3cfb1c737e3a786be363a&type=code&rule=android_logging" -H "X-Mobsf-Api-Key:{{ api_key}}"
        +                    
        curl -X POST --url http://localhost:8000/api/v1/suppress_by_files --data "hash=82ab8b2193b3cfb1c737e3a786be363a&type=code&rule=android_logging" -H "X-Mobsf-Api-Key: {{ api_key}}"
                           
      @@ -1070,7 +1157,7 @@

      View Suppressions

      Method: POST

    • -

      Header: Authorization:<api_key> Or X-Mobsf-Api-Key:<api_key>

      +

      Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>

    • Data Params

      @@ -1126,14 +1213,14 @@

      View Suppressions

      Sample Call:

      • -
        curl -X POST --url http://localhost:8000/api/v1/list_suppressions --data "hash=82ab8b2193b3cfb1c737e3a786be363a" -H "Authorization:{{ api_key}}"
        +                    
        curl -X POST --url http://localhost:8000/api/v1/list_suppressions --data "hash=82ab8b2193b3cfb1c737e3a786be363a" -H "Authorization:  {{ api_key}}"
                           

      OR

      • -
        curl -X POST --url http://localhost:8000/api/v1/list_suppressions --data "hash=82ab8b2193b3cfb1c737e3a786be363a" -H "X-Mobsf-Api-Key:{{ api_key}}"
        +                    
        curl -X POST --url http://localhost:8000/api/v1/list_suppressions --data "hash=82ab8b2193b3cfb1c737e3a786be363a" -H "X-Mobsf-Api-Key: {{ api_key}}"
                           
      @@ -1151,7 +1238,7 @@

      Delete Suppressions

      Method: POST

    • -

      Header: Authorization:<api_key> Or X-Mobsf-Api-Key:<api_key>

      +

      Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>

    • Data Params

      @@ -1222,14 +1309,14 @@

      Delete Suppressions

      Sample Call:

      • -
        curl -X POST --url http://localhost:8000/api/v1/delete_suppression --data "hash=82ab8b2193b3cfb1c737e3a786be363a&kind=file&type=code&rule=android_sql_raw_query" -H "Authorization:{{ api_key}}"
        +                    
        curl -X POST --url http://localhost:8000/api/v1/delete_suppression --data "hash=82ab8b2193b3cfb1c737e3a786be363a&kind=file&type=code&rule=android_sql_raw_query" -H "Authorization:  {{ api_key}}"
                           

      OR

      • -
        curl -X POST --url http://localhost:8000/api/v1/delete_suppression --data "hash=82ab8b2193b3cfb1c737e3a786be363a&kind=rule&type=manifest&rule=receiver_exported_intent_filter_exists" -H "X-Mobsf-Api-Key:{{ api_key}}"
        +                    
        curl -X POST --url http://localhost:8000/api/v1/delete_suppression --data "hash=82ab8b2193b3cfb1c737e3a786be363a&kind=rule&type=manifest&rule=receiver_exported_intent_filter_exists" -H "X-Mobsf-Api-Key: {{ api_key}}"
                           
      @@ -1247,7 +1334,7 @@

      Get Apps API

      Method: GET

    • -

      Header: Authorization:<api_key> Or X-Mobsf-Api-Key:<api_key>

      +

      Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>


    @@ -1306,7 +1393,7 @@

    Get Apps API

    Sample Call:

    • -
      curl --url http://localhost:8000/api/v1/dynamic/get_apps -H "Authorization:{{ api_key}}"
      +                    
      curl --url http://localhost:8000/api/v1/dynamic/get_apps -H "Authorization:  {{ api_key}}"
                         
    @@ -1324,7 +1411,7 @@

    Start Dynamic Analysis APIMethod: POST

  14. -

    Header: Authorization:<api_key> Or X-Mobsf-Api-Key:<api_key>

    +

    Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>

  15. Data Params

    @@ -1398,7 +1485,7 @@

    Start Dynamic Analysis APISample Call:

    • -
      curl -X POST --url http://localhost:8000/api/v1/dynamic/start_analysis --data "hash=3a552566097a8de588b8184b059b0158" -H "Authorization:{{ api_key}}"
      +                    
      curl -X POST --url http://localhost:8000/api/v1/dynamic/start_analysis --data "hash=3a552566097a8de588b8184b059b0158" -H "Authorization:  {{ api_key}}"
                         
    @@ -1416,7 +1503,7 @@

    View Logcat API

    Method: POST

  16. -

    Header: Authorization:<api_key> Or X-Mobsf-Api-Key:<api_key>

    +

    Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>

  17. Data Params

    @@ -1472,7 +1559,7 @@

    View Logcat API

    Sample Call:

    • -
      curl -X POST --url http://localhost:8000/api/v1/android/logcat --data "package=org.wikipedia" -H "Authorization:{{ api_key}}"
      +                    
      curl -X POST --url http://localhost:8000/api/v1/android/logcat --data "package=org.wikipedia" -H "Authorization:  {{ api_key}}"
                         
    @@ -1490,7 +1577,7 @@

    MobSFy API

    Method: POST

  18. -

    Header: Authorization:<api_key> Or X-Mobsf-Api-Key:<api_key>

    +

    Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>

  19. Data Params

    @@ -1549,7 +1636,7 @@

    MobSFy API

    Sample Call:

    • -
      curl -X POST --url http://localhost:8000/api/v1/android/mobsfy --data "identifier=192.168.56.139:5555" -H "Authorization:{{ api_key}}"
      +                    
      curl -X POST --url http://localhost:8000/api/v1/android/mobsfy --data "identifier=192.168.56.139:5555" -H "Authorization: {{ api_key}}"
                         
    @@ -1567,7 +1654,7 @@

    Execute ADB Commands API

    Method: POST

  20. -

    Header: Authorization:<api_key> Or X-Mobsf-Api-Key:<api_key>

    +

    Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>

  21. Data Params

    @@ -1626,7 +1713,7 @@

    Execute ADB Commands API

    Sample Call:

    • -
      curl -X POST --url http://localhost:8000/api/v1/android/adb_command --data "cmd=shell ls" -H "Authorization:{{ api_key}}"
      +                    
      curl -X POST --url http://localhost:8000/api/v1/android/adb_command --data "cmd=shell ls" -H "Authorization: {{ api_key}}"
                         
    @@ -1644,7 +1731,7 @@

    Install or Remove Root CA API

    Method: POST

  22. -

    Header: Authorization:<api_key> Or X-Mobsf-Api-Key:<api_key>

    +

    Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>

  23. Data Params

    @@ -1703,7 +1790,7 @@

    Install or Remove Root CA API

    Sample Call:

    • -
      curl -X POST --url http://localhost:8000/api/v1/android/root_ca --data "action=install" -H "Authorization:{{ api_key}}"
      +                    
      curl -X POST --url http://localhost:8000/api/v1/android/root_ca --data "action=install" -H "Authorization: {{ api_key}}"
                         
    @@ -1720,7 +1807,7 @@

    Set or Unset MobSF Global HTTP(S) Proxy

    Method: POST

  24. -

    Header: Authorization:<api_key> Or X-Mobsf-Api-Key:<api_key>

    +

    Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>

  25. Data Params

    @@ -1779,7 +1866,7 @@

    Set or Unset MobSF Global HTTP(S) Proxy

    Sample Call:

    • -
      curl -X POST --url http://localhost:8000/api/v1/android/global_proxy --data "action=set" -H "Authorization:{{ api_key}}"
      +                    
      curl -X POST --url http://localhost:8000/api/v1/android/global_proxy --data "action=set" -H "Authorization: {{ api_key}}"
                         
    @@ -1797,7 +1884,7 @@

    Activity or Exported Activity Tester APIMethod: POST

  26. -

    Header: Authorization:<api_key> Or X-Mobsf-Api-Key:<api_key>

    +

    Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>

  27. Data Params

    @@ -1860,7 +1947,7 @@

    Activity or Exported Activity Tester APISample Call:

    • -
      curl -X POST --url http://localhost:8000/api/v1/android/activity --data "hash=6825bb9fde2fc671322df005976755a1&test=exported" -H "Authorization:{{ api_key}}"
      +                    
      curl -X POST --url http://localhost:8000/api/v1/android/activity --data "hash=6825bb9fde2fc671322df005976755a1&test=exported" -H "Authorization: {{ api_key}}"
                         
    @@ -1879,7 +1966,7 @@

    Start Activity or Exported Activity A

    Method: POST

  28. -

    Header: Authorization:<api_key> Or X-Mobsf-Api-Key:<api_key>

    +

    Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>

  29. Data Params

    @@ -1942,7 +2029,7 @@

    Start Activity or Exported Activity A

    Sample Call:

    • -
      curl -X POST --url http://localhost:8000/api/v1/android/start_activity --data "hash=6825bb9fde2fc671322df005976755a1&activity=com.package.android.MainActivity" -H "Authorization:{{ api_key}}"
      +                    
      curl -X POST --url http://localhost:8000/api/v1/android/start_activity --data "hash=6825bb9fde2fc671322df005976755a1&activity=com.package.android.MainActivity" -H "Authorization: {{ api_key}}"
                         
    @@ -1960,7 +2047,7 @@

    TLS/SSL Security Tester API

    Method: POST

  30. -

    Header: Authorization:<api_key> Or X-Mobsf-Api-Key:<api_key>

    +

    Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>

  31. Data Params

    @@ -2016,7 +2103,7 @@

    TLS/SSL Security Tester API

    Sample Call:

    • -
      curl -X POST --url http://localhost:8000/api/v1/android/tls_tests --data "hash=6825bb9fde2fc671322df005976755a1" -H "Authorization:{{ api_key}}"
      +                    
      curl -X POST --url http://localhost:8000/api/v1/android/tls_tests --data "hash=6825bb9fde2fc671322df005976755a1" -H "Authorization: {{ api_key}}"
                         
    @@ -2034,7 +2121,7 @@

    Frida Instrument App APIMethod: POST

  32. -

    Header: Authorization:<api_key> Or X-Mobsf-Api-Key:<api_key>

    +

    Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>

  33. Data Params

    @@ -2137,14 +2224,14 @@

    Frida Instrument App APISample Call:

    • -
      curl -X POST --url http://localhost:8000/api/v1/frida/instrument --data "hash=6825bb9fde2fc671322df005976755a1&default_hooks=api_monitor,ssl_pinning_bypass,root_bypass,debugger_check_bypass&auxiliary_hooks=&frida_code=" -H "Authorization:{{ api_key}}"
      +                    
      curl -X POST --url http://localhost:8000/api/v1/frida/instrument --data "hash=6825bb9fde2fc671322df005976755a1&default_hooks=api_monitor,ssl_pinning_bypass,root_bypass,debugger_check_bypass&auxiliary_hooks=&frida_code=" -H "Authorization: {{ api_key}}"
                         

    OR

    • -
      curl -X POST --url http://localhost:8000/api/v1/frida/instrument --data "hash=6825bb9fde2fc671322df005976755a1&default_hooks=api_monitor,ssl_pinning_bypass,root_bypass,debugger_check_bypass&auxiliary_hooks=enum_class,string_catch,string_compare,enum_methods,search_class,trace_class&class_name=java.io.File&class_search=ssl&class_trace=javax.net.ssl.TrustManager&frida_code=Java.perform(function()+%7B%0A++%2F%2F+Use+send()+for+logging%0A%7D)%3B" -H "Authorization:{{ api_key}}"
      +                    
      curl -X POST --url http://localhost:8000/api/v1/frida/instrument --data "hash=6825bb9fde2fc671322df005976755a1&default_hooks=api_monitor,ssl_pinning_bypass,root_bypass,debugger_check_bypass&auxiliary_hooks=enum_class,string_catch,string_compare,enum_methods,search_class,trace_class&class_name=java.io.File&class_search=ssl&class_trace=javax.net.ssl.TrustManager&frida_code=Java.perform(function()+%7B%0A++%2F%2F+Use+send()+for+logging%0A%7D)%3B" -H "Authorization: {{ api_key}}"
                         
    @@ -2162,7 +2249,7 @@

    Frida API Monitor API

    Method: POST

  34. -

    Header: Authorization:<api_key> Or X-Mobsf-Api-Key:<api_key>

    +

    Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>

  35. Data Params

    @@ -2218,7 +2305,7 @@

    Frida API Monitor API

    Sample Call:

    • -
      curl -X POST --url http://localhost:8000/api/v1/frida/api_monitor --data "hash=6825bb9fde2fc671322df005976755a1" -H "Authorization:{{ api_key}}"
      +                    
      curl -X POST --url http://localhost:8000/api/v1/frida/api_monitor --data "hash=6825bb9fde2fc671322df005976755a1" -H "Authorization: {{ api_key}}"
                         
    @@ -2236,7 +2323,7 @@

    Frida Get Runtime Dependencie

    Method: POST

  36. -

    Header: Authorization:<api_key> Or X-Mobsf-Api-Key:<api_key>

    +

    Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>

  37. Data Params

    @@ -2292,7 +2379,7 @@

    Frida Get Runtime Dependencie

    Sample Call:

    • -
      curl -X POST --url http://localhost:8000/api/v1/frida/get_dependencies --data "hash=6825bb9fde2fc671322df005976755a1" -H "Authorization:{{ api_key}}"
      +                    
      curl -X POST --url http://localhost:8000/api/v1/frida/get_dependencies --data "hash=6825bb9fde2fc671322df005976755a1" -H "Authorization: {{ api_key}}"
                         
    @@ -2310,7 +2397,7 @@

    Frida View Logs API

    Method: POST

  38. -

    Header: Authorization:<api_key> Or X-Mobsf-Api-Key:<api_key>

    +

    Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>

  39. Data Params

    @@ -2366,7 +2453,7 @@

    Frida View Logs API

    Sample Call:

    • -
      curl -X POST --url http://localhost:8000/api/v1/frida/logs --data "hash=6825bb9fde2fc671322df005976755a1" -H "Authorization:{{ api_key}}"
      +                    
      curl -X POST --url http://localhost:8000/api/v1/frida/logs --data "hash=6825bb9fde2fc671322df005976755a1" -H "Authorization: {{ api_key}}"
                         
    @@ -2384,7 +2471,7 @@

    Frida List Scripts APIMethod: POST

  40. -

    Header: Authorization:<api_key> Or X-Mobsf-Api-Key:<api_key>

    +

    Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>

  41. Data Params

    @@ -2455,7 +2542,7 @@

    Frida List Scripts APISample Call:

    • -
      curl -X POST --url http://localhost:8000/api/v1/frida/list_scripts --data "device=android" -H "Authorization:{{ api_key}}"
      +                    
      curl -X POST --url http://localhost:8000/api/v1/frida/list_scripts --data "device=android" -H "Authorization: {{ api_key}}"
                         
    @@ -2474,7 +2561,7 @@

    Frida Get Script API

    Method: POST

  42. -

    Header: Authorization:<api_key> Or X-Mobsf-Api-Key:<api_key>

    +

    Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>

  43. Data Params

    @@ -2535,7 +2622,7 @@

    Frida Get Script API

    Sample Call:

    • -
      curl -X POST --url http://localhost:8000/api/v1/frida/get_script --data "device=android&scripts[]=hook_java_reflection&scripts[]=jni_hook_by_address&scripts[]=default&scripts[]=get_android_id" -H "Authorization:{{ api_key}}"
      +                    
      curl -X POST --url http://localhost:8000/api/v1/frida/get_script --data "device=android&scripts[]=hook_java_reflection&scripts[]=jni_hook_by_address&scripts[]=default&scripts[]=get_android_id" -H "Authorization: {{ api_key}}"
                         
    @@ -2553,7 +2640,7 @@

    Stop Dynamic Analysis API

    Method: POST

  44. -

    Header: Authorization:<api_key> Or X-Mobsf-Api-Key:<api_key>

    +

    Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>

  45. Data Params

    @@ -2611,7 +2698,7 @@

    Stop Dynamic Analysis API

    Sample Call:

    • -
      curl -X POST --url http://localhost:8000/api/v1/dynamic/stop_analysis --data "hash=6825bb9fde2fc671322df005976755a1" -H "Authorization:{{ api_key}}"
      +                    
      curl -X POST --url http://localhost:8000/api/v1/dynamic/stop_analysis --data "hash=6825bb9fde2fc671322df005976755a1" -H "Authorization: {{ api_key}}"
                         
    @@ -2629,7 +2716,7 @@

    Dynamic Analysis JSON Report API

    Method: POST

  46. -

    Header: Authorization:<api_key> Or X-Mobsf-Api-Key:<api_key>

    +

    Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>

  47. Data Params

    @@ -2685,7 +2772,7 @@

    Dynamic Analysis JSON Report API

    Sample Call:

    • -
      curl -X POST --url http://localhost:8000/api/v1/dynamic/report_json --data "hash=6825bb9fde2fc671322df005976755a1" -H "Authorization:{{ api_key}}"
      +                    
      curl -X POST --url http://localhost:8000/api/v1/dynamic/report_json --data "hash=6825bb9fde2fc671322df005976755a1" -H "Authorization: {{ api_key}}"
                         
    @@ -2704,7 +2791,7 @@

    Dynamic Analysis View Source API

    Method: POST

  48. -

    Header: Authorization:<api_key> Or X-Mobsf-Api-Key:<api_key>

    +

    Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>

  49. Data Params

    @@ -2770,7 +2857,7 @@

    Dynamic Analysis View Source API

    Sample Call:

    • -
      curl -X POST --url http://localhost:8000/api/v1/dynamic/view_source --data "file=data/data/org.wikipedia/shared_prefs/org.wikipedia_preferences.xml&hash=6825bb9fde2fc671322df005976755a1&type=xml" -H "Authorization:{{ api_key}}"
      +                    
      curl -X POST --url http://localhost:8000/api/v1/dynamic/view_source --data "file=data/data/org.wikipedia/shared_prefs/org.wikipedia_preferences.xml&hash=6825bb9fde2fc671322df005976755a1&type=xml" -H "Authorization: {{ api_key}}"
                         
    @@ -2789,7 +2876,7 @@

    Supported Corellium iOS Models API<

    Method: POST

  50. -

    Header: Authorization:<api_key> Or X-Mobsf-Api-Key:<api_key>

    +

    Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>


  51. @@ -2826,7 +2913,7 @@

    Supported Corellium iOS Models API<

    Sample Call:

    • -
      curl -X POST --url http://localhost:8000/api/v1/ios/corellium_supported_models -H "Authorization:{{ api_key}}"
      +                    
      curl -X POST --url http://localhost:8000/api/v1/ios/corellium_supported_models -H "Authorization: {{ api_key}}"
                         
    @@ -2845,7 +2932,7 @@

    Supported Corellium iOS Versi

    Method: POST

  52. -

    Header: Authorization:<api_key> Or X-Mobsf-Api-Key:<api_key>

    +

    Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>

  53. Data Params

    @@ -2901,7 +2988,7 @@

    Supported Corellium iOS Versi

    Sample Call:

    • -
      curl -X POST --url http://localhost:8000/api/v1/ios/corellium_ios_versions --data "model=iPhone15,3" -H "Authorization:{{ api_key}}"
      +                    
      curl -X POST --url http://localhost:8000/api/v1/ios/corellium_ios_versions --data "model=iPhone15,3" -H "Authorization: {{ api_key}}"
                         
    @@ -2921,7 +3008,7 @@

    Corellium Create iOS Instance AP

    Method: POST

  54. -

    Header: Authorization:<api_key> Or X-Mobsf-Api-Key:<api_key>

    +

    Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>

  55. Data Params

    @@ -2992,7 +3079,7 @@

    Corellium Create iOS Instance AP

    Sample Call:

    • -
      curl -X POST --url http://localhost:8000/api/v1/ios/corellium_create_ios_instance --data "project_id=728bb423-68bc-4300-a484-6e32a43be9cf&name=iosvm&flavor=iphone15p&version=16.0" -H "Authorization:{{ api_key}}"
      +                    
      curl -X POST --url http://localhost:8000/api/v1/ios/corellium_create_ios_instance --data "project_id=728bb423-68bc-4300-a484-6e32a43be9cf&name=iosvm&flavor=iphone15p&version=16.0" -H "Authorization: {{ api_key}}"
                         
    @@ -3012,7 +3099,7 @@

    iOS Dynamic Analysis APIMethod: POST

  56. -

    Header: Authorization:<api_key> Or X-Mobsf-Api-Key:<api_key>

    +

    Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>

  57. Data Params

    @@ -3052,7 +3139,7 @@

    iOS Dynamic Analysis APISample Call:

    • -
      curl -X POST --url http://localhost:8000/api/v1/ios/dynamic_analysis -H "Authorization:{{ api_key}}"
      +                    
      curl -X POST --url http://localhost:8000/api/v1/ios/dynamic_analysis -H "Authorization: {{ api_key}}"
                         
    @@ -3070,7 +3157,7 @@

    Corellium Start iOS Instance API<

    Method: POST

  58. -

    Header: Authorization:<api_key> Or X-Mobsf-Api-Key:<api_key>

    +

    Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>

  59. Data Params

    @@ -3126,7 +3213,7 @@

    Corellium Start iOS Instance API<

    Sample Call:

    • -
      curl -X POST --url http://localhost:8000/api/v1/ios/corellium_start_instance --data "instance_id=ce9cf65d-5ce5-4fad-823f-5d784c802d21" -H "Authorization:{{ api_key}}"
      +                    
      curl -X POST --url http://localhost:8000/api/v1/ios/corellium_start_instance --data "instance_id=ce9cf65d-5ce5-4fad-823f-5d784c802d21" -H "Authorization: {{ api_key}}"
                         
    @@ -3144,7 +3231,7 @@

    Corellium Stop iOS Instance APIMethod: POST

  60. -

    Header: Authorization:<api_key> Or X-Mobsf-Api-Key:<api_key>

    +

    Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>

  61. Data Params

    @@ -3200,7 +3287,7 @@

    Corellium Stop iOS Instance APISample Call:

    • -
      curl -X POST --url http://localhost:8000/api/v1/ios/corellium_stop_instance --data "instance_id=ce9cf65d-5ce5-4fad-823f-5d784c802d21" -H "Authorization:{{ api_key}}"
      +                    
      curl -X POST --url http://localhost:8000/api/v1/ios/corellium_stop_instance --data "instance_id=ce9cf65d-5ce5-4fad-823f-5d784c802d21" -H "Authorization: {{ api_key}}"
                         
    @@ -3218,7 +3305,7 @@

    Corellium Unpause iOS Instance

    Method: POST

  62. -

    Header: Authorization:<api_key> Or X-Mobsf-Api-Key:<api_key>

    +

    Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>

  63. Data Params

    @@ -3274,7 +3361,7 @@

    Corellium Unpause iOS Instance

    Sample Call:

    • -
      curl -X POST --url http://localhost:8000/api/v1/ios/corellium_unpause_instance --data "instance_id=ce9cf65d-5ce5-4fad-823f-5d784c802d21" -H "Authorization:{{ api_key}}"
      +                    
      curl -X POST --url http://localhost:8000/api/v1/ios/corellium_unpause_instance --data "instance_id=ce9cf65d-5ce5-4fad-823f-5d784c802d21" -H "Authorization: {{ api_key}}"
                         
    @@ -3292,7 +3379,7 @@

    Corellium Reboot iOS Instance AP

    Method: POST

  64. -

    Header: Authorization:<api_key> Or X-Mobsf-Api-Key:<api_key>

    +

    Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>

  65. Data Params

    @@ -3348,7 +3435,7 @@

    Corellium Reboot iOS Instance AP

    Sample Call:

    • -
      curl -X POST --url http://localhost:8000/api/v1/ios/corellium_reboot_instance --data "instance_id=ce9cf65d-5ce5-4fad-823f-5d784c802d21" -H "Authorization:{{ api_key}}"
      +                    
      curl -X POST --url http://localhost:8000/api/v1/ios/corellium_reboot_instance --data "instance_id=ce9cf65d-5ce5-4fad-823f-5d784c802d21" -H "Authorization: {{ api_key}}"
                         
    @@ -3367,7 +3454,7 @@

    Corellium Destroy iOS Instance

    Method: POST

  66. -

    Header: Authorization:<api_key> Or X-Mobsf-Api-Key:<api_key>

    +

    Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>

  67. Data Params

    @@ -3423,7 +3510,7 @@

    Corellium Destroy iOS Instance

    Sample Call:

    • -
      curl -X POST --url http://localhost:8000/api/v1/ios/corellium_destroy_instance --data "instance_id=ce9cf65d-5ce5-4fad-823f-5d784c802d21" -H "Authorization:{{ api_key}}"
      +                    
      curl -X POST --url http://localhost:8000/api/v1/ios/corellium_destroy_instance --data "instance_id=ce9cf65d-5ce5-4fad-823f-5d784c802d21" -H "Authorization: {{ api_key}}"
                         
    @@ -3441,7 +3528,7 @@

    Corellium List Apps in Instance

    Method: POST

  68. -

    Header: Authorization:<api_key> Or X-Mobsf-Api-Key:<api_key>

    +

    Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>

  69. Data Params

    @@ -3497,7 +3584,7 @@

    Corellium List Apps in Instance

    Sample Call:

    • -
      curl -X POST --url http://localhost:8000/api/v1/ios/corellium_list_apps --data "instance_id=ce9cf65d-5ce5-4fad-823f-5d784c802d21" -H "Authorization:{{ api_key}}"
      +                    
      curl -X POST --url http://localhost:8000/api/v1/ios/corellium_list_apps --data "instance_id=ce9cf65d-5ce5-4fad-823f-5d784c802d21" -H "Authorization: {{ api_key}}"
                         
    @@ -3516,7 +3603,7 @@

    Setup iOS Dynamic Analysis Env

    Method: POST

  70. -

    Header: Authorization:<api_key> Or X-Mobsf-Api-Key:<api_key>

    +

    Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>

  71. @@ -3574,7 +3661,7 @@

    Setup iOS Dynamic Analysis Env

    Sample Call:

    • -
      curl -X POST --url http://localhost:8000/api/v1/ios/setup_environment --data "instance_id=bd057756-87a8-45a6-945d-35c7ce48eafe&hash=35469622303ba10a2195557a3ad1810a" -H "Authorization:{{ api_key}}"
      +                    
      curl -X POST --url http://localhost:8000/api/v1/ios/setup_environment --data "instance_id=bd057756-87a8-45a6-945d-35c7ce48eafe&hash=35469622303ba10a2195557a3ad1810a" -H "Authorization: {{ api_key}}"
                         
    @@ -3593,7 +3680,7 @@

    iOS Dynamic Analyzer APIMethod: POST

  72. -

    Header: Authorization:<api_key> Or X-Mobsf-Api-Key:<api_key>

    +

    Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>

  73. @@ -3651,7 +3738,7 @@

    iOS Dynamic Analyzer APISample Call:

    • -
      curl -X POST --url http://localhost:8000/api/v1/ios/dynamic_analyzer --data "instance_id=bd057756-87a8-45a6-945d-35c7ce48eafe&bundle_id=com.highaltitudehacks.DVIAswiftv2" -H "Authorization:{{ api_key}}"
      +                    
      curl -X POST --url http://localhost:8000/api/v1/ios/dynamic_analyzer --data "instance_id=bd057756-87a8-45a6-945d-35c7ce48eafe&bundle_id=com.highaltitudehacks.DVIAswiftv2" -H "Authorization: {{ api_key}}"
                         
    @@ -3669,7 +3756,7 @@

    Run App API

    Method: POST

  74. -

    Header: Authorization:<api_key> Or X-Mobsf-Api-Key:<api_key>

    +

    Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>

  75. @@ -3727,7 +3814,7 @@

    Run App API

    Sample Call:

    • -
      curl -X POST --url http://localhost:8000/api/v1/ios/run_app --data "instance_id=bd057756-87a8-45a6-945d-35c7ce48eafe&bundle_id=com.highaltitudehacks.DVIAswiftv2" -H "Authorization:{{ api_key}}"
      +                    
      curl -X POST --url http://localhost:8000/api/v1/ios/run_app --data "instance_id=bd057756-87a8-45a6-945d-35c7ce48eafe&bundle_id=com.highaltitudehacks.DVIAswiftv2" -H "Authorization: {{ api_key}}"
                         
    @@ -3745,7 +3832,7 @@

    Stop App API

    Method: POST

  76. -

    Header: Authorization:<api_key> Or X-Mobsf-Api-Key:<api_key>

    +

    Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>

  77. @@ -3803,7 +3890,7 @@

    Stop App API

    Sample Call:

    • -
      curl -X POST --url http://localhost:8000/api/v1/ios/stop_app --data "instance_id=bd057756-87a8-45a6-945d-35c7ce48eafe&bundle_id=com.highaltitudehacks.DVIAswiftv2" -H "Authorization:{{ api_key}}"
      +                    
      curl -X POST --url http://localhost:8000/api/v1/ios/stop_app --data "instance_id=bd057756-87a8-45a6-945d-35c7ce48eafe&bundle_id=com.highaltitudehacks.DVIAswiftv2" -H "Authorization: {{ api_key}}"
                         
    @@ -3821,7 +3908,7 @@

    Remove App API

    Method: POST

  78. -

    Header: Authorization:<api_key> Or X-Mobsf-Api-Key:<api_key>

    +

    Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>

  79. @@ -3879,7 +3966,7 @@

    Remove App API

    Sample Call:

    • -
      curl -X POST --url http://localhost:8000/api/v1/ios/remove_app --data "instance_id=bd057756-87a8-45a6-945d-35c7ce48eafe&bundle_id=com.highaltitudehacks.DVIAswiftv2" -H "Authorization:{{ api_key}}"
      +                    
      curl -X POST --url http://localhost:8000/api/v1/ios/remove_app --data "instance_id=bd057756-87a8-45a6-945d-35c7ce48eafe&bundle_id=com.highaltitudehacks.DVIAswiftv2" -H "Authorization: {{ api_key}}"
                         
    @@ -3897,7 +3984,7 @@

    Take Screenshot API

    Method: POST

  80. -

    Header: Authorization:<api_key> Or X-Mobsf-Api-Key:<api_key>

    +

    Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>

  81. @@ -3950,7 +4037,7 @@

    Take Screenshot API

    Sample Call:

    • -
      curl -X POST --url http://localhost:8000/api/v1/ios/take_screenshot --data "instance_id=bd057756-87a8-45a6-945d-35c7ce48eafe" -H "Authorization:{{ api_key}}"
      +                    
      curl -X POST --url http://localhost:8000/api/v1/ios/take_screenshot --data "instance_id=bd057756-87a8-45a6-945d-35c7ce48eafe" -H "Authorization: {{ api_key}}"
                         
    @@ -3969,7 +4056,7 @@

    Get App Container Path AP

    Method: POST

  82. -

    Header: Authorization:<api_key> Or X-Mobsf-Api-Key:<api_key>

    +

    Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>

  83. @@ -4022,7 +4109,7 @@

    Get App Container Path AP

    Sample Call:

    • -
      curl -X POST --url http://localhost:8000/api/v1/ios/get_app_container_path --data "bundle_id=com.highaltitudehacks.DVIAswiftv2" -H "Authorization:{{ api_key}}"
      +                    
      curl -X POST --url http://localhost:8000/api/v1/ios/get_app_container_path --data "bundle_id=com.highaltitudehacks.DVIAswiftv2" -H "Authorization: {{ api_key}}"
                         
    @@ -4040,7 +4127,7 @@

    Network Capture API

    Method: POST

  84. -

    Header: Authorization:<api_key> Or X-Mobsf-Api-Key:<api_key>

    +

    Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>

  85. @@ -4098,7 +4185,7 @@

    Network Capture API

    Sample Call:

    • -
      curl -X POST --url http://localhost:8000/api/v1/ios/network_capture --data "instance_id=bd057756-87a8-45a6-945d-35c7ce48eafe&state=on" -H "Authorization:{{ api_key}}"
      +                    
      curl -X POST --url http://localhost:8000/api/v1/ios/network_capture --data "instance_id=bd057756-87a8-45a6-945d-35c7ce48eafe&state=on" -H "Authorization: {{ api_key}}"
                         
    @@ -4116,7 +4203,7 @@

    Live PCAP Download APIMethod: POST

  86. -

    Header: Authorization:<api_key> Or X-Mobsf-Api-Key:<api_key>

    +

    Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>

  87. @@ -4169,7 +4256,7 @@

    Live PCAP Download APISample Call:

    • -
      curl -X POST --url http://localhost:8000/api/v1/ios/live_pcap_download --data "instance_id=bd057756-87a8-45a6-945d-35c7ce48eafe" -H "Authorization:{{ api_key}}"
      +                    
      curl -X POST --url http://localhost:8000/api/v1/ios/live_pcap_download --data "instance_id=bd057756-87a8-45a6-945d-35c7ce48eafe" -H "Authorization: {{ api_key}}"
                         
    @@ -4187,7 +4274,7 @@

    SSH Execute API

    Method: POST

  88. -

    Header: Authorization:<api_key> Or X-Mobsf-Api-Key:<api_key>

    +

    Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>

  89. @@ -4245,7 +4332,7 @@

    SSH Execute API

    Sample Call:

    • -
      curl -X POST --url http://localhost:8000/api/v1/ios/ssh_execute --data "instance_id=bd057756-87a8-45a6-945d-35c7ce48eafe&cmd=ls" -H "Authorization:{{ api_key}}"
      +                    
      curl -X POST --url http://localhost:8000/api/v1/ios/ssh_execute --data "instance_id=bd057756-87a8-45a6-945d-35c7ce48eafe&cmd=ls" -H "Authorization: {{ api_key}}"
                         
    @@ -4263,7 +4350,7 @@

    Download App Data API

    Method: POST

  90. -

    Header: Authorization:<api_key> Or X-Mobsf-Api-Key:<api_key>

    +

    Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>

  91. @@ -4321,7 +4408,7 @@

    Download App Data API

    Sample Call:

    • -
      curl -X POST --url http://localhost:8000/api/v1/ios/download_app_data --data "instance_id=bd057756-87a8-45a6-945d-35c7ce48eafe&bundle_id=com.highaltitudehacks.DVIAswiftv2" -H "Authorization:{{ api_key}}"
      +                    
      curl -X POST --url http://localhost:8000/api/v1/ios/download_app_data --data "instance_id=bd057756-87a8-45a6-945d-35c7ce48eafe&bundle_id=com.highaltitudehacks.DVIAswiftv2" -H "Authorization: {{ api_key}}"
                         
    @@ -4339,7 +4426,7 @@

    Instance Input API

    Method: POST

  92. -

    Header: Authorization:<api_key> Or X-Mobsf-Api-Key:<api_key>

    +

    Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>

  93. @@ -4417,7 +4504,7 @@

    Instance Input API

    Sample Call:

    • -
      curl -X POST --url http://localhost:8000/api/v1/ios/instance_input --data "instance_id=bd057756-87a8-45a6-945d-35c7ce48eafe&x=264&y=824&event=finger&max_x=750&max_y=1334" -H "Authorization:{{ api_key}}"
      +                    
      curl -X POST --url http://localhost:8000/api/v1/ios/instance_input --data "instance_id=bd057756-87a8-45a6-945d-35c7ce48eafe&x=264&y=824&event=finger&max_x=750&max_y=1334" -H "Authorization: {{ api_key}}"
                         
    @@ -4435,7 +4522,7 @@

    System Logs API

    Method: POST

  94. -

    Header: Authorization:<api_key> Or X-Mobsf-Api-Key:<api_key>

    +

    Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>

  95. @@ -4488,7 +4575,7 @@

    System Logs API

    Sample Call:

    • -
      curl -X POST --url http://localhost:8000/api/v1/ios/system_logs --data "instance_id=bd057756-87a8-45a6-945d-35c7ce48eafe" -H "Authorization:{{ api_key}}"
      +                    
      curl -X POST --url http://localhost:8000/api/v1/ios/system_logs --data "instance_id=bd057756-87a8-45a6-945d-35c7ce48eafe" -H "Authorization: {{ api_key}}"
                         
    @@ -4506,7 +4593,7 @@

    File Upload API

    Method: POST

  96. -

    Header: Authorization:<api_key> Or X-Mobsf-Api-Key:<api_key>

    +

    Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>

  97. @@ -4564,7 +4651,7 @@

    File Upload API

    Sample Call:

    • -
      curl -F "file=@/Users/foo/foo.sh" -F "instance_id=bd057756-87a8-45a6-945d-35c7ce48eafe" --url http://localhost:8000/api/v1/ios/file_upload -H "Authorization:{{ api_key}}"
      +                    
      curl -F "file=@/Users/foo/foo.sh" -F "instance_id=bd057756-87a8-45a6-945d-35c7ce48eafe" --url http://localhost:8000/api/v1/ios/file_upload -H "Authorization: {{ api_key}}"
                         
    @@ -4582,7 +4669,7 @@

    File Download API

    Method: POST

  98. -

    Header: Authorization:<api_key> Or X-Mobsf-Api-Key:<api_key>

    +

    Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>

  99. @@ -4640,7 +4727,7 @@

    File Download API

    Sample Call:

    • -
      curl -X POST --url http://localhost:8000/api/v1/ios/file_download --data "instance_id=bd057756-87a8-45a6-945d-35c7ce48eafe&file=/var/mobile/Containers/Data/Application/6DC4F886-537F-4F6D-87EE-ED976F4F4682/Library/Application Support/com.braze.core.persistence/data/61cbc3f/remote-configuration.json" -H "Authorization:{{ api_key}}"
      +                    
      curl -X POST --url http://localhost:8000/api/v1/ios/file_download --data "instance_id=bd057756-87a8-45a6-945d-35c7ce48eafe&file=/var/mobile/Containers/Data/Application/6DC4F886-537F-4F6D-87EE-ED976F4F4682/Library/Application Support/com.braze.core.persistence/data/61cbc3f/remote-configuration.json" -H "Authorization: {{ api_key}}"
                         
    @@ -4658,7 +4745,7 @@

    Frida Instrument API

    Method: POST

  100. -

    Header: Authorization:<api_key> Or X-Mobsf-Api-Key:<api_key>

    +

    Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>

  101. @@ -4777,7 +4864,7 @@

    Frida Instrument API

    Sample Call:

    • -
      curl -X POST --url http://localhost:8000/api/v1/frida/ios_instrument --data "frida_action=spawn&pid=&new_bundle_id=&hash=f49355aa96053a36248905f78d5419a8&bundle_id=com.1debit.beta.ChimeApp&instance_id=bd057756-87a8-45a6-945d-35c7ce48eafe&default_hooks=jailbreak_bypass&dump_hooks=network,crypto,cookies,file-access,json,sqlite,data-dir,keychain,nslog,text-inputs,nsurlcredentialstorage,nsuserdefaults,pasteboard&auxiliary_hooks=&class_name=&class_search=&method_search=asdad&class_trace=&frida_code=" -H "Authorization:{{ api_key}}"
      +                    
      curl -X POST --url http://localhost:8000/api/v1/frida/ios_instrument --data "frida_action=spawn&pid=&new_bundle_id=&hash=f49355aa96053a36248905f78d5419a8&bundle_id=com.1debit.beta.ChimeApp&instance_id=bd057756-87a8-45a6-945d-35c7ce48eafe&default_hooks=jailbreak_bypass&dump_hooks=network,crypto,cookies,file-access,json,sqlite,data-dir,keychain,nslog,text-inputs,nsurlcredentialstorage,nsuserdefaults,pasteboard&auxiliary_hooks=&class_name=&class_search=&method_search=asdad&class_trace=&frida_code=" -H "Authorization: {{ api_key}}"
                         
    @@ -4796,7 +4883,7 @@

    iOS Dynamic Analysis Report APIMethod: POST

  102. -

    Header: Authorization:<api_key> Or X-Mobsf-Api-Key:<api_key>

    +

    Header: Authorization: <api_key> Or X-Mobsf-Api-Key: <api_key>

  103. @@ -4854,7 +4941,7 @@

    iOS Dynamic Analysis Report APISample Call:

    • -
      curl -X POST --url http://localhost:8000/api/v1/dynamic/ios_report_json --data "instance_id=bd057756-87a8-45a6-945d-35c7ce48eafe&bundle_id=com.highaltitudehacks.DVIAswiftv2" -H "Authorization:{{ api_key}}"
      +                    
      curl -X POST --url http://localhost:8000/api/v1/dynamic/ios_report_json --data "instance_id=bd057756-87a8-45a6-945d-35c7ce48eafe&bundle_id=com.highaltitudehacks.DVIAswiftv2" -H "Authorization: {{ api_key}}"
                         
    diff --git a/mobsf/templates/general/home.html b/mobsf/templates/general/home.html index 59def1caae..743d64580a 100644 --- a/mobsf/templates/general/home.html +++ b/mobsf/templates/general/home.html @@ -128,18 +128,9 @@
    RECENT SCANS | RECENT SCANS | = 200 && xhr.status < 300) { + var response = JSON.parse(xhr.responseText); + if (response.status === 'ok') { + const last_log = response.logs[response.logs.length - 1]; + _("status").innerText = last_log.status + ' .'; + var number = Math.ceil((Math.floor(Math.random() * 5))); + for (i=0;i {% endif %} @@ -1051,7 +1050,38 @@
    Description:

    {% endif %} - +

    SCAN LOGS

    +

    + + + + + + + {% for log in logs %} + + + + + + {% endfor %} + +
    TimestampEventError
    + {{log.timestamp}} + + {{log.status}} + + {% if not log.exception %} +

    + OK +

    + {% else %} +

    + {{log.exception}} +

    + {% endif %} +
    +


    diff --git a/mobsf/templates/pdf/ios_report.html b/mobsf/templates/pdf/ios_report.html index ce8f4219da..cfc30b17c9 100644 --- a/mobsf/templates/pdf/ios_report.html +++ b/mobsf/templates/pdf/ios_report.html @@ -1065,6 +1065,37 @@
    Description:

    {% endif %} {% endif %} +

    SCAN LOGS

    + + + + + + + + {% for log in logs %} + + + + + + {% endfor %} + +
    TimestampEventError
    + {{log.timestamp}} + + {{log.status}} + + {% if not log.exception %} +

    + OK +

    + {% else %} +

    + {{log.exception}} +

    + {% endif %} +

    diff --git a/mobsf/templates/pdf/windows_report.html b/mobsf/templates/pdf/windows_report.html index 49a786eb72..870ee0f450 100644 --- a/mobsf/templates/pdf/windows_report.html +++ b/mobsf/templates/pdf/windows_report.html @@ -190,9 +190,39 @@

    APPX BINARY ANALYSIS

    {% endfor %} - -
    +

    SCAN LOGS

    + + + + + + + + {% for log in logs %} + + + + + + {% endfor %} + +
    TimestampEventError
    + {{log.timestamp}} + + {{log.status}} + + {% if not log.exception %} +

    + OK +

    + {% else %} +

    + {{log.exception}} +

    + {% endif %} +
    +


    diff --git a/mobsf/templates/static_analysis/android_binary_analysis.html b/mobsf/templates/static_analysis/android_binary_analysis.html index 3c95452318..01333d5604 100755 --- a/mobsf/templates/static_analysis/android_binary_analysis.html +++ b/mobsf/templates/static_analysis/android_binary_analysis.html @@ -668,14 +668,16 @@

    {{ providers | length }}

    Download {{ app_type | upper}} {% endif %} {% if app_type not in 'so' %} - Manage Suppressions + Manage Suppressions {% endif %}

    - {% if app_type not in 'jar,aar,so' %}

    - Start Dynamic Analysis -

    + {% if app_type not in 'jar,aar,so' %} + Start Dynamic Analysis {% endif %} + +

    + @@ -2494,3 +2496,36 @@ {% endblock %} +{% block scan_logs %} + + + + + + + + {% for log in logs %} + + + + + + {% endfor %} + +
    TimestampEventError
    + {{log.timestamp}} + + {{log.status}} + + {% if not log.exception %} +

    + OK +

    + {% else %} +

    + {{log.exception}} +

    + {% endif %} +
    + +{% endblock %} \ No newline at end of file diff --git a/mobsf/templates/static_analysis/android_source_analysis.html b/mobsf/templates/static_analysis/android_source_analysis.html index ec093128f5..b1e29f4703 100755 --- a/mobsf/templates/static_analysis/android_source_analysis.html +++ b/mobsf/templates/static_analysis/android_source_analysis.html @@ -543,9 +543,10 @@

    {{ providers | length }}

    Rescan - Manage Suppressions + Manage Suppressions View AndroidManifest.xml View Source +

    @@ -1892,3 +1893,36 @@ {% endblock %} +{% block scan_logs %} + + + + + + + + {% for log in logs %} + + + + + + {% endfor %} + +
    TimestampEventError
    + {{log.timestamp}} + + {{log.status}} + + {% if not log.exception %} +

    + OK +

    + {% else %} +

    + {{log.exception}} +

    + {% endif %} +
    + +{% endblock %} \ No newline at end of file diff --git a/mobsf/templates/static_analysis/ios_binary_analysis.html b/mobsf/templates/static_analysis/ios_binary_analysis.html index 73d3f7d240..32761b6ff9 100755 --- a/mobsf/templates/static_analysis/ios_binary_analysis.html +++ b/mobsf/templates/static_analysis/ios_binary_analysis.html @@ -445,7 +445,8 @@
    SCAN OPTIONS

    Rescan - Manage Suppressions + Manage Suppressions +

    @@ -1863,3 +1864,36 @@ ]; {% endblock %} +{% block scan_logs %} + + + + + + + + {% for log in logs %} + + + + + + {% endfor %} + +
    TimestampEventError
    + {{log.timestamp}} + + {{log.status}} + + {% if not log.exception %} +

    + OK +

    + {% else %} +

    + {{log.exception}} +

    + {% endif %} +
    + +{% endblock %} \ No newline at end of file diff --git a/mobsf/templates/static_analysis/ios_source_analysis.html b/mobsf/templates/static_analysis/ios_source_analysis.html index c831ef83a4..a98a65e8d5 100755 --- a/mobsf/templates/static_analysis/ios_source_analysis.html +++ b/mobsf/templates/static_analysis/ios_source_analysis.html @@ -368,8 +368,9 @@

    Rescan - Manage Suppressions - View Info.plist + Manage Suppressions + View Info.plist +

    @@ -1387,4 +1388,37 @@ {% endif %} ]; +{% endblock %} +{% block scan_logs %} + + + + + + + + {% for log in logs %} + + + + + + {% endfor %} + +
    TimestampEventError
    + {{log.timestamp}} + + {{log.status}} + + {% if not log.exception %} +

    + OK +

    + {% else %} +

    + {{log.exception}} +

    + {% endif %} +
    + {% endblock %} \ No newline at end of file diff --git a/mobsf/templates/static_analysis/windows_binary_analysis.html b/mobsf/templates/static_analysis/windows_binary_analysis.html index 013ecacb64..600e8986df 100644 --- a/mobsf/templates/static_analysis/windows_binary_analysis.html +++ b/mobsf/templates/static_analysis/windows_binary_analysis.html @@ -204,7 +204,8 @@

    Rescan - View Strings + View Strings +

    @@ -402,4 +403,37 @@ $(this).addClass("active"); }); +{% endblock %} +{% block scan_logs %} + + + + + + + + {% for log in logs %} + + + + + + {% endfor %} + +
    TimestampEventError
    + {{log.timestamp}} + + {{log.status}} + + {% if not log.exception %} +

    + OK +

    + {% else %} +

    + {{log.exception}} +

    + {% endif %} +
    + {% endblock %} \ No newline at end of file diff --git a/poetry.lock b/poetry.lock index 447d119e8f..c84508b29f 100644 --- a/poetry.lock +++ b/poetry.lock @@ -225,13 +225,13 @@ files = [ [[package]] name = "bracex" -version = "2.4" +version = "2.5" description = "Bash style brace expander." optional = false python-versions = ">=3.8" files = [ - {file = "bracex-2.4-py3-none-any.whl", hash = "sha256:efdc71eff95eaff5e0f8cfebe7d01adf2c8637c8c92edaf63ef348c241a82418"}, - {file = "bracex-2.4.tar.gz", hash = "sha256:a27eaf1df42cf561fed58b7a8f3fdf129d1ea16a81e1fadd1d17989bc6384beb"}, + {file = "bracex-2.5-py3-none-any.whl", hash = "sha256:d2fcf4b606a82ac325471affe1706dd9bbaa3536c91ef86a31f6b766f3dad1d0"}, + {file = "bracex-2.5.tar.gz", hash = "sha256:0725da5045e8d37ea9592ab3614d8b561e22c3c5fde3964699be672e072ab611"}, ] [[package]] @@ -730,24 +730,24 @@ dotenv = ["python-dotenv"] [[package]] name = "frida" -version = "16.4.7" +version = "16.4.8" description = "Dynamic instrumentation toolkit for developers, reverse-engineers, and security researchers" optional = false python-versions = ">=3.7" files = [ - {file = "frida-16.4.7-cp37-abi3-macosx_10_13_x86_64.whl", hash = "sha256:82887e0b76ed6c83ced556abc71ae7b5b441bf6f1d4f8d4077fd2b8aaad96ff0"}, - {file = "frida-16.4.7-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:a09a551491f67252b002ab729f9abd90c77629f866132be0d42a5e686b99609c"}, - {file = "frida-16.4.7-cp37-abi3-manylinux1_i686.whl", hash = "sha256:7249a7d91dd8db9c0f4f4963ef03bd7be55cd1c7803643de6978ef065a5ed29c"}, - {file = "frida-16.4.7-cp37-abi3-manylinux1_x86_64.whl", hash = "sha256:8edb260bed11fe9765e43d1e26144144f7c8545a43da186b4767ba8a7652ff56"}, - {file = "frida-16.4.7-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:1cd536313d3ae1bab5c6d462752ee41aab28de892a9c9640c6d48001e68f11b0"}, - {file = "frida-16.4.7-cp37-abi3-manylinux2014_armv7l.whl", hash = "sha256:7b4d39c618a6c0f2135778089fab01d04f56e22234b20036ff81e40dba84151c"}, - {file = "frida-16.4.7-cp37-abi3-manylinux_2_17_aarch64.whl", hash = "sha256:b7ca5f8b2dc07d47f1c3dc71816cf3590222ca331fee8a04d721475c125d9bfe"}, - {file = "frida-16.4.7-cp37-abi3-manylinux_2_17_armv7l.whl", hash = "sha256:ede39a1169971bdaf4b94b0f62a6dc091e9a6361861b0e5f6e478af1dd8cd0e9"}, - {file = "frida-16.4.7-cp37-abi3-manylinux_2_5_i686.whl", hash = "sha256:9555c511cf5a4fbe0db21281792eac87cc3a45381abe457614ad2a21ce5875c6"}, - {file = "frida-16.4.7-cp37-abi3-manylinux_2_5_x86_64.whl", hash = "sha256:576599759de2f326ed3f8f26f3f7118517cf25cf1728ec7f86a70f3308778ff3"}, - {file = "frida-16.4.7-cp37-abi3-win32.whl", hash = "sha256:705a8db36c0588199f459cae712642d4228c113e098c228fed52d5d416595ac7"}, - {file = "frida-16.4.7-cp37-abi3-win_amd64.whl", hash = "sha256:79212a4fa9ba1dc1f2b951a8fd288b193334e9d0d010937424df4a465124abc3"}, - {file = "frida-16.4.7.tar.gz", hash = "sha256:0a27cafa405de46796510fbf5846b545ea1b4e21943672e50fe719ea057bfced"}, + {file = "frida-16.4.8-cp37-abi3-macosx_10_13_x86_64.whl", hash = "sha256:7eeea45d6f2a3d4902674c584ed44e7d3c2ceb0d7dbfbcb68a27608223a37b37"}, + {file = "frida-16.4.8-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:2fdf65dc80e1be8af20a0e94c2eee92e1a775491b468d3198ff32caf591c2b4c"}, + {file = "frida-16.4.8-cp37-abi3-manylinux1_i686.whl", hash = "sha256:b6904be23500b91220806af9f21b033c0e517c5bb9b96a4511fa79f2e605c3d0"}, + {file = "frida-16.4.8-cp37-abi3-manylinux1_x86_64.whl", hash = "sha256:f7def5c2c34aa1b580c80d88f5726b0623ff802356e3c3d16db3299e7ad21c11"}, + {file = "frida-16.4.8-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:01ed697dd1e55c95520d741dbc069a3caa51219ac03af56d8536014be9c113ec"}, + {file = "frida-16.4.8-cp37-abi3-manylinux2014_armv7l.whl", hash = "sha256:65cf8c332b0b1e96b5b2ed3ab328bf936db692e6b4d09bf39b3eff060deaab8e"}, + {file = "frida-16.4.8-cp37-abi3-manylinux_2_17_aarch64.whl", hash = "sha256:969b62850219c259c04a2c957a959b162499962e5b18fc2e87c119dd8c3cf8f0"}, + {file = "frida-16.4.8-cp37-abi3-manylinux_2_17_armv7l.whl", hash = "sha256:40cb570e1e28f729cd7af350a95b5ada04893913d83eb383dcc667cbbbb6c707"}, + {file = "frida-16.4.8-cp37-abi3-manylinux_2_5_i686.whl", hash = "sha256:e0ad48b7c1611863f2e2298b6da3c3a4487cf298c932a44cc41321bfb9a89824"}, + {file = "frida-16.4.8-cp37-abi3-manylinux_2_5_x86_64.whl", hash = "sha256:91796c3f8feb279a24e4163c5317bf6b1addb8868143ec17d8e2c37ebf11e96f"}, + {file = "frida-16.4.8-cp37-abi3-win32.whl", hash = "sha256:8130cfa16642f55883803089d90ef167c44b113db6cecaa56ca7ea35dd8a25d7"}, + {file = "frida-16.4.8-cp37-abi3-win_amd64.whl", hash = "sha256:a6f8cf93dd4eb84ed38fdaf98f4e7d9b7cf48ef89bc2e3aea5910a0ee0643d1e"}, + {file = "frida-16.4.8.tar.gz", hash = "sha256:4a3864987b23a97f1bd4696abed009c39b2100dde5b12156f7def3cf403dca96"}, ] [package.dependencies] @@ -2260,13 +2260,13 @@ files = [ [[package]] name = "tqdm" -version = "4.66.4" +version = "4.66.5" description = "Fast, Extensible Progress Meter" optional = false python-versions = ">=3.7" files = [ - {file = "tqdm-4.66.4-py3-none-any.whl", hash = "sha256:b75ca56b413b030bc3f00af51fd2c1a1a5eac6a0c1cca83cbb37a5c52abce644"}, - {file = "tqdm-4.66.4.tar.gz", hash = "sha256:e4d936c9de8727928f3be6079590e97d9abfe8d39a590be678eb5919ffc186bb"}, + {file = "tqdm-4.66.5-py3-none-any.whl", hash = "sha256:90279a3770753eafc9194a0364852159802111925aa30eb3f9d85b0e805ac7cd"}, + {file = "tqdm-4.66.5.tar.gz", hash = "sha256:e1020aef2e5096702d8a025ac7d16b1577279c9d63f8375b63083e9a5f0fcbad"}, ] [package.dependencies] diff --git a/pyproject.toml b/pyproject.toml index edaf5762d0..65c7ee1b14 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "mobsf" -version = "4.0.5" +version = "4.0.6" description = "Mobile Security Framework (MobSF) is an automated, all-in-one mobile application (Android/iOS/Windows) pen-testing, malware analysis and security assessment framework capable of performing static and dynamic analysis." keywords = ["mobsf", "mobile security framework", "mobile security", "security tool", "static analysis", "dynamic analysis", "malware analysis"] authors = ["Ajin Abraham "] diff --git a/scripts/update_android_permissions.py b/scripts/update_android_permissions.py index f2395b23ee..7a283b4449 100644 --- a/scripts/update_android_permissions.py +++ b/scripts/update_android_permissions.py @@ -47,7 +47,7 @@ # check the permissions we currently have in dvm_permissions.py DVM_PERMISSIONS = {} eval(compile(open('../mobsf/StaticAnalyzer/views/' - 'android/dvm_permissions.py').read(), + 'android/kb/dvm_permissions.py').read(), '', 'exec')) MANIFEST_PERMISSIONS = DVM_PERMISSIONS['MANIFEST_PERMISSION']