def start_activity(request, api=False): """Lunch a specific activity.""" try: env = Environment() activity = request.POST['activity'] md5_hash = request.POST['hash'] valid_md5 = is_md5(md5_hash) valid_act = re.match(r'^[\w]+(\.[\w]+)*$', activity) if not valid_act or not valid_md5: return invalid_params(api) app_dir = os.path.join(settings.UPLD_DIR, md5_hash + '/') screen_dir = os.path.join(app_dir, 'screenshots-apk/') if not os.path.exists(screen_dir): os.makedirs(screen_dir) logger.info('Launching Activity - %s', activity) outfile = ('{}act-{}.png'.format(screen_dir, activity)) static_android_db = StaticAnalyzerAndroid.objects.get(MD5=md5_hash) package = static_android_db.PACKAGE_NAME env.launch_n_capture(package, activity, outfile) data = {'status': 'ok'} except Exception as exp: logger.exception('Start Activity') data = {'status': 'failed', 'message': str(exp)} return send_response(data, api)
def tls_tests(request, api=False): """Perform TLS tests.""" logger.info('Running TLS/SSL Security tests') data = {} package = None try: test_duration = 25 test_package = 'tls_tests' env = Environment() md5_hash = request.POST['hash'] if not is_md5(md5_hash): return invalid_params(api) package = get_package_name(md5_hash) if not package: data = { 'status': 'failed', 'message': 'App details not found in database' } return send_response(data, api) res = run_tls_tests( request, md5_hash, env, package, test_package, test_duration, ) data = {'status': 'ok', 'tls_tests': res} except Exception as exp: logger.exception('Checking Application Security in Transit') data = {'status': 'failed', 'message': str(exp)} finally: logger.info('Test Completed. Resuming HTTPS Proxy') env.configure_proxy(package, request) return send_response(data, api)
def mobsfy(request, api=False): """Configure Instance for Dynamic Analysis.""" logger.info('MobSFying Android instance') data = {} msg = 'Connection failed' try: identifier = request.POST['identifier'] if cmd_injection_check(identifier): # Additional Check, not required data = { 'status': 'failed', 'message': 'Command Injection Detected', } return send_response(data, api) create_env = Environment(identifier) if not create_env.connect_n_mount(): data = {'status': 'failed', 'message': msg} return send_response(data, api) version = create_env.mobsfy_init() if not version: data = {'status': 'failed', 'message': msg} return send_response(data, api) else: data = {'status': 'ok', 'android_version': version} except Exception as exp: logger.exception('MobSFying Android instance failed') data = {'status': 'failed', 'message': str(exp)} return send_response(data, api)
def take_screenshot(request, api=False): """Take Screenshot.""" logger.info('Taking screenshot') data = {} try: env = Environment() bin_hash = request.POST['hash'] if not is_md5(bin_hash): return invalid_params(api) data = {} rand_int = random.randint(1, 1000000) screen_dir = os.path.join(settings.UPLD_DIR, bin_hash + '/screenshots-apk/') if not os.path.exists(screen_dir): os.makedirs(screen_dir) outile = '{}screenshot-{}.png'.format( screen_dir, str(rand_int)) env.screen_shot(outile) logger.info('Screenshot captured') data = {'status': 'ok'} except Exception as exp: logger.exception('Taking screenshot') data = {'status': 'failed', 'message': str(exp)} return send_response(data, api)
def dynamic_analysis(request, api=False): """Android Dynamic Analysis Entry point.""" try: scan_apps = [] device_packages = {} and_ver = None and_sdk = None apks = StaticAnalyzerAndroid.objects.filter( APP_TYPE='apk') for apk in reversed(apks): temp_dict = { 'ICON_FOUND': apk.ICON_FOUND, 'MD5': apk.MD5, 'APP_NAME': apk.APP_NAME, 'VERSION_NAME': apk.VERSION_NAME, 'FILE_NAME': apk.FILE_NAME, 'PACKAGE_NAME': apk.PACKAGE_NAME, } scan_apps.append(temp_dict) try: identifier = get_device() except Exception: msg = ('Is Android VM running? MobSF cannot' ' find android instance identifier.' ' Please run an android instance and refresh' ' this page. If this error persists,' ' set ANALYZER_IDENTIFIER in ' f'{get_config_loc()}') return print_n_send_error_response(request, msg, api) try: if identifier: env = Environment(identifier) device_packages = env.get_device_packages() pkg_file = Path(settings.DWD_DIR) / 'packages.json' with pkg_file.open('w', encoding='utf-8') as target: dump(device_packages, target) and_ver = env.get_android_version() and_sdk = env.get_android_sdk() except Exception: pass context = {'apps': scan_apps, 'identifier': identifier, 'android_version': and_ver, 'android_sdk': and_sdk, 'proxy_ip': get_proxy_ip(identifier), 'proxy_port': settings.PROXY_PORT, 'settings_loc': get_config_loc(), 'device_packages': device_packages, 'title': 'MobSF Dynamic Analysis', 'version': settings.MOBSF_VER} if api: return context template = 'dynamic_analysis/dynamic_analysis.html' return render(request, template, context) except Exception as exp: logger.exception('Dynamic Analysis') return print_n_send_error_response(request, exp, api)
def download_xposed_log(apk_dir): """Download Xposed Output.""" env = Environment() xposed_out = ('/data/data/' 'de.robv.android.xposed.installer' '/log/error.log') env.adb_command(['pull', xposed_out, apk_dir + 'x_logcat.txt']) logger.info('Downloading droidmon API monitor logs')
def download_data(request, api=False): """Download Application Data from Device.""" logger.info('Downloading app data') data = {} try: env = Environment() md5_hash = request.POST['hash'] if not is_md5(md5_hash): return invalid_params(api) package = get_package_name(md5_hash) if not package: data = { 'status': 'failed', 'message': 'App details not found in database' } return send_response(data, api) apk_dir = os.path.join(settings.UPLD_DIR, md5_hash + '/') httptools_url = get_http_tools_url(request) stop_httptools(httptools_url) files_loc = '/data/local/' logger.info('Archiving files created by app') env.adb_command([ 'tar', '-cvf', files_loc + package + '.tar', '/data/data/' + package + '/' ], True) logger.info('Downloading Archive') env.adb_command( ['pull', files_loc + package + '.tar', apk_dir + package + '.tar']) logger.info('Stopping ADB server') env.adb_command(['kill-server']) data = {'status': 'ok'} except Exception as exp: logger.exception('Downloading application data') data = {'status': 'failed', 'message': str(exp)} return send_response(data, api)
def get_component(request): """Get Android Component.""" data = {} try: env = Environment() comp = request.POST['component'] bin_hash = request.POST['hash'] if is_attack_pattern(comp) or not is_md5(bin_hash): return invalid_params() comp = env.android_component(bin_hash, comp) data = {'status': 'ok', 'message': comp} except Exception as exp: logger.exception('Getting Android Component') data = {'status': 'failed', 'message': str(exp)} return send_response(data)
def activity_tester(request, api=False): """Exported & non exported activity Tester.""" data = {} try: env = Environment() test = request.POST['test'] md5_hash = request.POST['hash'] if not is_md5(md5_hash): return invalid_params(api) app_dir = os.path.join(settings.UPLD_DIR, md5_hash + '/') screen_dir = os.path.join(app_dir, 'screenshots-apk/') if not os.path.exists(screen_dir): os.makedirs(screen_dir) static_android_db = StaticAnalyzerAndroid.objects.get(MD5=md5_hash) package = static_android_db.PACKAGE_NAME iden = '' if test == 'exported': iden = 'Exported ' logger.info('Exported activity tester') activities = python_list(static_android_db.EXPORTED_ACTIVITIES) else: logger.info('Activity tester') activities = python_list(static_android_db.ACTIVITIES) logger.info('Fetching %sactivities for %s', iden, package) if not activities: msg = 'No {}Activites found'.format(iden) logger.info(msg) data = {'status': 'failed', 'message': msg} return send_response(data, api) act_no = 0 logger.info('Starting %sActivity Tester...', iden) logger.info('%s %sActivities Identified', str(len(activities)), iden) for activity in activities: act_no += 1 logger.info('Launching %sActivity - %s. %s', iden, str(act_no), activity) if test == 'exported': file_iden = 'expact' else: file_iden = 'act' outfile = ('{}{}-{}.png'.format(screen_dir, file_iden, act_no)) env.launch_n_capture(package, activity, outfile) data = {'status': 'ok'} except Exception as exp: logger.exception('%sActivity tester', iden) data = {'status': 'failed', 'message': str(exp)} return send_response(data, api)
def run_apk(request): """Run Android APK.""" data = {} try: env = Environment() md5_hash = request.POST['hash'] if not is_md5(md5_hash): return invalid_params() pkg = get_package_name(md5_hash) if not pkg: return invalid_params() env.run_app(pkg) data = {'status': 'ok'} except Exception as exp: logger.exception('Running the App') data = {'status': 'failed', 'message': str(exp)} return send_response(data)
def trigger_static_analysis(request, checksum): """On device APK Static Analysis.""" try: identifier = None if not is_md5(checksum): return print_n_send_error_response( request, 'Invalid MD5') package = get_package_name(checksum) if not package: return print_n_send_error_response( request, 'Cannot get package name from checksum') try: identifier = get_device() except Exception: pass if not identifier: err = 'Cannot connect to Android Runtime' return print_n_send_error_response(request, err) env = Environment(identifier) apk_file = env.get_apk(checksum, package) if not apk_file: err = 'Failed to download APK file' return print_n_send_error_response(request, err) data = { 'analyzer': 'static_analyzer', 'status': 'success', 'hash': checksum, 'scan_type': 'apk', 'file_name': f'{package}.apk', } add_to_recent_scan(data) return HttpResponseRedirect( f'/static_analyzer/?name=' # lgtm [py/url-redirection] f'{package}.apk&checksum={checksum}' f'&type=apk') except Exception: msg = 'On device APK Static Analysis' logger.exception(msg) return print_n_send_error_response(request, msg)
def connect(self): """Connect to Frida Server.""" session = None device = None try: env = Environment() self.clean_up() env.run_frida_server() device = frida.get_device(get_device(), settings.FRIDA_TIMEOUT) pid = device.spawn([self.package]) logger.info('Spawning %s', self.package) session = device.attach(pid) time.sleep(2) except frida.ServerNotRunningError: logger.warning('Frida server is not running') self.connect() except frida.TimedOutError: logger.error('Timed out while waiting for device to appear') except (frida.ProcessNotFoundError, frida.TransportError, frida.InvalidOperationError): pass except Exception: logger.exception('Error Connecting to Frida') try: if session: script = session.create_script(self.get_script()) script.on('message', self.frida_response) script.load() device.resume(pid) sys.stdin.read() script.unload() session.detach() except (frida.ProcessNotFoundError, frida.TransportError, frida.InvalidOperationError): pass except Exception: logger.exception('Error Connecting to Frida')
def screen_cast(request): """ScreenCast.""" data = {} try: env = Environment() trd = threading.Thread(target=env.screen_stream) trd.daemon = True trd.start() data = {'status': 'ok'} except Exception as exp: logger.exception('Screen streaming') data = {'status': 'failed', 'message': str(exp)} return send_response(data)
def collect_logs(request, api=False): """Collecting Data and Cleanup.""" logger.info('Collecting Data and Cleaning Up') data = {} try: env = Environment() md5_hash = request.POST['hash'] if not is_md5(md5_hash): return invalid_params(api) package = get_package_name(md5_hash) if not package: data = { 'status': 'failed', 'message': 'App details not found in database' } return send_response(data, api) apk_dir = os.path.join(settings.UPLD_DIR, md5_hash + '/') lout = os.path.join(apk_dir, 'logcat.txt') dout = os.path.join(apk_dir, 'dump.txt') logger.info('Downloading logcat logs') logcat = env.adb_command(['logcat', '-d', package + ':V', '*:*']) with open(lout, 'wb') as flip: flip.write(logcat) logger.info('Downloading dumpsys logs') dumpsys = env.adb_command(['dumpsys'], True) with open(dout, 'wb') as flip: flip.write(dumpsys) if env.get_android_version() < 5: download_xposed_log(apk_dir) env.adb_command(['am', 'force-stop', package], True) logger.info('Stopping app') # Unset Global Proxy env.unset_global_proxy() data = {'status': 'ok'} except Exception as exp: logger.exception('Data Collection & Clean Up failed') data = {'status': 'failed', 'message': str(exp)} return send_response(data, api)
def touch(request): """Sending Touch Events.""" data = {} try: env = Environment() x_axis = request.POST['x'] y_axis = request.POST['y'] if not is_number(x_axis) and not is_number(y_axis): logger.error('Axis parameters must be numbers') return invalid_params() args = ['input', 'tap', x_axis, y_axis] trd = threading.Thread(target=env.adb_command, args=(args, True)) trd.daemon = True trd.start() data = {'status': 'ok'} except Exception as exp: logger.exception('Sending Touch Events') data = {'status': 'failed', 'message': str(exp)} return send_response(data)
def global_proxy(request, api=False): """Set/unset global proxy.""" data = {} try: env = Environment() version = env.get_android_version() action = request.POST['action'] if action == 'set': env.set_global_proxy(version) data = {'status': 'ok', 'message': 'set'} elif action == 'unset': env.unset_global_proxy() data = {'status': 'ok', 'message': 'unset'} else: data = {'status': 'failed', 'message': 'Action not supported'} except Exception as exp: logger.exception('MobSF Global Proxy') data = {'status': 'failed', 'message': str(exp)} return send_response(data, api)
def mobsf_ca(request, api=False): """Install and Remove MobSF Proxy RootCA.""" data = {} try: env = Environment() action = request.POST['action'] if action == 'install': env.install_mobsf_ca(action) data = {'status': 'ok', 'message': 'installed'} elif action == 'remove': env.install_mobsf_ca(action) data = {'status': 'ok', 'message': 'removed'} else: data = {'status': 'failed', 'message': 'Action not supported'} except Exception as exp: logger.exception('MobSF RootCA Handler') data = {'status': 'failed', 'message': str(exp)} return send_response(data, api)
def dynamic_analyzer(request, checksum, api=False): """Android Dynamic Analyzer Environment.""" logger.info('Creating Dynamic Analysis Environment') try: no_device = False if not is_md5(checksum): # We need this check since checksum is not validated # in REST API return print_n_send_error_response(request, 'Invalid Parameters', api) package = get_package_name(checksum) if not package: return print_n_send_error_response(request, 'Invalid Parameters', api) try: identifier = get_device() except Exception: no_device = True if no_device or not identifier: msg = ('Is the android instance running? MobSF cannot' ' find android instance identifier. ' 'Please run an android instance and refresh' ' this page. If this error persists,' ' set ANALYZER_IDENTIFIER in ' f'{get_config_loc()}') return print_n_send_error_response(request, msg, api) env = Environment(identifier) if not env.connect_n_mount(): msg = 'Cannot Connect to ' + identifier return print_n_send_error_response(request, msg, api) version = env.get_android_version() logger.info('Android Version identified as %s', version) xposed_first_run = False if not env.is_mobsfyied(version): msg = ('This Android instance is not MobSfyed/Outdated.\n' 'MobSFying the android runtime environment') logger.warning(msg) if not env.mobsfy_init(): return print_n_send_error_response( request, 'Failed to MobSFy the instance', api) if version < 5: xposed_first_run = True if xposed_first_run: msg = ('Have you MobSFyed the instance before' ' attempting Dynamic Analysis?' ' Install Framework for Xposed.' ' Restart the device and enable' ' all Xposed modules. And finally' ' restart the device once again.') return print_n_send_error_response(request, msg, api) # Clean up previous analysis env.dz_cleanup(checksum) # Configure Web Proxy env.configure_proxy(package) # Supported in Android 5+ env.enable_adb_reverse_tcp(version) # Apply Global Proxy to device env.set_global_proxy(version) # Start Clipboard monitor env.start_clipmon() # Get Screen Resolution screen_width, screen_height = env.get_screen_res() apk_path = Path(settings.UPLD_DIR) / checksum / f'{checksum}.apk' # Install APK status, output = env.install_apk(apk_path.as_posix(), package) if not status: # Unset Proxy env.unset_global_proxy() msg = (f'This APK cannot be installed. Is this APK ' f'compatible the Android VM/Emulator?\n{output}') return print_n_send_error_response(request, msg, api) logger.info('Testing Environment is Ready!') context = { 'screen_witdth': screen_width, 'screen_height': screen_height, 'package': package, 'hash': checksum, 'android_version': version, 'version': settings.MOBSF_VER, 'title': 'Dynamic Analyzer' } template = 'dynamic_analysis/android/dynamic_analyzer.html' if api: return context return render(request, template, context) except Exception: logger.exception('Dynamic Analyzer') return print_n_send_error_response(request, 'Dynamic Analysis Failed.', api)