- URLS INVOKED -
-Source | -URL | -
---|---|
- {{item.source}} - | -- {{item.url}} - | -
diff --git a/mobsf/DynamicAnalyzer/tools/frida_scripts_ios/dump/crypto.js b/mobsf/DynamicAnalyzer/tools/frida_scripts_ios/dump/crypto.js index 1e754009ba..52fb283f1b 100755 --- a/mobsf/DynamicAnalyzer/tools/frida_scripts_ios/dump/crypto.js +++ b/mobsf/DynamicAnalyzer/tools/frida_scripts_ios/dump/crypto.js @@ -17,25 +17,25 @@ Interceptor.attach(Module.findExportByName("libSystem.B.dylib","CCCrypt"), } if(ptr(args[3]) != 0 ) { - cccrypt['key'] = base64ArrayBuffer(Memory.readByteArray(ptr(args[3]),parseInt(args[4]))); + cccrypt['Key'] = base64ArrayBuffer(Memory.readByteArray(ptr(args[3]),parseInt(args[4]))); } else { - cccrypt['key'] = 0; + cccrypt['Key'] = 0; } if(ptr(args[5]) != 0 ) { - cccrypt['iv'] = base64ArrayBuffer(Memory.readByteArray(ptr(args[5]),16)); + cccrypt['IV'] = base64ArrayBuffer(Memory.readByteArray(ptr(args[5]),16)); } else { - cccrypt['iv'] = 0; + cccrypt['IV'] = 0; } this.dataInLength = parseInt(args[7]); if(ptr(args[6]) != 0 ) { - cccrypt['dataIn'] = base64ArrayBuffer(Memory.readByteArray(ptr(args[6]),this.dataInLength)) + cccrypt['dataInput'] = base64ArrayBuffer(Memory.readByteArray(ptr(args[6]),this.dataInLength)) } else { - cccrypt['dataIn'] = null; + cccrypt['dataInput'] = null; } this.dataOut = args[8]; @@ -47,10 +47,10 @@ Interceptor.attach(Module.findExportByName("libSystem.B.dylib","CCCrypt"), onLeave: function(retval) { var cccrypt_re = {}; if(ptr(this.dataOut) != 0 ) { - cccrypt_re['dataOut'] = base64ArrayBuffer(Memory.readByteArray(this.dataOut,parseInt(ptr(Memory.readU32(ptr(this.dataOutLength),4))))); + cccrypt_re['dataOutput'] = base64ArrayBuffer(Memory.readByteArray(this.dataOut,parseInt(ptr(Memory.readU32(ptr(this.dataOutLength),4))))); } else { - cccrypt_re['dataOut'] = null; + cccrypt_re['dataOutput'] = null; } send(JSON.stringify({'[MBSFDUMP] crypto': cccrypt_re})); @@ -69,16 +69,16 @@ Interceptor.attach(Module.findExportByName("libSystem.B.dylib","CCCryptorCreate" } if(ptr(args[3]) != 0 ) { - cccryptorcreate['key'] = base64ArrayBuffer(Memory.readByteArray(ptr(args[3]),parseInt(args[4]))); + cccryptorcreate['Key'] = base64ArrayBuffer(Memory.readByteArray(ptr(args[3]),parseInt(args[4]))); } else { - cccryptorcreate['key'] = 0; + cccryptorcreate['Key'] = 0; } if(ptr(args[5]) != 0 ) { - cccryptorcreate['iv'] = base64ArrayBuffer(Memory.readByteArray(ptr(args[5]),16)); + cccryptorcreate['IV'] = base64ArrayBuffer(Memory.readByteArray(ptr(args[5]),16)); } else { - cccryptorcreate['iv'] = 0; + cccryptorcreate['IV'] = 0; } send(JSON.stringify({'[MBSFDUMP] crypto': cccryptorcreate})); @@ -94,10 +94,10 @@ Interceptor.attach(Module.findExportByName("libSystem.B.dylib","CCCryptorUpdate" onEnter: function(args) { var cryptorupdate = {} if(ptr(args[1]) != 0) { - cryptorupdate['dataIn'] = base64ArrayBuffer(Memory.readByteArray(ptr(args[1]),parseInt(args[2]))); + cryptorupdate['dataInput'] = base64ArrayBuffer(Memory.readByteArray(ptr(args[1]),parseInt(args[2]))); } else { - cryptorupdate['dataIn'] = null; + cryptorupdate['dataInput'] = null; } //this.len = args[4]; @@ -110,9 +110,9 @@ Interceptor.attach(Module.findExportByName("libSystem.B.dylib","CCCryptorUpdate" onLeave: function(retval) { var cryptorupdate_re = {} if(ptr(this.out) != 0) { - cryptorupdate_re['dataOut'] = base64ArrayBuffer(Memory.readByteArray(this.out,parseInt(ptr(Memory.readU32(ptr(this.len),4))))) + cryptorupdate_re['dataOutput'] = base64ArrayBuffer(Memory.readByteArray(this.out,parseInt(ptr(Memory.readU32(ptr(this.len),4))))) } else { - cryptorupdate_re['dataOut'] = null; + cryptorupdate_re['dataOutput'] = null; } send(JSON.stringify({'[MBSFDUMP] crypto': cryptorupdate_re})); } @@ -129,9 +129,9 @@ Interceptor.attach(Module.findExportByName("libSystem.B.dylib","CCCryptorFinal") onLeave: function(retval) { var cccryptorfinal_re = {} if(ptr(this.out2) != 0) { - cccryptorfinal_re['dataOut'] = base64ArrayBuffer(Memory.readByteArray(this.out2,parseInt(ptr(Memory.readU32(ptr(this.len2),4))))) + cccryptorfinal_re['dataOutput'] = base64ArrayBuffer(Memory.readByteArray(this.out2,parseInt(ptr(Memory.readU32(ptr(this.len2),4))))) } else { - cccryptorfinal_re['dataOut'] = null; + cccryptorfinal_re['dataOutput'] = null; } send(JSON.stringify({'[MBSFDUMP] crypto': cccryptorfinal_re})); } diff --git a/mobsf/DynamicAnalyzer/views/android/analysis.py b/mobsf/DynamicAnalyzer/views/android/analysis.py index b46616d5c0..d9e9fd5123 100644 --- a/mobsf/DynamicAnalyzer/views/android/analysis.py +++ b/mobsf/DynamicAnalyzer/views/android/analysis.py @@ -211,8 +211,8 @@ def safe_extract(tar, path='.', tar.extractall(path, members, numeric_owner=numeric_owner) safe_extract(tar, untar_dir, members=safe_paths(tar)) - except FileExistsError: - pass + except (FileExistsError, tarfile.ReadError): + logger.warning('Failed to extract tar file') except Exception: logger.exception('Tar extraction failed') return True @@ -247,7 +247,7 @@ def get_app_files(apk_dir, package): all_files['plist'].append( {'type': 'plist', 'file': fileparam}) else: - with open(file_path, # lgtm [py/path-injection] + with open(file_path, 'r', encoding='ISO-8859-1') as flip: file_cnt_sig = flip.read(6) diff --git a/mobsf/DynamicAnalyzer/views/android/report.py b/mobsf/DynamicAnalyzer/views/android/report.py index f5e52a0b83..453e5bb770 100644 --- a/mobsf/DynamicAnalyzer/views/android/report.py +++ b/mobsf/DynamicAnalyzer/views/android/report.py @@ -147,7 +147,7 @@ def view_file(request, api=False): mode='r', encoding='ISO-8859-1') as flip: dat = flip.read() - if fil.endswith('.plist') and 'bplist00' in dat: + if fil.endswith('.plist') and dat.startswith('bplist0'): dat = writePlistToString(dat).decode('utf-8', 'ignore') if fil.endswith(('.xml', '.plist')) and typ in ['xml', 'plist']: rtyp = 'xml' diff --git a/mobsf/DynamicAnalyzer/views/ios/analysis.py b/mobsf/DynamicAnalyzer/views/ios/analysis.py index a1732de0c3..ea37f672ff 100644 --- a/mobsf/DynamicAnalyzer/views/ios/analysis.py +++ b/mobsf/DynamicAnalyzer/views/ios/analysis.py @@ -45,7 +45,7 @@ def get_logs_data(app_dir): def run_analysis(app_dir, checksum): """Run Dynamic File Analysis.""" analysis_result = {} - logger.info('iOS Dynamic File Analysis') + logger.info('Dynamic File Analysis') domains = {} # Collect Log data data = get_logs_data(app_dir) @@ -57,21 +57,22 @@ def run_analysis(app_dir, checksum): # Domain Extraction and Malware Check logger.info('Performing Malware Check on extracted Domains') domains = MalwareDomainCheck().scan(urls) - # Email Etraction Regex - emails = [] + emails = set() for email in EMAIL_REGEX.findall(data.lower()): - if (email not in emails) and (not email.startswith('//')): - emails.append(email) - pfiles = get_app_files( - app_dir, - f'{checksum}-app-container') + if email.startswith('//'): + continue + if email.endswith('.png'): + continue + emails.add(email) + # App data files analysis + pfiles = get_app_files(app_dir, f'{checksum}-app-container') analysis_result['sqlite'] = pfiles['sqlite'] analysis_result['plist'] = pfiles['plist'] analysis_result['others'] = pfiles['others'] analysis_result['urls'] = urls analysis_result['domains'] = domains - analysis_result['emails'] = emails + analysis_result['emails'] = list(emails) return analysis_result @@ -95,7 +96,7 @@ def ios_api_analysis(app_dir): dump_file = app_dir / 'mobsf_dump_file.txt' if not dump_file.exists(): return dump - logger.info('Frida Analyzing Dump data') + logger.info('Analyzing Frida Data Dump') data = dump_file.read_text( encoding='utf-8', errors='ignore').splitlines() diff --git a/mobsf/DynamicAnalyzer/views/ios/corellium_instance.py b/mobsf/DynamicAnalyzer/views/ios/corellium_instance.py index 7c31bf8438..f350250eee 100644 --- a/mobsf/DynamicAnalyzer/views/ios/corellium_instance.py +++ b/mobsf/DynamicAnalyzer/views/ios/corellium_instance.py @@ -257,7 +257,7 @@ def get_supported_models(request, api=False): if not apikey: data = { 'status': 'failed', - 'message': 'Missing corellium API key'} + 'message': 'Missing Corellium API key'} return send_response(data, api) cm = CorelliumModelsAPI(apikey) r = cm.get_models() @@ -280,7 +280,7 @@ def get_supported_os(request, api=False): model = request.POST['model'] apikey = getattr(settings, 'CORELLIUM_API_KEY', '') if not apikey: - data['message'] = 'Missing corellium API key' + data['message'] = 'Missing Corellium API key' return send_response(data, api) cm = CorelliumModelsAPI(apikey) r = cm.get_supported_os(model) diff --git a/mobsf/DynamicAnalyzer/views/ios/report.py b/mobsf/DynamicAnalyzer/views/ios/report.py index 1906451c80..db32182106 100644 --- a/mobsf/DynamicAnalyzer/views/ios/report.py +++ b/mobsf/DynamicAnalyzer/views/ios/report.py @@ -17,8 +17,10 @@ common_check, ) from mobsf.MobSF.utils import ( + base64_decode, is_md5, key, + pretty_json, print_n_send_error_response, replace, ) @@ -27,6 +29,8 @@ logger = logging.getLogger(__name__) register.filter('key', key) register.filter('replace', replace) +register.filter('pretty_json', pretty_json) +register.filter('base64_decode', base64_decode) def ios_view_report(request, checksum, api=False): diff --git a/mobsf/DynamicAnalyzer/views/ios/utils.py b/mobsf/DynamicAnalyzer/views/ios/utils.py index 2dce7ad7ac..d8642daae0 100644 --- a/mobsf/DynamicAnalyzer/views/ios/utils.py +++ b/mobsf/DynamicAnalyzer/views/ios/utils.py @@ -24,7 +24,7 @@ def common_check(instance_id): if not getattr(settings, 'CORELLIUM_API_KEY', ''): return { 'status': 'failed', - 'message': 'Missing corellium API key'} + 'message': 'Missing Corellium API key'} elif not is_instance_id(instance_id): return { 'status': 'failed', diff --git a/mobsf/MobSF/utils.py b/mobsf/MobSF/utils.py index 492a314bd5..7ed9d5b5b8 100755 --- a/mobsf/MobSF/utils.py +++ b/mobsf/MobSF/utils.py @@ -1,7 +1,9 @@ """Common Utils.""" import ast +import base64 import hashlib import io +import json import logging import ntpath import os @@ -31,6 +33,7 @@ logger = logging.getLogger(__name__) ADB_PATH = None +BASE64_REGEX = re.compile(r'^[-A-Za-z0-9+/]*={0,3}$') class Color(object): @@ -243,7 +246,7 @@ def python_dict(value): def is_base64(b_str): - return re.match('^[A-Za-z0-9+/]+[=]{0,2}$', b_str) + return BASE64_REGEX.match(b_str) def is_internet_available(): @@ -715,6 +718,31 @@ def replace(value, arg): return value.replace(what, to) +def pretty_json(value): + """Pretty print JSON.""" + try: + return json.dumps(json.loads(value), indent=4) + except Exception: + return value + + +def base64_decode(value): + """Try Base64 decode.""" + commonb64s = ('eyJ0') + decoded = None + try: + if is_base64(value) or value.startswith(commonb64s): + decoded = base64.b64decode( + value).decode('ISO-8859-1') + if set(decoded).difference(string.printable): + decoded = None + except Exception: + pass + if decoded: + return f'{value}\nBase64 Decoded: {decoded}' + return value + + def android_component(data): """Return Android component from data.""" cmp = '' diff --git a/mobsf/templates/dynamic_analysis/ios/dynamic_analyzer.html b/mobsf/templates/dynamic_analysis/ios/dynamic_analyzer.html index 5b34753b6e..502f7fe8dc 100644 --- a/mobsf/templates/dynamic_analysis/ios/dynamic_analyzer.html +++ b/mobsf/templates/dynamic_analysis/ios/dynamic_analyzer.html @@ -220,6 +220,10 @@
- URLs Invoked + UserDefaults Data
- User Defaults + Keychain Data
File Access @@ -81,48 +81,56 @@
- App Logs + App Data Directory
- Text Inputs + URLs Invoked
- Pasteboard + App Logs
- App Cookies + Text Inputs
- Screenshots + Pasteboard
+ App Cookies +
+ +Crypto Operations @@ -130,14 +138,29 @@
+ Credential Storage +
+ +- App Keychain + SQLite Queries +
+ ++ Screenshots
Trackers
Base64 Strings
- -XML Files
+Plist Files
- URLS INVOKED -
-Source | -URL | -
---|---|
- {{item.source}} - | -- {{item.url}} - | -
- USERDEFAULTS + USERDEFAULTS DATA