From c6f037103d79b70a3d0814c812f165c3e2b82330 Mon Sep 17 00:00:00 2001 From: Ajin Abraham Date: Sat, 16 Dec 2023 22:19:09 -0800 Subject: [PATCH] Bug Fixes + Improvements (#2307) * Replace Android test APK * Added tests for Library analysis from binary (scan_library route) * iOS merge findings from swift and objective c rules with same rule identifier. Fixes #2287 * iOS Binary analysis, sort regex matches. Fixes #2252 * Framework dylibs with no extensions to skip PIE checks. Fixes #2307 * Select correct network_security config. Fixes #2049 * Android Manifest Analysis added support for detecting task hijacking (StrandHogg 1.0 and StrandHogg 2.0) . Fixes #2124 * Added new manifest analysis rule to warn on apps targeting older Android OS * Updated severity of findings * UI improvement for AppSec dashboard to show a loader * UI changes in Static Analysis to collapse large no of files in API and Code Analysis for better real estate * Improved certificate file analysis for android, jar, aar, and ios * MobSF version Bump --- mobsf/MobSF/init.py | 2 +- mobsf/MobSF/utils.py | 3 +- mobsf/MobSF/views/home.py | 2 +- mobsf/StaticAnalyzer/test_files | 2 +- mobsf/StaticAnalyzer/tests.py | 42 +++++--- .../views/android/android_manifest_desc.py | 65 +++++++++++-- .../views/android/cert_analysis.py | 7 +- .../views/android/manifest_analysis.py | 97 ++++++++++++++----- .../views/android/network_security.py | 18 +++- .../views/common/binary/macho.py | 10 +- .../views/ios/binary_rule_matcher.py | 2 +- .../StaticAnalyzer/views/ios/code_analysis.py | 25 ++++- .../StaticAnalyzer/views/ios/file_analysis.py | 10 +- .../views/ios/rules/swift_rules.yaml | 26 ++--- mobsf/templates/base/base_layout.html | 2 +- mobsf/templates/general/about.html | 2 +- mobsf/templates/general/apidocs.html | 4 +- mobsf/templates/general/home.html | 4 +- .../android_binary_analysis.html | 39 ++++++-- .../android_source_analysis.html | 40 ++++++-- .../static_analysis/appsec_dashboard.html | 21 +++- .../static_analysis/ios_source_analysis.html | 39 ++++++-- pyproject.toml | 2 +- scripts/mass_static_analysis.py | 4 +- 24 files changed, 353 insertions(+), 115 deletions(-) diff --git a/mobsf/MobSF/init.py b/mobsf/MobSF/init.py index b475711c68..ea18aa5ec2 100644 --- a/mobsf/MobSF/init.py +++ b/mobsf/MobSF/init.py @@ -10,7 +10,7 @@ logger = logging.getLogger(__name__) -VERSION = '3.8.7' +VERSION = '3.8.8' BANNER = """ __ __ _ ____ _____ _____ ___ | \/ | ___ | |__/ ___|| ___|_ _|___ / ( _ ) diff --git a/mobsf/MobSF/utils.py b/mobsf/MobSF/utils.py index ba88e8a89a..52848aa55f 100755 --- a/mobsf/MobSF/utils.py +++ b/mobsf/MobSF/utils.py @@ -492,7 +492,8 @@ def update_local_db(db_name, url, local_file): else: logger.info('%s Database is up-to-date', db_name) return update - except requests.exceptions.ReadTimeout: + except (requests.exceptions.ReadTimeout, + requests.exceptions.ConnectionError): logger.warning('Failed to download %s DB.', db_name) except Exception: logger.exception('[ERROR] %s DB Update', db_name) diff --git a/mobsf/MobSF/views/home.py b/mobsf/MobSF/views/home.py index c4cf9a2a7a..b66e7583c9 100755 --- a/mobsf/MobSF/views/home.py +++ b/mobsf/MobSF/views/home.py @@ -164,7 +164,7 @@ def upload(self): def api_docs(request): """Api Docs Route.""" context = { - 'title': 'REST API Docs', + 'title': 'API Docs', 'api_key': api_key(), 'version': settings.MOBSF_VER, } diff --git a/mobsf/StaticAnalyzer/test_files b/mobsf/StaticAnalyzer/test_files index 5094ab786b..7d44d847af 160000 --- a/mobsf/StaticAnalyzer/test_files +++ b/mobsf/StaticAnalyzer/test_files @@ -1 +1 @@ -Subproject commit 5094ab786b9435830c68e1dd2646d7013ca694c1 +Subproject commit 7d44d847af8cbdc34d24f284c1616165ac8d9d5a diff --git a/mobsf/StaticAnalyzer/tests.py b/mobsf/StaticAnalyzer/tests.py index 9d644331da..19e9ab7395 100755 --- a/mobsf/StaticAnalyzer/tests.py +++ b/mobsf/StaticAnalyzer/tests.py @@ -71,7 +71,7 @@ def static_analysis_test(): if platform.system() in ['Darwin', 'Linux']: pdfs = [ '/pdf/02e7989c457ab67eb514a8328779f256/', - '/pdf/3a552566097a8de588b8184b059b0158/', + '/pdf/82ab8b2193b3cfb1c737e3a786be363a/', '/pdf/6c23c2970551be15f32bbab0b5db0c71/', '/pdf/52c50ae824e329ba8b5b7a0f523efffe/', '/pdf/57bb5be0ea44a755ada4a93885c3825e/', @@ -81,7 +81,7 @@ def static_analysis_test(): else: pdfs = [ '/pdf/02e7989c457ab67eb514a8328779f256/', - '/pdf/3a552566097a8de588b8184b059b0158/', + '/pdf/82ab8b2193b3cfb1c737e3a786be363a/', '/pdf/52c50ae824e329ba8b5b7a0f523efffe/', '/pdf/57bb5be0ea44a755ada4a93885c3825e/', '/pdf/8179b557433835827a70510584f3143e/', @@ -101,7 +101,7 @@ def static_analysis_test(): # Compare apps test logger.info('Running App Compare tests') - first_app = '3a552566097a8de588b8184b059b0158' + first_app = '82ab8b2193b3cfb1c737e3a786be363a' second_app = '52c50ae824e329ba8b5b7a0f523efffe' url = '/compare/{}/{}/'.format(first_app, second_app) resp = http_client.get(url, follow=True) @@ -113,10 +113,24 @@ def static_analysis_test(): logger.info(resp.content) return True + # Scan shared object or dylib from binaries. + logger.info('Running Library Analysis test') + md5 = '82ab8b2193b3cfb1c737e3a786be363a' + lib = 'apktool_out/lib/arm64-v8a/libdivajni.so' + url = f'/scan_library/{md5}?library={lib}' + resp = http_client.get(url, follow=True) + assert (resp.status_code == 200) + if resp.status_code == 200: + logger.info('[OK] Library Analysis test passed successfully') + else: + logger.error('Library Analysis test failed') + logger.info(resp.content) + return True + # Search by MD5 if platform.system() in ['Darwin', 'Linux']: scan_md5s = ['02e7989c457ab67eb514a8328779f256', - '3a552566097a8de588b8184b059b0158', + '82ab8b2193b3cfb1c737e3a786be363a', '6c23c2970551be15f32bbab0b5db0c71', '52c50ae824e329ba8b5b7a0f523efffe', '57bb5be0ea44a755ada4a93885c3825e', @@ -124,7 +138,7 @@ def static_analysis_test(): '7b0a23bffc80bac05739ea1af898daad'] else: scan_md5s = ['02e7989c457ab67eb514a8328779f256', - '3a552566097a8de588b8184b059b0158', + '82ab8b2193b3cfb1c737e3a786be363a', '52c50ae824e329ba8b5b7a0f523efffe', '57bb5be0ea44a755ada4a93885c3825e', '8179b557433835827a70510584f3143e', @@ -242,7 +256,7 @@ def api_test(): if platform.system() in ['Darwin', 'Linux']: pdfs = [ {'hash': '02e7989c457ab67eb514a8328779f256'}, - {'hash': '3a552566097a8de588b8184b059b0158'}, + {'hash': '82ab8b2193b3cfb1c737e3a786be363a'}, {'hash': '6c23c2970551be15f32bbab0b5db0c71'}, {'hash': '52c50ae824e329ba8b5b7a0f523efffe'}, {'hash': '57bb5be0ea44a755ada4a93885c3825e'}, @@ -252,7 +266,7 @@ def api_test(): else: pdfs = [ {'hash': '02e7989c457ab67eb514a8328779f256'}, - {'hash': '3a552566097a8de588b8184b059b0158'}, + {'hash': '82ab8b2193b3cfb1c737e3a786be363a'}, {'hash': '52c50ae824e329ba8b5b7a0f523efffe'}, {'hash': '57bb5be0ea44a755ada4a93885c3825e'}, {'hash': '8179b557433835827a70510584f3143e'}, @@ -314,9 +328,9 @@ def api_test(): logger.info('[OK] Scorecard API test completed') logger.info('Running View Source API test') # View Source tests - files = [{'file': 'opensecurity/helloworld/MainActivity.java', + files = [{'file': 'jakhar/aseem/diva/MainActivity.java', 'type': 'apk', - 'hash': '3a552566097a8de588b8184b059b0158'}, + 'hash': '82ab8b2193b3cfb1c737e3a786be363a'}, {'file': 'opensecurity/webviewignoressl/MainActivity.java', 'type': 'studio', 'hash': '52c50ae824e329ba8b5b7a0f523efffe'}, @@ -351,7 +365,7 @@ def api_test(): resp = http_client.post( '/api/v1/compare', { - 'hash1': '3a552566097a8de588b8184b059b0158', + 'hash1': '82ab8b2193b3cfb1c737e3a786be363a', 'hash2': '52c50ae824e329ba8b5b7a0f523efffe', }, HTTP_AUTHORIZATION=auth) @@ -359,7 +373,7 @@ def api_test(): resp_custom = http_client.post( '/api/v1/compare', { - 'hash1': '3a552566097a8de588b8184b059b0158', + 'hash1': '82ab8b2193b3cfb1c737e3a786be363a', 'hash2': '52c50ae824e329ba8b5b7a0f523efffe', }, HTTP_X_MOBSF_API_KEY=auth) @@ -373,7 +387,7 @@ def api_test(): logger.info('Running Delete Scan Results test') # Suppression tests # Android Manifest by rule - and_hash = '3a552566097a8de588b8184b059b0158' + and_hash = '82ab8b2193b3cfb1c737e3a786be363a' rule = 'app_is_debuggable' typ = 'manifest' logger.info('Running Suppression disable by rule for APK manifest') @@ -489,7 +503,7 @@ def api_test(): # Deleting Scan Results if platform.system() in ['Darwin', 'Linux']: scan_md5s = ['02e7989c457ab67eb514a8328779f256', - '3a552566097a8de588b8184b059b0158', + '82ab8b2193b3cfb1c737e3a786be363a', '6c23c2970551be15f32bbab0b5db0c71', '52c50ae824e329ba8b5b7a0f523efffe', '57bb5be0ea44a755ada4a93885c3825e', @@ -498,7 +512,7 @@ def api_test(): ] else: scan_md5s = ['02e7989c457ab67eb514a8328779f256', - '3a552566097a8de588b8184b059b0158', + '82ab8b2193b3cfb1c737e3a786be363a', '52c50ae824e329ba8b5b7a0f523efffe', '57bb5be0ea44a755ada4a93885c3825e', '8179b557433835827a70510584f3143e', diff --git a/mobsf/StaticAnalyzer/views/android/android_manifest_desc.py b/mobsf/StaticAnalyzer/views/android/android_manifest_desc.py index 6b7713d03f..d9f4db47b1 100644 --- a/mobsf/StaticAnalyzer/views/android/android_manifest_desc.py +++ b/mobsf/StaticAnalyzer/views/android/android_manifest_desc.py @@ -42,15 +42,28 @@ '[android:networkSecurityConfig=%s]'), }, 'vulnerable_os_version': { + 'title': ('App can be installed on a vulnerable ' + 'upatched Android version
Android %s, [minSdk=%s]'), + 'level': 'high', + 'description': ('This application can be installed on an older version' + ' of android that has multiple unfixed ' + 'vulnerabilities. These devices won\'t receive ' + 'reasonable security updates from Google. ' + 'Support an Android version => 10, API 29 ' + 'to receive reasonable security updates.'), + 'name': ('App can be installed on a vulnerable ' + 'upatched Android version %s, [minSdk=%s]'), + }, + 'vulnerable_os_version2': { 'title': ('App can be installed on a vulnerable Android version' - '
[minSdk=%s]'), + '
Android %s, minSdk=%s]'), 'level': 'warning', 'description': ('This application can be installed on an older version' - ' of android that has multiple unfixed ' - 'vulnerabilities. Support an Android version > 8, ' - 'API 26 to receive reasonable security updates.'), + ' of android that has multiple vulnerabilities. ' + 'Support an Android version => 10, API 29 ' + 'to receive reasonable security updates.'), 'name': ('App can be installed on a vulnerable Android version' - '[minSdk=%s]'), + ' %s, [minSdk=%s]'), }, 'app_is_debuggable': { 'title': 'Debug Enabled For App
[android:debuggable=true]', @@ -104,7 +117,7 @@ }, 'non_standard_launchmode': { 'title': 'Launch Mode of activity (%s) is not standard.', - 'level': 'high', + 'level': 'warning', 'description': ('An Activity should not be having the launch mode' ' attribute set to "singleTask/singleInstance" as ' 'it becomes root Activity and it is possible for' @@ -114,9 +127,45 @@ ' information is included in an Intent.'), 'name': 'Launch Mode of activity (%s) is not standard.', }, + 'task_hijacking': { + 'title': ('Activity (%s) is vulnerable to Android ' + 'Task Hijacking/StrandHogg.'), + 'level': 'high', + 'description': ('An Activity should not be having the launch mode ' + 'attribute set to "singleTask". It is then ' + 'possible for other applications to place a ' + 'malicious activity on top of the activity stack ' + 'resulting in Task Hijacking/StrandHogg 1.0' + 'vulnerability. This makes the application an easy ' + 'target for phishing attacks. The vulnerability can ' + 'be remediated by setting the launch mode attribute ' + 'to "singleInstance" or by setting an empty ' + 'taskAffinity (taskAffinity="") attribute. You can ' + 'also update the target SDK version of the app to ' + '28 or higher to fix this issue at platform level.'), + 'name': ('Activity (%s) is vulnerable to Android ' + 'Task Hijacking/StrandHogg.'), + }, + 'task_hijacking2': { + 'title': 'Activity (%s) is vulnerable to StrandHogg 2.0', + 'level': 'high', + 'description': ('Activity is found to be vulnerable to ' + 'StrandHogg 2.0 task hijacking vulnerability. ' + 'When vulnerable, it is possible for other ' + 'applications to place a malicious activity ' + 'on top of the activity stack of the vulnerable ' + 'application. This makes the application an easy ' + 'target for phishing attacks. The vulnerability can ' + 'be remediated by setting the launch mode attribute ' + 'to "singleInstance" and by setting an empty ' + 'taskAffinity (taskAffinity=""). You can also update ' + 'the target SDK version of the app to 29 or higher ' + 'to fix this issue at platform level.'), + 'name': 'Activity (%s) is vulnerable to StrandHogg 2.0', + }, 'improper_provider_permission': { 'title': 'Improper Content Provider Permissions
[%s]', - 'level': 'high', + 'level': 'warning', 'description': ('A content provider permission was set to allows' ' access from any other app on the device. ' 'Content providers may contain sensitive ' @@ -343,7 +392,7 @@ 'explicitly_exported': { 'title': ('%s (%s) is not Protected.' '
[android:exported=true]'), - 'level': 'high', + 'level': 'warning', 'description': ('A%s %s is found to be shared with other apps on the' ' device therefore leaving it accessible to any other' ' application on the device.'), diff --git a/mobsf/StaticAnalyzer/views/android/cert_analysis.py b/mobsf/StaticAnalyzer/views/android/cert_analysis.py index 104eb10aa9..dbdc6d694a 100755 --- a/mobsf/StaticAnalyzer/views/android/cert_analysis.py +++ b/mobsf/StaticAnalyzer/views/android/cert_analysis.py @@ -50,10 +50,11 @@ def get_hardcoded_cert_keystore(files): for file_name in files: if '.' not in file_name: continue - ext = file_name.split('.')[-1] - if re.search('cer|pem|cert|crt|pub|key|pfx|p12|der', ext): + ext = Path(file_name).suffix + if ext in ('.cer', '.pem', '.cert', '.crt', + '.pub', '.key', '.pfx', '.p12', '.der'): certz.append(escape(file_name)) - if re.search('jks|bks', ext): + if ext in ('.jks', '.bks'): key_store.append(escape(file_name)) if certz: desc = 'Certificate/Key files hardcoded inside the app.' diff --git a/mobsf/StaticAnalyzer/views/android/manifest_analysis.py b/mobsf/StaticAnalyzer/views/android/manifest_analysis.py index 74c5ad4ddd..080baf4f27 100755 --- a/mobsf/StaticAnalyzer/views/android/manifest_analysis.py +++ b/mobsf/StaticAnalyzer/views/android/manifest_analysis.py @@ -14,8 +14,42 @@ ANDROID_4_2_LEVEL = 17 ANDROID_5_0_LEVEL = 21 ANDROID_8_0_LEVEL = 26 +ANDROID_9_0_LEVEL = 28 +ANDROID_10_0_LEVEL = 29 ANDROID_MANIFEST_FILE = 'AndroidManifest.xml' - +ANDROID_API_LEVEL_MAP = { + '1': '1.0', + '2': '1.1', + '3': '1.5', + '4': '1.6', + '5': '2.0-2.1', + '8': '2.2-2.2.3', + '9': '2.3-2.3.2', + '10': '2.3.3-2.3.7', + '11': '3.0', + '12': '3.1', + '13': '3.2-3.2.6', + '14': '4.0-4.0.2', + '15': '4.0.3-4.0.4', + '16': '4.1-4.1.2', + '17': '4.2-4.2.2', + '18': '4.3-4.3.1', + '19': '4.4-4.4.4', + '20': '4.4W-4.4W.2', + '21': '5.0-5.0.2', + '22': '5.1-5.1.1', + '23': '6.0-6.0.1', + '24': '7.0', + '25': '7.1-7.1.2', + '26': '8.0', + '27': '8.1', + '28': '9', + '29': '10', + '30': '11', + '31': '12', + '32': '12L', + '33': '13', +} def get_browsable_activities(node, ns): """Get Browsable Activities.""" @@ -110,7 +144,12 @@ def manifest_analysis(mfxml, ns, man_data_dic, src_type, app_dir): # GENERAL if man_data_dic['min_sdk'] and int(man_data_dic['min_sdk']) < ANDROID_8_0_LEVEL: minsdk = man_data_dic.get('min_sdk') - ret_list.append(('vulnerable_os_version', (minsdk,), ())) + android_version = ANDROID_API_LEVEL_MAP.get(minsdk, 'XX') + ret_list.append(('vulnerable_os_version', (android_version, minsdk,), ())) + elif man_data_dic['min_sdk'] and int(man_data_dic['min_sdk']) < ANDROID_10_0_LEVEL: + minsdk = man_data_dic.get('min_sdk') + android_version = ANDROID_API_LEVEL_MAP.get(minsdk, 'XX') + ret_list.append(('vulnerable_os_version2', (android_version, minsdk,), ())) # APPLICATIONS # Handle multiple application tags in AAR backupDisabled = False @@ -174,30 +213,44 @@ def manifest_analysis(mfxml, ns, man_data_dic, src_type, app_dir): else: itemname = 'NIL' item = '' + if itemname in ['Activity', 'Activity-Alias']: + # Task Affinity + task_affinity = node.getAttribute(f'{ns}:taskAffinity') + if (task_affinity): + item = node.getAttribute(f'{ns}:name') + ret_list.append(('task_affinity_set', (item,), ())) - # Task Affinity - if ( - itemname in ['Activity', 'Activity-Alias'] and - node.getAttribute(f'{ns}:taskAffinity') - ): + # LaunchMode + try: + affected_sdk = int( + man_data_dic['min_sdk']) < ANDROID_5_0_LEVEL + except Exception: + # in case min_sdk is not defined we assume vulnerability + affected_sdk = True + launchmode = node.getAttribute(f'{ns}:launchMode') item = node.getAttribute(f'{ns}:name') - ret_list.append(('task_affinity_set', (item,), ())) + modes = ('singleTask', 'singleInstance') + if (affected_sdk + and launchmode in modes): + ret_list.append(('non_standard_launchmode', (item,), ())) - # LaunchMode - try: - affected_sdk = int( - man_data_dic['min_sdk']) < ANDROID_5_0_LEVEL - except Exception: - # in case min_sdk is not defined we assume vulnerability - affected_sdk = True + # Android Task Hijacking or StrandHogg 1.0 + try: + target_sdk = int(man_data_dic['target_sdk']) + except Exception: + target_sdk = ANDROID_8_0_LEVEL + if (target_sdk < ANDROID_9_0_LEVEL + and launchmode == 'singleTask'): + ret_list.append(('task_hijacking', (item,), ())) + + # Android StrandHogg 2.0 + exported_act = node.getAttribute(f'{ns}:exported') + if (target_sdk < ANDROID_10_0_LEVEL + and itemname in ['Activity', 'Activity-Alias'] + and exported_act == 'true' + and (launchmode != 'singleInstance' or task_affinity != '')): + ret_list.append(('task_hijacking2', (item,), ())) - if ( - affected_sdk and - itemname in ['Activity', 'Activity-Alias'] and - (node.getAttribute(f'{ns}:launchMode') == 'singleInstance' - or node.getAttribute(f'{ns}:launchMode') == 'singleTask')): - item = node.getAttribute(f'{ns}:name') - ret_list.append(('non_standard_launchmode', (item,), ())) # Exported Check item = '' is_inf = False diff --git a/mobsf/StaticAnalyzer/views/android/network_security.py b/mobsf/StaticAnalyzer/views/android/network_security.py index aadbf7d896..8befc1fc5c 100644 --- a/mobsf/StaticAnalyzer/views/android/network_security.py +++ b/mobsf/StaticAnalyzer/views/android/network_security.py @@ -4,6 +4,10 @@ from xml.dom import minidom from pathlib import Path +from mobsf.MobSF.utils import ( + is_path_traversal, +) + logger = logging.getLogger(__name__) HIGH = 'high' WARNING = 'warning' @@ -13,7 +17,7 @@ def read_netsec_config(app_dir, config, src_type): """Read the manifest file.""" - msg = 'Reading Network Security Config' + msg = 'Reading Network Security config' try: config_file = None config = config.replace('@xml/', '', 1) @@ -24,14 +28,20 @@ def read_netsec_config(app_dir, config, src_type): else: # APK xml_dir = base / 'apktool_out' / 'res' / 'xml' + 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) + return netsec_file.read_text('utf8', 'ignore') + # Couldn't find the file defined in manifest xmls = Path(xml_dir).glob('*.xml') for xml in xmls: - if xml.stem in [config, 'network_security_config']: + if 'network_security' in xml.stem: config_file = xml break if not config_file: return None - logger.info(msg) + logger.info('%s from %s', msg, config_file.name) return config_file.read_text('utf8', 'ignore') except Exception: logger.exception(msg) @@ -50,7 +60,7 @@ def analysis(app_dir, config, is_debuggable, src_type): netsec_conf = read_netsec_config(app_dir, config, src_type) if not netsec_conf: return netsec - logger.info('Parsing Network Security Config') + logger.info('Parsing Network Security config') parsed = minidom.parseString(netsec_conf) finds = [] summary = {HIGH: 0, WARNING: 0, INFO: 0, SECURE: 0} diff --git a/mobsf/StaticAnalyzer/views/common/binary/macho.py b/mobsf/StaticAnalyzer/views/common/binary/macho.py index 9d4ca34f05..a128b5192e 100644 --- a/mobsf/StaticAnalyzer/views/common/binary/macho.py +++ b/mobsf/StaticAnalyzer/views/common/binary/macho.py @@ -2,6 +2,7 @@ # coding=utf-8 import shutil import subprocess +from pathlib import Path import lief @@ -75,7 +76,12 @@ def checksec(self): 'to execute reliably.') else: severity = 'high' - if self.macho_name.endswith('.dylib'): + ext = Path(self.macho_name).suffix + # PIE check not applicable for static and dynamic libraries + # https://github.com/MobSF/Mobile-Security-Framework-MobSF/ + # issues/2290#issuecomment-1837272113 + if (ext == '.dylib' + or (not ext and '.framework' in self.macho_name)): severity = 'info' desc = ( 'The binary is built without Position ' @@ -86,7 +92,7 @@ def checksec(self): 'the address space positions of key data areas of a ' 'process, including the base of the executable and the ' 'positions of the stack,heap and libraries. Use compiler ' - 'option -fPIC to enable Position Independent Code.' + 'option -fPIC to enable Position Independent Code. ' 'Not applicable for dylibs and static libraries.') macho_dict['pie'] = { 'has_pie': has_pie, diff --git a/mobsf/StaticAnalyzer/views/ios/binary_rule_matcher.py b/mobsf/StaticAnalyzer/views/ios/binary_rule_matcher.py index 3fcb1bfcca..94b46a08f6 100644 --- a/mobsf/StaticAnalyzer/views/ios/binary_rule_matcher.py +++ b/mobsf/StaticAnalyzer/views/ios/binary_rule_matcher.py @@ -13,7 +13,7 @@ def get_desc(desc_str, match): """Generate formatted detailed description with matches.""" return desc_str.format( - b', '.join(list(set(match))).decode('utf-8', 'ignore')) + b', '.join(sorted(set(match))).decode('utf-8', 'ignore')) def _add_bfindings(findings, desc, detailed_desc, rule): diff --git a/mobsf/StaticAnalyzer/views/ios/code_analysis.py b/mobsf/StaticAnalyzer/views/ios/code_analysis.py index b50dd40a62..39de03388f 100755 --- a/mobsf/StaticAnalyzer/views/ios/code_analysis.py +++ b/mobsf/StaticAnalyzer/views/ios/code_analysis.py @@ -22,6 +22,19 @@ class _SourceType(Enum): nocode = 'No Code' +def merge_findings(swift, objc): + code_analysis = {} + # Add all unique keys + for k in swift: + if k in objc: + swift[k]['files'].update(objc[k]['files']) + code_analysis[k] = swift[k] + for k in objc: + if k not in code_analysis: + code_analysis[k] = objc[k] + return code_analysis + + def ios_source_analysis(src): """IOS Objective-C and Swift Code Analysis.""" try: @@ -40,20 +53,22 @@ def ios_source_analysis(src): source_types = set() # Code and API Analysis - code_findings = scan( + objc_findings = scan( objective_c_rules.as_posix(), {'.m'}, [src], settings.SKIP_CLASS_PATH) - if code_findings: + if objc_findings: source_types.add(_SourceType.objc) - code_findings.update(scan( + swift_findings = scan( swift_rules.as_posix(), {'.swift'}, [src], - settings.SKIP_CLASS_PATH)) - if code_findings: + settings.SKIP_CLASS_PATH) + if swift_findings: source_types.add(_SourceType.swift) + code_findings = merge_findings(swift_findings, objc_findings) + # API Analysis api_findings = scan( api_rules.as_posix(), {'.m', '.swift'}, diff --git a/mobsf/StaticAnalyzer/views/ios/file_analysis.py b/mobsf/StaticAnalyzer/views/ios/file_analysis.py index 688d93cca3..5615aef42c 100644 --- a/mobsf/StaticAnalyzer/views/ios/file_analysis.py +++ b/mobsf/StaticAnalyzer/views/ios/file_analysis.py @@ -2,9 +2,9 @@ """iOS File Analysis.""" import os -import re import shutil import logging +from pathlib import Path from django.utils.html import escape @@ -39,15 +39,15 @@ def ios_list_files(src, md5_hash, binary_form, mode): fileparam = file_path.replace(src, '') filez.append(fileparam) full_paths.append(file_path) - ext = jfile.split('.')[-1] - if re.search(r'cer|pem|cert|crt|pub|key|pfx|p12|der', ext): + ext = Path(jfile).suffix + if ext in ('.cer', '.pem', '.cert', '.crt', + '.pub', '.key', '.pfx', '.p12', '.der'): certz.append({ 'file_path': escape(file_path.replace(src, '')), 'type': None, 'hash': None, }) - - if re.search(r'^db$|^sqlitedb$|^sqlite$', ext): + if ext in ('.db', '.sqlitedb', '.sqlite', '.sqlite3'): database.append({ 'file_path': escape(fileparam), 'type': mode, diff --git a/mobsf/StaticAnalyzer/views/ios/rules/swift_rules.yaml b/mobsf/StaticAnalyzer/views/ios/rules/swift_rules.yaml index 909908a1dc..7b46a82980 100644 --- a/mobsf/StaticAnalyzer/views/ios/rules/swift_rules.yaml +++ b/mobsf/StaticAnalyzer/views/ios/rules/swift_rules.yaml @@ -117,7 +117,7 @@ type: Regex metadata: cvss: 7.5 - cwe: CWE-532 + cwe: cwe-532 masvs: storage-3 owasp-mobile: '' ref: https://github.com/MobSF/owasp-mstg/blob/master/Document/0x06i-Testing-Code-Quality-and-Build-Settings.md#finding-debugging-code-and-verbose-error-logging-mstg-code-4 @@ -131,7 +131,7 @@ type: RegexOr metadata: cvss: 5.9 - cwe: CWE-327 + cwe: cwe-327 masvs: crypto-4 owasp-mobile: m5 ref: https://github.com/MobSF/owasp-mstg/blob/master/Document/0x04g-Testing-Cryptography.md#identifying-insecure-andor-deprecated-cryptographic-algorithms-mstg-crypto-4 @@ -144,7 +144,7 @@ type: RegexOr metadata: cvss: 5.9 - cwe: CWE-327 + cwe: cwe-327 masvs: crypto-4 owasp-mobile: m5 ref: https://github.com/MobSF/owasp-mstg/blob/master/Document/0x04g-Testing-Cryptography.md#identifying-insecure-andor-deprecated-cryptographic-algorithms-mstg-crypto-4 @@ -157,7 +157,7 @@ type: RegexOr metadata: cvss: 5.9 - cwe: CWE-327 + cwe: cwe-327 masvs: crypto-4 owasp-mobile: m5 ref: https://github.com/MobSF/owasp-mstg/blob/master/Document/0x04g-Testing-Cryptography.md#identifying-insecure-andor-deprecated-cryptographic-algorithms-mstg-crypto-4 @@ -171,7 +171,7 @@ type: RegexOr metadata: cvss: 5.9 - cwe: CWE-327 + cwe: cwe-327 masvs: crypto-4 owasp-mobile: m5 ref: https://github.com/MobSF/owasp-mstg/blob/master/Document/0x04g-Testing-Cryptography.md#identifying-insecure-andor-deprecated-cryptographic-algorithms-mstg-crypto-4 @@ -185,7 +185,7 @@ type: RegexOr metadata: cvss: 5.9 - cwe: CWE-327 + cwe: cwe-327 masvs: crypto-4 owasp-mobile: m5 ref: https://github.com/MobSF/owasp-mstg/blob/master/Document/0x04g-Testing-Cryptography.md#identifying-insecure-andor-deprecated-cryptographic-algorithms-mstg-crypto-4 @@ -344,7 +344,7 @@ type: Regex metadata: cvss: 4.3 - cwe: CWE-311 + cwe: cwe-311 masvs: storage-1 owasp-mobile: m2 ref: https://github.com/MobSF/owasp-mstg/blob/master/Document/0x06d-Testing-Data-Storage.md#ios-data-storage @@ -410,7 +410,7 @@ type: RegexOr metadata: cvss: 0 - cwe: CWE-295 + cwe: cwe-295 masvs: network-4 owasp-mobile: m3 ref: https://github.com/MobSF/owasp-mstg/blob/master/Document/0x05g-Testing-Network-Communication.md#testing-custom-certificate-stores-and-certificate-pinning-mstg-network-4 @@ -422,7 +422,7 @@ type: Regex metadata: cvss: 0 - cwe: CWE-311 + cwe: cwe-311 masvs: storage-14 owasp-mobile: m2 ref: https://github.com/MobSF/owasp-mstg/blob/master/Document/0x06d-Testing-Data-Storage.md#realm-databases @@ -436,7 +436,7 @@ type: RegexAnd metadata: cvss: 0 - cwe: CWE-311 + cwe: cwe-311 masvs: storage-14 owasp-mobile: m2 ref: https://github.com/MobSF/owasp-mstg/blob/master/Document/0x06d-Testing-Data-Storage.md#coredata @@ -450,7 +450,7 @@ type: RegexAnd metadata: cvss: 0 - cwe: CWE-311 + cwe: cwe-311 masvs: storage-14 owasp-mobile: m2 ref: https://github.com/MobSF/owasp-mstg/blob/master/Document/0x06d-Testing-Data-Storage.md#realm-databases @@ -475,7 +475,7 @@ type: Regex metadata: cvss: 0 - cwe: CWE-939 + cwe: cwe-939 masvs: platform-3 owasp-mobile: m1 - id: ios_sensitive_backup @@ -517,7 +517,7 @@ type: RegexAndOr metadata: cvss: 7.5 - cwe: CWE-757 + cwe: cwe-757 masvs: network-2 owasp-mobile: m3 ref: https://github.com/MobSF/owasp-mstg/blob/master/Document/0x04f-Testing-Network-Communication.md#verifying-data-encryption-on-the-network-mstg-network-1-and-mstg-network-2 diff --git a/mobsf/templates/base/base_layout.html b/mobsf/templates/base/base_layout.html index 94ea285eed..d423d7446c 100644 --- a/mobsf/templates/base/base_layout.html +++ b/mobsf/templates/base/base_layout.html @@ -41,7 +41,7 @@ - + diff --git a/mobsf/templates/general/about.html b/mobsf/templates/general/about.html index e444650d90..64b2ce9164 100644 --- a/mobsf/templates/general/about.html +++ b/mobsf/templates/general/about.html @@ -14,7 +14,7 @@

About Mobile Security Framework

- 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. MobSF support mobile app binaries (APK, XAPK, IPA & APPX) along with zipped source code and provides REST APIs for seamless integration with your CI/CD or DevSecOps pipeline. The Dynamic Analyzer helps you to perform runtime security assessment and interactive instrumented testing. + 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. MobSF support mobile app binaries (APK, XAPK, IPA & APPX) along with zipped source code and provides APIs for seamless integration with your CI/CD or DevSecOps pipeline. The Dynamic Analyzer helps you to perform runtime security assessment and interactive instrumented testing.

Author: Ajin Abraham

Active Collaborators

diff --git a/mobsf/templates/general/apidocs.html b/mobsf/templates/general/apidocs.html index cc7105a58c..37c1d27882 100644 --- a/mobsf/templates/general/apidocs.html +++ b/mobsf/templates/general/apidocs.html @@ -19,9 +19,9 @@
-

REST API Docs

+

API Docs

- REST API Key: {{ api_key}} + API Key: {{ api_key}}

diff --git a/mobsf/templates/general/home.html b/mobsf/templates/general/home.html index 09204302f6..eba13a359d 100644 --- a/mobsf/templates/general/home.html +++ b/mobsf/templates/general/home.html @@ -35,7 +35,7 @@ DYNAMIC ANALYZER