From 19f8ade1a04858dcbd765abafa2254c2caaa60b1 Mon Sep 17 00:00:00 2001 From: "Ajin.Abraham" Date: Wed, 29 Nov 2023 20:24:32 -0800 Subject: [PATCH] upload files to device --- mobsf/DynamicAnalyzer/forms.py | 6 + .../tools/onDevice/xposed/hooks.json | 389 --------- .../views/ios/corellium_apis.py | 66 +- .../views/ios/corellium_frida_ssh.py | 27 +- .../views/ios/corellium_instance.py | 42 +- .../views/ios/dynamic_analyzer.py | 2 + mobsf/MobSF/urls.py | 3 + .../ios/dynamic_analysis.html | 2 +- .../ios/dynamic_analyzer.html | 66 +- poetry.lock | 796 ++++++++---------- 10 files changed, 493 insertions(+), 906 deletions(-) create mode 100644 mobsf/DynamicAnalyzer/forms.py delete mode 100755 mobsf/DynamicAnalyzer/tools/onDevice/xposed/hooks.json diff --git a/mobsf/DynamicAnalyzer/forms.py b/mobsf/DynamicAnalyzer/forms.py new file mode 100644 index 0000000000..2695513011 --- /dev/null +++ b/mobsf/DynamicAnalyzer/forms.py @@ -0,0 +1,6 @@ +"""File upload to iOS form.""" +from django import forms + + +class UploadFileForm(forms.Form): + file = forms.FileField() diff --git a/mobsf/DynamicAnalyzer/tools/onDevice/xposed/hooks.json b/mobsf/DynamicAnalyzer/tools/onDevice/xposed/hooks.json deleted file mode 100755 index 820d152ca7..0000000000 --- a/mobsf/DynamicAnalyzer/tools/onDevice/xposed/hooks.json +++ /dev/null @@ -1,389 +0,0 @@ -{ - "hookConfigs": [ - { - "class_name": "android.telephony.TelephonyManager", - "method": "getDeviceId", - "thisObject": false, - "type": "fingerprint" - }, - { - "class_name": "android.telephony.TelephonyManager", - "method": "getSubscriberId", - "thisObject": false, - "type": "fingerprint" - }, - { - "class_name": "android.telephony.TelephonyManager", - "method": "getLine1Number", - "thisObject": false, - "type": "fingerprint" - }, - { - "class_name": "android.telephony.TelephonyManager", - "method": "getNetworkOperator", - "thisObject": false, - "type": "fingerprint" - }, - { - "class_name": "android.telephony.TelephonyManager", - "method": "getNetworkOperatorName", - "thisObject": false, - "type": "fingerprint" - }, - { - "class_name": "android.telephony.TelephonyManager", - "method": "getSimOperatorName", - "thisObject": false, - "type": "fingerprint" - }, - { - "class_name": "android.net.wifi.WifiInfo", - "method": "getMacAddress", - "thisObject": false, - "type": "fingerprint" - }, - { - "class_name": "android.telephony.TelephonyManager", - "method": "getSimCountryIso", - "thisObject": false, - "type": "fingerprint" - }, - { - "class_name": "android.telephony.TelephonyManager", - "method": "getSimSerialNumber", - "thisObject": false, - "type": "fingerprint" - }, - { - "class_name": "android.telephony.TelephonyManager", - "method": "getNetworkCountryIso", - "thisObject": false, - "type": "fingerprint" - }, - { - "class_name": "android.telephony.TelephonyManager", - "method": "getDeviceSoftwareVersion", - "thisObject": false, - "type": "fingerprint" - }, - { - "class_name": "android.os.Debug", - "method": "isDebuggerConnected", - "thisObject": false, - "type": "fingerprint" - }, - { - "class_name": "android.app.SharedPreferencesImpl$EditorImpl", - "method": "putString", - "thisObject": false, - "type": "globals" - }, - { - "class_name": "android.app.SharedPreferencesImpl$EditorImpl", - "method": "putBoolean", - "thisObject": false, - "type": "globals" - }, - { - "class_name": "android.app.SharedPreferencesImpl$EditorImpl", - "method": "putInt", - "thisObject": false, - "type": "globals" - }, - { - "class_name": "android.app.SharedPreferencesImpl$EditorImpl", - "method": "putLong", - "thisObject": false, - "type": "globals" - }, - { - "class_name": "android.app.SharedPreferencesImpl$EditorImpl", - "method": "putFloat", - "thisObject": false, - "type": "globals" - }, - { - "class_name": "android.content.ContentValues", - "method": "put", - "thisObject": false, - "type": "globals" - }, - { - "class_name": "java.net.URL", - "method": "openConnection", - "thisObject": true, - "type": "network" - }, - { - "class_name": "org.apache.http.impl.client.AbstractHttpClient", - "method": "execute", - "thisObject": false, - "type": "network" - }, - { - "class_name": "android.app.ContextImpl", - "method": "registerReceiver", - "thisObject": false, - "type": "binder" - }, - { - "class_name": "android.app.ActivityThread", - "method": "handleReceiver", - "thisObject": false, - "type": "binder" - }, - { - "class_name": "android.app.Activity", - "method": "startActivity", - "thisObject": false, - "type": "binder" - }, - { - "class_name": "dalvik.system.BaseDexClassLoader", - "method": "findResource", - "thisObject": false, - "type": "dex" - }, - { - "class_name": "dalvik.system.BaseDexClassLoader", - "method": "findLibrary", - "thisObject": false, - "type": "dex" - }, - { - "class_name": "dalvik.system.DexFile", - "method": "loadDex", - "thisObject": false, - "type": "dex" - }, - { - "class_name": "dalvik.system.DexClassLoader", - "method": null, - "thisObject": false, - "type": "dex" - }, - { - "class_name": "dalvik.system.BaseDexClassLoader", - "method": "findResources", - "thisObject": false, - "type": "dex" - }, - { - "class_name": "dalvik.system.DexFile", - "method": "loadClass", - "thisObject": false, - "type": "dex" - }, - { - "class_name": "dalvik.system.DexFile", - "method": null, - "thisObject": false, - "type": "dex" - }, - { - "class_name": "dalvik.system.PathClassLoader", - "method": null, - "thisObject": false, - "type": "dex" - }, - { - "class_name": "java.lang.reflect.Method", - "method": "invoke", - "thisObject": false, - "type": "reflection" - }, - { - "class_name": "javax.crypto.spec.SecretKeySpec", - "method": null, - "thisObject": false, - "type": "crypto" - }, - { - "class_name": "javax.crypto.Cipher", - "method": "doFinal", - "thisObject": true, - "type": "crypto" - }, - { - "class_name": "javax.crypto.Mac", - "method": "doFinal", - "thisObject": false, - "type": "crypto" - }, - { - "class_name": "android.app.ApplicationPackageManager", - "method": "setComponentEnabledSetting", - "thisObject": false, - "type": "generic" - }, - { - "class_name": "android.app.NotificationManager", - "method": "notify", - "thisObject": false, - "type": "generic" - }, - { - "class_name": "android.util.Base64", - "method": "decode", - "thisObject": false, - "type": "generic" - }, - { - "class_name": "android.telephony.TelephonyManager", - "method": "listen", - "thisObject": false, - "type": "generic" - }, - { - "class_name": "android.util.Base64", - "method": "encode", - "thisObject": false, - "type": "generic" - }, - { - "class_name": "android.util.Base64", - "method": "encodeToString", - "thisObject": false, - "type": "generic" - }, - { - "class_name": "android.net.ConnectivityManager", - "method": "setMobileDataEnabled", - "thisObject": false, - "type": "generic" - }, - { - "class_name": "android.content.BroadcastReceiver", - "method": "abortBroadcast", - "thisObject": false, - "type": "generic" - }, - { - "class_name": "android.telephony.SmsManager", - "method": "sendTextMessage", - "thisObject": false, - "type": "sms" - }, - { - "class_name": "android.telephony.SmsManager", - "method": "sendMultipartTextMessage", - "thisObject": false, - "type": "sms" - }, - { - "class_name": "java.lang.Runtime", - "method": "exec", - "thisObject": false, - "type": "runtime" - }, - { - "class_name": "java.lang.ProcessBuilder", - "method": "start", - "thisObject": true, - "type": "runtime" - }, - { - "class_name": "java.io.FileOutputStream", - "method": "write", - "thisObject": false, - "type": "runtime" - }, - { - "class_name": "java.io.FileInputStream", - "method": "read", - "thisObject": false, - "type": "runtime" - }, - { - "class_name": "android.app.ActivityManager", - "method": "killBackgroundProcesses", - "thisObject": false, - "type": "runtime" - }, - { - "class_name": "android.os.Process", - "method": "killProcess", - "thisObject": false, - "type": "runtime" - }, - { - "class_name": "android.content.ContentResolver", - "method": "query", - "thisObject": false, - "type": "content" - }, - { - "class_name": "android.content.ContentResolver", - "method": "registerContentObserver", - "thisObject": false, - "type": "content" - }, - { - "class_name": "android.content.ContentResolver", - "method": "insert", - "thisObject": false, - "type": "content" - }, - { - "class_name": "android.accounts.AccountManager", - "method": "getAccountsByType", - "thisObject": false, - "type": "content" - }, - { - "class_name": "android.accounts.AccountManager", - "method": "getAccounts", - "thisObject": false, - "type": "content" - }, - { - "class_name": "android.location.Location", - "method": "getLatitude", - "thisObject": false, - "type": "content" - }, - { - "class_name": "android.location.Location", - "method": "getLongitude", - "thisObject": false, - "type": "content" - }, - { - "class_name": "android.content.ContentResolver", - "method": "delete", - "thisObject": false, - "type": "content" - }, - { - "class_name": "android.media.AudioRecord", - "method": "startRecording", - "thisObject": false, - "type": "content" - }, - { - "class_name": "android.media.MediaRecorder", - "method": "start", - "thisObject": false, - "type": "content" - }, - { - "class_name": "android.os.SystemProperties", - "method": "get", - "thisObject": false, - "type": "content" - }, - { - "class_name": "android.app.ApplicationPackageManager", - "method": "getInstalledPackages", - "thisObject": false, - "type": "content" - }, - { - "class_name": "libcore.io.IoBridge", - "method": "open", - "thisObject": false, - "type": "file" - } - ], - "trace": false -} \ No newline at end of file diff --git a/mobsf/DynamicAnalyzer/views/ios/corellium_apis.py b/mobsf/DynamicAnalyzer/views/ios/corellium_apis.py index def96c6a0e..bd9ab30bba 100644 --- a/mobsf/DynamicAnalyzer/views/ios/corellium_apis.py +++ b/mobsf/DynamicAnalyzer/views/ios/corellium_apis.py @@ -210,45 +210,6 @@ def poll_instance(self): return r.json() return False - def agent_ready(self): - """Check if corellium agent is ready.""" - r = requests.get( - f'{self.api}/instances/{self.instance_id}/agent/v1/app/ready', - headers=self.headers) - if r.status_code in SUCCESS_RESP: - return r.json()['ready'] - return False - - def unlock_instance(self): - """Unlock the instance.""" - r = requests.post( - f'{self.api}/instances/{self.instance_id}/agent/v1/system/unlock', - headers=self.headers) - return r.status_code in SUCCESS_RESP - - def list_apps(self): - """List all apps installed.""" - r = requests.get( - f'{self.api}/instances/{self.instance_id}/agent/v1/app/apps', - headers=self.headers) - if r.status_code in SUCCESS_RESP: - return r.json() - elif r.status_code in ERROR_RESP: - return r.json() - return False - - def get_icons(self, bundleids): - """Get app icons by bundleId.""" - r = requests.get( - (f'{self.api}/instances/{self.instance_id}' - f'/agent/v1/app/icons?{bundleids}'), - headers=self.headers) - if r.status_code in SUCCESS_RESP: - return r.json() - elif r.status_code in ERROR_RESP: - return r.json() - return False - def screenshot(self): """Take screenshot inside VM.""" r = requests.get( @@ -422,8 +383,8 @@ def agent_ready(self): return r.json()['error'] return False - def unlock_instance(self): - """Unlock instance.""" + def unlock_device(self): + """Unlock iOS device.""" r = requests.post( f'{self.api}/instances/{self.instance_id}/agent/v1/system/unlock', headers=self.headers) @@ -499,3 +460,26 @@ def remove_app(self, bundle_id): return OK logger.error('Failed to remove the app. %s', r.json()['error']) return r.json()['error'] + + def list_apps(self): + """List all apps installed.""" + r = requests.get( + f'{self.api}/instances/{self.instance_id}/agent/v1/app/apps', + headers=self.headers) + if r.status_code in SUCCESS_RESP: + return r.json() + elif r.status_code in ERROR_RESP: + return r.json() + return False + + def get_icons(self, bundleids): + """Get app icons by bundleId.""" + r = requests.get( + (f'{self.api}/instances/{self.instance_id}' + f'/agent/v1/app/icons?{bundleids}'), + headers=self.headers) + if r.status_code in SUCCESS_RESP: + return r.json() + elif r.status_code in ERROR_RESP: + return r.json() + return False diff --git a/mobsf/DynamicAnalyzer/views/ios/corellium_frida_ssh.py b/mobsf/DynamicAnalyzer/views/ios/corellium_frida_ssh.py index 018c2a606a..1fbcd8e80e 100644 --- a/mobsf/DynamicAnalyzer/views/ios/corellium_frida_ssh.py +++ b/mobsf/DynamicAnalyzer/views/ios/corellium_frida_ssh.py @@ -22,11 +22,10 @@ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # Modified for MobSF. -import os import logging import socketserver import select - +from pathlib import Path import paramiko @@ -146,10 +145,7 @@ def ssh_jumphost_port_forward(ssh_string): target, _jumpbox = ssh_jump_host(ssh_string) # Frida port forward_port = 27042 - if os.getenv('MOBSF_PLATFORM') == 'docker': - remote_host = 'host.docker.internal' - else: - remote_host = '127.0.0.1' + remote_host = '127.0.0.1' forward_tunnel( forward_port, remote_host, @@ -166,3 +162,22 @@ def ssh_execute_cmd(target, cmd): stdout = _stdout.read().decode(encoding='utf-8', errors='ignore') stderr = _stderr.read().decode(encoding='utf-8', errors='ignore') return f'{stdout}\n{stderr}' + + +def ssh_file_upload(ssh_conn_string, fobject, fname): + """File Upload over SSH.""" + target, jumpbox = ssh_jump_host(ssh_conn_string) + with target.open_sftp() as sftp: + rfile = Path(fname.replace('..', '')).name + sftp.putfo(fobject, f'/tmp/{rfile}') + target.close() + jumpbox.close() + + +def ssh_file_download(ssh_conn_string, remote_path, local_path): + """File Download over SSH.""" + target, jumpbox = ssh_jump_host(ssh_conn_string) + with target.open_sftp() as sftp: + sftp.get(remote_path, local_path) + target.close() + jumpbox.close() diff --git a/mobsf/DynamicAnalyzer/views/ios/corellium_instance.py b/mobsf/DynamicAnalyzer/views/ios/corellium_instance.py index da06f99e7c..d497b19152 100644 --- a/mobsf/DynamicAnalyzer/views/ios/corellium_instance.py +++ b/mobsf/DynamicAnalyzer/views/ios/corellium_instance.py @@ -24,12 +24,14 @@ print_n_send_error_response, strict_package_check, ) +from mobsf.DynamicAnalyzer.forms import UploadFileForm from mobsf.DynamicAnalyzer.views.common.shared import ( invalid_params, send_response, ) from mobsf.DynamicAnalyzer.views.ios.corellium_frida_ssh import ( ssh_execute_cmd, + ssh_file_upload, ssh_jump_host, ) from mobsf.DynamicAnalyzer.views.ios.corellium_apis import ( @@ -200,9 +202,9 @@ def list_apps(request, api=False): if failed: return send_response(failed, api) apikey = getattr(settings, 'CORELLIUM_API_KEY', '') - ci = CorelliumInstanceAPI(apikey, instance_id) + ca = CorelliumAgentAPI(apikey, instance_id) # Get apps in device - r = ci.list_apps() + r = ca.list_apps() app_list = [] bundle_ids = [] if r and r.get('apps'): @@ -217,7 +219,7 @@ def list_apps(request, api=False): return send_response(failed, api) # Get app icons logger.info('Getting all application icons') - ic = ci.get_icons('&'.join(bundle_ids)) + ic = ca.get_icons('&'.join(bundle_ids)) for i in r.get('apps'): bundleid = i['bundleID'] checksum = get_md5(bundleid.encode('utf-8')) @@ -398,7 +400,7 @@ def setup_environment(request, checksum, api=False): ', please wait.') return send_response(data, api) # Unlock iOS Device - ca.unlock_instance() + ca.unlock_device() # Upload IPA ipa_path = Path(settings.UPLD_DIR) / checksum / f'{checksum}.ipa' msg = ca.upload_ipa(ipa_path) @@ -448,7 +450,7 @@ def run_app(request, api=False): return send_response(data, api) ca = CorelliumAgentAPI(apikey, instance_id) if (ca.agent_ready() - and ca.unlock_instance() + and ca.unlock_device() and ca.run_app(bundle_id) == OK): data['status'] = OK data['message'] = 'App Started' @@ -811,3 +813,33 @@ def system_logs(request, api=False): data['message'] = str(exp) return send_response(data) return print_n_send_error_response(request, err, api) +# AJAX + + +@require_http_methods(['POST']) +def upload_file(request, api=False): + """Upload files to device.""" + err_msg = 'Failed to upload file' + data = { + 'status': 'failed', + 'message': err_msg, + } + try: + instance_id = request.POST['instance_id'] + failed = common_check(instance_id) + if failed: + return send_response(failed, api) + apikey = getattr(settings, 'CORELLIUM_API_KEY', '') + form = UploadFileForm(request.POST, request.FILES) + if form.is_valid(): + ci = CorelliumInstanceAPI(apikey, instance_id) + fobject = request.FILES['file'] + ssh_file_upload( + ci.get_ssh_connection_string(), + fobject, + fobject.name) + data = {'status': 'ok'} + except Exception as exp: + logger.exception(err_msg) + data['message'] = str(exp) + return send_response(data) diff --git a/mobsf/DynamicAnalyzer/views/ios/dynamic_analyzer.py b/mobsf/DynamicAnalyzer/views/ios/dynamic_analyzer.py index c1aae14750..b8c6dc7d65 100644 --- a/mobsf/DynamicAnalyzer/views/ios/dynamic_analyzer.py +++ b/mobsf/DynamicAnalyzer/views/ios/dynamic_analyzer.py @@ -14,6 +14,7 @@ strict_package_check, ) from mobsf.StaticAnalyzer.models import StaticAnalyzerIOS +from mobsf.DynamicAnalyzer.forms import UploadFileForm from mobsf.DynamicAnalyzer.views.ios.corellium_apis import ( CorelliumAPI, ) @@ -97,6 +98,7 @@ def dynamic_analyzer(request, api=False): 'instance_id': instance_id, 'bundle_id': bundleid, 'version': settings.MOBSF_VER, + 'form': UploadFileForm(), 'title': 'iOS Dynamic Analyzer'} template = 'dynamic_analysis/ios/dynamic_analyzer.html' if api: diff --git a/mobsf/MobSF/urls.py b/mobsf/MobSF/urls.py index 73fa9ba221..bfd4e37cda 100755 --- a/mobsf/MobSF/urls.py +++ b/mobsf/MobSF/urls.py @@ -250,6 +250,9 @@ re_path(r'^ios/ssh_execute/$', instance.ssh_execute, name='ssh_execute'), + re_path(r'^ios/upload_file/$', + instance.upload_file, + name='upload_file'), re_path(r'^ios/touch/$', instance.touch, name='ios_touch'), diff --git a/mobsf/templates/dynamic_analysis/ios/dynamic_analysis.html b/mobsf/templates/dynamic_analysis/ios/dynamic_analysis.html index d8ae206cc2..d72311e0a3 100644 --- a/mobsf/templates/dynamic_analysis/ios/dynamic_analysis.html +++ b/mobsf/templates/dynamic_analysis/ios/dynamic_analysis.html @@ -34,7 +34,7 @@

MobSF iOS Dynamic Analyzer

{% if not corellium_auth %}
Cannot authenticate with Corellium. Please ensure that MOBSF_CORELLIUM_API_KEY is configured.
{% endif %} -
Correllium Project ID: {{ project_id }}
+
Corellium Project ID: {{ project_id }}
Refresh Create VM diff --git a/mobsf/templates/dynamic_analysis/ios/dynamic_analyzer.html b/mobsf/templates/dynamic_analysis/ios/dynamic_analyzer.html index ca3fdb711f..95f758df79 100644 --- a/mobsf/templates/dynamic_analysis/ios/dynamic_analyzer.html +++ b/mobsf/templates/dynamic_analysis/ios/dynamic_analyzer.html @@ -10,6 +10,7 @@ +