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 @@
+
+
+
+ {% block scan_logs %}
+ {% endblock %}
+
+
+
+
@@ -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
api/v1/upload
- Upload a File
api/v1/scan
- Scan a File
+ api/v1/scan_logs
- Display Live Scan Logs
api/v1/scans
- Display Recent Scans
api/v1/delete_scan
- Delete a Scan
api/v1/scorecard
- App Scorecard
@@ -107,7 +108,7 @@ Upload File 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
@@ -163,14 +164,14 @@ Upload File API
Sample Call:
OR
@@ -187,7 +188,7 @@ Scan File 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
@@ -257,14 +258,100 @@ Scan File API
Sample Call:
OR
+
+
+
+ Scan Logs API
+ API that provides live and latest scan logs.
+
+
+
+
+ Param Name |
+ Param Value |
+ Required |
+
+
+
+
+ hash |
+ hash of the scan |
+ Yes |
+
+
+
+
+
+ -
+
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:
+
+ OR
+
@@ -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:
OR
@@ -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:
OR
@@ -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:
OR
@@ -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:
OR
@@ -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:
OR
@@ -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:
OR
@@ -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:
OR
@@ -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:
OR
@@ -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:
OR
@@ -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:
@@ -1324,7 +1411,7 @@ Start Dynamic Analysis 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
@@ -1398,7 +1485,7 @@ Start Dynamic Analysis API
Sample Call:
@@ -1416,7 +1503,7 @@ View Logcat 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
@@ -1472,7 +1559,7 @@ View Logcat API
Sample Call:
@@ -1490,7 +1577,7 @@ MobSFy 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
@@ -1549,7 +1636,7 @@ MobSFy API
Sample Call:
@@ -1567,7 +1654,7 @@ Execute ADB Commands 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
@@ -1626,7 +1713,7 @@ Execute ADB Commands API
Sample Call:
@@ -1644,7 +1731,7 @@ Install or Remove Root CA 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
@@ -1703,7 +1790,7 @@ Install or Remove Root CA API
Sample Call:
@@ -1720,7 +1807,7 @@ Set or Unset MobSF Global HTTP(S) Proxy
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
@@ -1779,7 +1866,7 @@ Set or Unset MobSF Global HTTP(S) Proxy
Sample Call:
@@ -1797,7 +1884,7 @@ Activity or Exported Activity Tester 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
@@ -1860,7 +1947,7 @@ Activity or Exported Activity Tester APISample Call:
@@ -1879,7 +1966,7 @@ Start Activity or Exported Activity A
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
@@ -1942,7 +2029,7 @@ Start Activity or Exported Activity A
Sample Call:
@@ -1960,7 +2047,7 @@ TLS/SSL Security Tester 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
@@ -2016,7 +2103,7 @@ TLS/SSL Security Tester API
Sample Call:
@@ -2034,7 +2121,7 @@ Frida Instrument App 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
@@ -2137,14 +2224,14 @@ Frida Instrument App API
Sample 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
- Header: Authorization:<api_key>
Or X-Mobsf-Api-Key:<api_key>
+ Header: Authorization: <api_key>
Or X-Mobsf-Api-Key: <api_key>
Data Params
@@ -2218,7 +2305,7 @@ Frida API Monitor API
Sample Call:
@@ -2236,7 +2323,7 @@ Frida Get Runtime Dependencie
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
@@ -2292,7 +2379,7 @@ Frida Get Runtime Dependencie
Sample Call:
@@ -2310,7 +2397,7 @@ Frida View Logs 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
@@ -2366,7 +2453,7 @@ Frida View Logs API
Sample Call:
@@ -2384,7 +2471,7 @@ Frida List Scripts 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
@@ -2455,7 +2542,7 @@ Frida List Scripts API
Sample Call:
@@ -2474,7 +2561,7 @@ Frida Get Script 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
@@ -2535,7 +2622,7 @@ Frida Get Script API
Sample Call:
@@ -2553,7 +2640,7 @@ Stop Dynamic Analysis 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
@@ -2611,7 +2698,7 @@ Stop Dynamic Analysis API
Sample Call:
@@ -2629,7 +2716,7 @@ Dynamic Analysis JSON 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
@@ -2685,7 +2772,7 @@ Dynamic Analysis JSON Report API
Sample Call:
@@ -2704,7 +2791,7 @@ Dynamic Analysis View Source 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
@@ -2770,7 +2857,7 @@ Dynamic Analysis View Source API
Sample Call:
@@ -2789,7 +2876,7 @@ Supported Corellium iOS Models 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>
@@ -2826,7 +2913,7 @@ Supported Corellium iOS Models API<
Sample Call:
@@ -2845,7 +2932,7 @@ Supported Corellium iOS Versi
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
@@ -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
- Header: Authorization:<api_key>
Or X-Mobsf-Api-Key:<api_key>
+ Header: Authorization: <api_key>
Or X-Mobsf-Api-Key: <api_key>
Data Params
@@ -2992,7 +3079,7 @@ Corellium Create iOS Instance AP
Sample Call:
@@ -3012,7 +3099,7 @@ iOS Dynamic Analysis 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
@@ -3052,7 +3139,7 @@ iOS Dynamic Analysis APISample Call:
@@ -3070,7 +3157,7 @@ Corellium Start iOS Instance 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
@@ -3126,7 +3213,7 @@ Corellium Start iOS Instance API<
Sample Call:
@@ -3144,7 +3231,7 @@ Corellium Stop iOS Instance 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
@@ -3200,7 +3287,7 @@ Corellium Stop iOS Instance APISample Call:
@@ -3218,7 +3305,7 @@ Corellium Unpause iOS Instance
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
@@ -3274,7 +3361,7 @@ Corellium Unpause iOS Instance
Sample Call:
@@ -3292,7 +3379,7 @@ Corellium Reboot iOS Instance AP
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
@@ -3348,7 +3435,7 @@ Corellium Reboot iOS Instance AP
Sample Call:
@@ -3367,7 +3454,7 @@ Corellium Destroy iOS Instance
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
@@ -3423,7 +3510,7 @@ Corellium Destroy iOS Instance
Sample Call:
@@ -3441,7 +3528,7 @@ Corellium List Apps in Instance
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
@@ -3497,7 +3584,7 @@ Corellium List Apps in Instance
Sample Call:
@@ -3516,7 +3603,7 @@ Setup iOS Dynamic Analysis Env
Method: POST
- Header: Authorization:<api_key>
Or X-Mobsf-Api-Key:<api_key>
+ Header: Authorization: <api_key>
Or X-Mobsf-Api-Key: <api_key>
@@ -3574,7 +3661,7 @@ Setup iOS Dynamic Analysis Env
Sample Call:
@@ -3593,7 +3680,7 @@ iOS Dynamic Analyzer APIMethod: POST
- Header: Authorization:<api_key>
Or X-Mobsf-Api-Key:<api_key>
+ Header: Authorization: <api_key>
Or X-Mobsf-Api-Key: <api_key>
@@ -3651,7 +3738,7 @@ iOS Dynamic Analyzer APISample Call:
@@ -3669,7 +3756,7 @@ Run App 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>
@@ -3727,7 +3814,7 @@ Run App API
Sample Call:
@@ -3745,7 +3832,7 @@ Stop App 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>
@@ -3803,7 +3890,7 @@ Stop App API
Sample Call:
@@ -3821,7 +3908,7 @@ Remove App 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>
@@ -3879,7 +3966,7 @@ Remove App API
Sample Call:
@@ -3897,7 +3984,7 @@ Take Screenshot 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>
@@ -3950,7 +4037,7 @@ Take Screenshot API
Sample Call:
@@ -3969,7 +4056,7 @@ Get App Container Path AP
Method: POST
- Header: Authorization:<api_key>
Or X-Mobsf-Api-Key:<api_key>
+ Header: Authorization: <api_key>
Or X-Mobsf-Api-Key: <api_key>
@@ -4022,7 +4109,7 @@ Get App Container Path AP
Sample Call:
@@ -4040,7 +4127,7 @@ Network Capture 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>
@@ -4098,7 +4185,7 @@ Network Capture API
Sample Call:
@@ -4116,7 +4203,7 @@ Live PCAP Download APIMethod: POST
- Header: Authorization:<api_key>
Or X-Mobsf-Api-Key:<api_key>
+ Header: Authorization: <api_key>
Or X-Mobsf-Api-Key: <api_key>
@@ -4169,7 +4256,7 @@ Live PCAP Download APISample Call:
@@ -4187,7 +4274,7 @@ SSH Execute 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>
@@ -4245,7 +4332,7 @@ SSH Execute API
Sample Call:
@@ -4263,7 +4350,7 @@ Download App Data 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>
@@ -4321,7 +4408,7 @@ Download App Data API
Sample Call:
@@ -4339,7 +4426,7 @@ Instance Input 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>
@@ -4417,7 +4504,7 @@ Instance Input API
Sample Call:
@@ -4435,7 +4522,7 @@ System Logs 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>
@@ -4488,7 +4575,7 @@ System Logs API
Sample Call:
@@ -4506,7 +4593,7 @@ File Upload 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>
@@ -4564,7 +4651,7 @@ File Upload API
Sample Call:
@@ -4582,7 +4669,7 @@ File Download 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>
@@ -4640,7 +4727,7 @@ File Download API
Sample Call:
@@ -4658,7 +4745,7 @@ Frida Instrument 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>
@@ -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
- Header: Authorization:<api_key>
Or X-Mobsf-Api-Key:<api_key>
+ Header: Authorization: <api_key>
Or X-Mobsf-Api-Key: <api_key>
-
-
+ SCAN LOGS
+
+
+ Timestamp |
+ Event |
+ Error |
+
+
+ {% for log in logs %}
+
+
+ {{log.timestamp}}
+ |
+
+ {{log.status}}
+ |
+
+ {% if not log.exception %}
+
+ OK
+
+ {% else %}
+
+ {{log.exception}}
+
+ {% endif %}
+ |
+
+ {% endfor %}
+
+
+
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 @@ Suppression Rules
{% endblock %}
+{% block scan_logs %}
+
+
+ Timestamp |
+ Event |
+ Error |
+
+
+ {% for log in logs %}
+
+
+ {{log.timestamp}}
+ |
+
+ {{log.status}}
+ |
+
+ {% if not log.exception %}
+
+ OK
+
+ {% else %}
+
+ {{log.exception}}
+
+ {% endif %}
+ |
+
+ {% endfor %}
+
+
+
+{% 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 @@ Suppression Rules
{% endblock %}
+{% block scan_logs %}
+
+
+ Timestamp |
+ Event |
+ Error |
+
+
+ {% for log in logs %}
+
+
+ {{log.timestamp}}
+ |
+
+ {{log.status}}
+ |
+
+ {% if not log.exception %}
+
+ OK
+
+ {% else %}
+
+ {{log.exception}}
+
+ {% endif %}
+ |
+
+ {% endfor %}
+
+
+
+{% 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 @@ Suppression Rules
];
{% endblock %}
+{% block scan_logs %}
+
+
+ Timestamp |
+ Event |
+ Error |
+
+
+ {% for log in logs %}
+
+
+ {{log.timestamp}}
+ |
+
+ {{log.status}}
+ |
+
+ {% if not log.exception %}
+
+ OK
+
+ {% else %}
+
+ {{log.exception}}
+
+ {% endif %}
+ |
+
+ {% endfor %}
+
+
+
+{% 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 @@ Suppression Rules
{% endif %}
];
+{% endblock %}
+{% block scan_logs %}
+
+
+ Timestamp |
+ Event |
+ Error |
+
+
+ {% for log in logs %}
+
+
+ {{log.timestamp}}
+ |
+
+ {{log.status}}
+ |
+
+ {% if not log.exception %}
+
+ OK
+
+ {% else %}
+
+ {{log.exception}}
+
+ {% endif %}
+ |
+
+ {% endfor %}
+
+
+
{% 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 @@ Strings
$(this).addClass("active");
});
+{% endblock %}
+{% block scan_logs %}
+
+
+ Timestamp |
+ Event |
+ Error |
+
+
+ {% for log in logs %}
+
+
+ {{log.timestamp}}
+ |
+
+ {{log.status}}
+ |
+
+ {% if not log.exception %}
+
+ OK
+
+ {% else %}
+
+ {{log.exception}}
+
+ {% endif %}
+ |
+
+ {% endfor %}
+
+
+
{% 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']