def take_screenshot(request): """Take Screenshot""" logger.info("Taking Screenshot") try: if request.method == 'POST': md5_hash = request.POST['md5'] if re.match('^[0-9a-f]{32}$', md5_hash): data = {} rand_int = random.randint(1, 1000000) base_dir = settings.BASE_DIR # make sure that list only png from this directory screen_dir = os.path.join( settings.UPLD_DIR, md5_hash + '/screenshots-apk/') if not os.path.exists(screen_dir): os.makedirs(screen_dir) adb_command( ["screencap", "-p", "/data/local/screen.png"], True) adb_command(["pull", "/data/local/screen.png", screen_dir + "screenshot-" + str(rand_int) + ".png"]) logger.info("Screenshot Taken") data = {'screenshot': 'yes'} return HttpResponse(json.dumps(data), content_type='application/json') else: return print_n_send_error_response(request, "Invalid Scan Hash", True) else: return print_n_send_error_response(request, "Only POST allowed", True) except: PrintException("Taking Screenshot") return print_n_send_error_response(request, "Error Taking Screenshot", True)
def dump_data(request): """Downloading Application Data from Device""" logger.info("Downloading Application Data from Device") try: if request.method == 'POST': data = {} package = request.POST['pkg'] md5_hash = request.POST['md5'] if re.match('^[0-9a-f]{32}$', md5_hash): if re.findall(r";|\$\(|\|\||&&", package): return print_n_send_error_response(request, "Possible RCE Attack", True) base_dir = settings.BASE_DIR apk_dir = os.path.join(settings.UPLD_DIR, md5_hash + '/') # Let's try to close Proxy a bit early as we don't have much # control on the order of thread execution stop_capfuzz(settings.PORT) logger.info("Deleting Dump Status File") adb_command(["rm", "/sdcard/mobsec_status"], True) logger.info("Creating TAR of Application Files.") adb_command(["am", "startservice", "-a", package, "opensecurity.ajin.datapusher/.GetPackageLocation"], True) logger.info("Waiting for TAR dump to complete...") if settings.ANDROID_DYNAMIC_ANALYZER == "MobSF_REAL_DEVICE": timeout = settings.DEVICE_TIMEOUT else: timeout = settings.VM_TIMEOUT start_time = time.time() while True: current_time = time.time() if b"MOBSEC-TAR-CREATED" in adb_command(["cat", "/sdcard/mobsec_status"], shell=True): break if (current_time - start_time) > timeout: logger.error( "TAR Generation Failed. Process timed out.") break logger.info("Dumping Application Files from Device/VM") adb_command(["pull", "/data/local/" + package + ".tar", apk_dir + package + ".tar"]) if settings.ANDROID_DYNAMIC_ANALYZER == "MobSF_AVD": logger.info("Removing package") adb_command(["uninstall", package]) stop_avd() logger.info("Stopping ADB") adb_command(["kill-server"]) data = {'dump': 'yes'} return HttpResponse(json.dumps(data), content_type='application/json') else: return print_n_send_error_response(request, "Invalid Scan Hash", True) else: return print_n_send_error_response(request, "Only POST allowed", True) except: PrintException("Downloading Application Data from Device") return print_n_send_error_response(request, "Application Data Dump from Device failed", True)
def mobsf_ca(request): """Install and Remove MobSF Proxy RootCA""" try: if request.method == 'POST': data = {} act = request.POST['action'] rootca = get_ca_dir() adb = getADB() if act == "install": logger.info("Installing MobSF RootCA") adb_command( ["push", rootca, "/data/local/tmp/" + settings.ROOT_CA]) if settings.ANDROID_DYNAMIC_ANALYZER == "MobSF_AVD": # For some reason, avd emulator does not have cp binary adb_command(["/data/local/tmp/busybox", "cp", "/data/local/tmp/" + settings.ROOT_CA, "/system/etc/security/cacerts/" + settings.ROOT_CA], True) adb_command(["chmod", "644", "/system/etc/security/cacerts/" + settings.ROOT_CA], True) else: adb_command(["su", "-c", "cp", "/data/local/tmp/" + settings.ROOT_CA, "/system/etc/security/cacerts/" + settings.ROOT_CA], True) adb_command(["su", "-c", "chmod", "644", "/system/etc/security/cacerts/" + settings.ROOT_CA], True) adb_command( ["rm", "/data/local/tmp/" + settings.ROOT_CA], True) data = {'ca': 'installed'} elif act == "remove": logger.info("Removing MobSF RootCA") if settings.ANDROID_DYNAMIC_ANALYZER == "MobSF_AVD": adb_command( ["rm", "/system/etc/security/cacerts/" + settings.ROOT_CA], True) else: adb_command(["su", "-c", "rm", "/system/etc/security/cacerts/" + settings.ROOT_CA], True) data = {'ca': 'removed'} return HttpResponse(json.dumps(data), content_type='application/json') else: return print_n_send_error_response(request, "Only POST allowed", True) except: PrintException("MobSF RootCA Handler") return print_n_send_error_response(request, "Error in RootCA Handler", True)
def touch(request): """Sending Touch Events""" logger.info("Sending Touch Events") try: data = {} if (request.method == 'POST') and (is_number(request.POST['x'])) and (is_number(request.POST['y'])): x_axis = request.POST['x'] y_axis = request.POST['y'] adb = getADB() args = ["input", "tap", x_axis, y_axis] data = {'status': 'success'} try: adb_command(args, True) except: data = {'status': 'error'} PrintException("Performing Touch Action") else: data = {'status': 'failed'} return HttpResponse(json.dumps(data), content_type='application/json') except: PrintException("Sending Touch Events") return print_n_send_error_response(request, "Error Sending Touch Events", True)
def compare_apps(request, first_hash: str, second_hash: str): if first_hash == second_hash: error_msg = "Results with same hash cannot be compared" return print_n_send_error_response(request, error_msg, False) logger.info( "Starting App compare for - {} and {}".format(first_hash, second_hash)) return generic_compare(request, first_hash, second_hash)
def delete_scan(request, api=False): """ Delete Scan from DB and remove the scan related files """ try: if request.method == 'POST': if api: md5_hash = request.POST['hash'] else: md5_hash = request.POST['md5'] data = {'deleted': 'no'} if re.match('[0-9a-f]{32}', md5_hash): # Delete DB Entries scan = RecentScansDB.objects.filter(MD5=md5_hash) if scan.exists(): RecentScansDB.objects.filter(MD5=md5_hash).delete() ScopeURLSandTests.objects.filter(MD5=md5_hash).delete() StaticAnalyzerAndroid.objects.filter(MD5=md5_hash).delete() StaticAnalyzerIPA.objects.filter(MD5=md5_hash).delete() StaticAnalyzerIOSZIP.objects.filter(MD5=md5_hash).delete() StaticAnalyzerWindows.objects.filter(MD5=md5_hash).delete() # Delete Upload Dir Contents app_upload_dir = os.path.join(settings.UPLD_DIR, md5_hash) if isDirExists(app_upload_dir): shutil.rmtree(app_upload_dir) # Delete Download Dir Contents dw_dir = settings.DWD_DIR for item in os.listdir(dw_dir): item_path = os.path.join(dw_dir, item) # Delete all related files if isFileExists(item_path) and item.startswith(md5_hash + "-"): os.remove(item_path) # Delete related directories if isDirExists(item_path) and item.startswith(md5_hash + "-"): shutil.rmtree(item_path) data = {'deleted': 'yes'} if api: return data else: return HttpResponse(json.dumps(data), content_type='application/json; charset=utf-8') except Exception as exp: msg = str(exp) exp_doc = exp.__doc__ if api: return print_n_send_error_response(request, msg, True, exp_doc) else: return print_n_send_error_response(request, msg, False, exp_doc)
def final_test(request): """Collecting Data and Cleanup""" global TCP_SERVER_MODE logger.info("Collecting Data and Cleaning Up") try: if request.method == 'POST': data = {} md5_hash = request.POST['md5'] package = request.POST['pkg'] if re.findall(r";|\$\(|\|\||&&", package): return print_n_send_error_response(request, "Possible RCE Attack", True) if re.match('^[0-9a-f]{32}$', md5_hash): # Stop ScreenCast Client if it is running TCP_SERVER_MODE = "off" base_dir = settings.BASE_DIR apk_dir = os.path.join(settings.UPLD_DIR, md5_hash + '/') adb = getADB() # Change to check output of subprocess when analysis is done # Can't RCE os.system(adb + ' -s ' + get_identifier() + ' logcat -d dalvikvm:W ActivityManager:I > "' + apk_dir + 'logcat.txt"') logger.info("Downloading Logcat logs") adb_command(["pull", "/data/data/de.robv.android.xposed.installer/log/error.log", apk_dir + "x_logcat.txt"]) logger.info("Downloading Droidmon API Monitor Logcat logs") # Can't RCE os.system(adb + ' -s ' + get_identifier() + ' shell dumpsys > "' + apk_dir + 'dump.txt"') logger.info("Downloading Dumpsys logs") adb_command(["am", "force-stop", package], True) logger.info("Stopping Application") adb_command( ["am", "force-stop", "opensecurity.screencast"], True) logger.info("Stopping ScreenCast Service") data = {'final': 'yes'} return HttpResponse(json.dumps(data), content_type='application/json') else: return print_n_send_error_response(request, "Invalid Scan Hash", True) else: return print_n_send_error_response(request, "Only POST allowed", True) except: PrintException("Data Collection & Clean Up") return print_n_send_error_response(request, "Data Collection & Clean Up failed", True)
def get_env(request): """Get Dynamic Analysis Environment for Android""" logger.info("Setting up Dynamic Analysis Environment") try: if request.method == 'POST': data = {} md5_hash = request.POST['md5'] package = request.POST['pkg'] launcher = request.POST['lng'] if re.findall(r";|\$\(|\|\||&&", package) or re.findall(r";|\$\(|\|\||&&", launcher): return print_n_send_error_response(request, "Possible RCE Attack", True) if re.match('^[0-9a-f]{32}$', md5_hash): base_dir = settings.BASE_DIR app_dir = os.path.join( settings.UPLD_DIR, md5_hash + '/') # APP DIRECTORY app_file = md5_hash + '.apk' # NEW FILENAME app_path = app_dir + app_file # APP PATH adb = getADB() if settings.ANDROID_DYNAMIC_ANALYZER == "MobSF_AVD": proxy_ip = '127.0.0.1' else: proxy_ip = settings.PROXY_IP # Proxy IP start_proxy(settings.PORT, package) # vm needs the connect function try: if not settings.ANDROID_DYNAMIC_ANALYZER == "MobSF_AVD": connect() except Exception as exp: data = {'ready': 'no', 'msg': 'Cannot Connect to the VM/Device.', 'error': str(exp)} return HttpResponse(json.dumps(data), content_type='application/json') # Change True to support non-activity components install_and_run(app_path, package, launcher, True) screen_width, screen_width = get_res() data = {'ready': 'yes', 'screen_witdth': screen_width, 'screen_height': screen_width, } return HttpResponse(json.dumps(data), content_type='application/json') else: return print_n_send_error_response(request, "Invalid Scan Hash", True) else: return print_n_send_error_response(request, "Only POST allowed", True) except: PrintException("Setting up Dynamic Analysis Environment") return print_n_send_error_response(request, "Environment Setup Failed", True)
def execute_adb(request): """Execute ADB Commands""" logger.info("Executing ADB Commands") try: if request.method == 'POST': data = {} cmd = request.POST['cmd'] resp = "error" try: resp = adb_command(cmd.split(' ')) except: PrintException("Executing ADB Commands") data = {'cmd': 'yes', 'resp': resp.decode("utf8", "ignore")} return HttpResponse(json.dumps(data), content_type='application/json') else: return print_n_send_error_response(request, "Only POST allowed", True) except: PrintException("Executing ADB Commands") return print_n_send_error_response(request, "Error running ADB commands", True)
def search(request): """ Search Scan by MD5 Route """ md5 = request.GET['md5'] if re.match('[0-9a-f]{32}', md5): db_obj = RecentScansDB.objects.filter(MD5=md5) if db_obj.exists(): return HttpResponseRedirect('/' + db_obj[0].URL) else: return HttpResponseRedirect('/not_found') return print_n_send_error_response(request, "Invalid Scan Hash")
def view(request): """View File""" logger.info("Viewing File") try: typ = '' fil = '' rtyp = '' dat = '' if re.match('^[0-9a-f]{32}$', request.GET['md5']): fil = request.GET['file'] md5_hash = request.GET['md5'] typ = request.GET['type'] src = os.path.join(settings.UPLD_DIR, md5_hash + '/DYNAMIC_DeviceData/') sfile = os.path.join(src, fil) # Prevent Directory Traversal Attacks if ("../" in fil) or ("%2e%2e" in fil) or (".." in fil) or ("%252e" in fil): return print_n_send_error_response(request, "Path Traversal Attack Detected") else: with io.open(sfile, mode='r', encoding="utf8", errors="ignore") as flip: dat = flip.read() if (fil.endswith('.xml')) and (typ == 'xml'): rtyp = 'xml' elif typ == 'db': dat = handle_sqlite(sfile) rtyp = 'asciidoc' elif typ == 'others': rtyp = 'asciidoc' else: return print_n_send_error_response(request, "File Type not supported") context = {'title': escape(ntpath.basename(fil)), 'file': escape( ntpath.basename(fil)), 'dat': dat, 'type': rtyp, } template = "general/view.html" return render(request, template, context) else: return print_n_send_error_response(request, "Invalid Scan Hash") except: PrintException("Viewing File") return print_n_send_error_response(request, "ERROR Viewing File")
def download(request): """ Download from MobSF Route """ msg = "Error Downloading File " if request.method == 'GET': allowed_exts = settings.ALLOWED_EXTENSIONS filename = request.path.replace("/download/", "", 1) # Security Checks if "../" in filename: return print_n_send_error_response(request, "Path Traversal Attack detected") ext = os.path.splitext(filename)[1] if ext in allowed_exts: dwd_file = os.path.join(settings.DWD_DIR, filename) if os.path.isfile(dwd_file): wrapper = FileWrapper(open(dwd_file, "rb")) response = HttpResponse( wrapper, content_type=allowed_exts[ext]) response['Content-Length'] = os.path.getsize(dwd_file) return response msg += filename return print_n_send_error_response(request, msg)
def capfuzz_start(request): """Start CapFuzz UI""" logger.info("Starting CapFuzz Web UI") try: stop_capfuzz(settings.PORT) start_fuzz_ui(settings.PORT) time.sleep(3) logger.info("CapFuzz UI Started") if request.GET['project']: project = request.GET['project'] else: project = "" return HttpResponseRedirect('http://localhost:' + str(settings.PORT) + "/dashboard/" + project) except: PrintException("Starting CapFuzz Web UI") return print_n_send_error_response(request, "Error Starting CapFuzz UI")
def android_dynamic_analyzer(request): """Android Dynamic Analyzer View""" logger.info("Dynamic Analysis Started") try: if request.method == 'POST': md5_hash = request.POST['md5'] package = request.POST['pkg'] launcher = request.POST['lng'] if re.findall(r';|\$\(|\|\||&&', package) or re.findall(r';|\$\(|\|\||&&', launcher): return print_n_send_error_response(request, "Possible RCE Attack") if re.match('^[0-9a-f]{32}$', md5_hash): # Delete ScreenCast Cache screen_file = os.path.join(settings.SCREEN_DIR, 'screen.png') if os.path.exists(screen_file): os.remove(screen_file) # Delete Contents of Screenshot Dir screen_dir = os.path.join( settings.UPLD_DIR, md5_hash + '/screenshots-apk/') if os.path.isdir(screen_dir): shutil.rmtree(screen_dir) else: os.makedirs(screen_dir) # Start DM stop_capfuzz(settings.PORT) adb = getADB() is_avd = False if settings.ANDROID_DYNAMIC_ANALYZER == "MobSF_REAL_DEVICE": logger.info( "MobSF will perform Dynamic Analysis on real Android Device") elif settings.ANDROID_DYNAMIC_ANALYZER == "MobSF_AVD": # adb, avd_path, reference_name, dup_name, emulator is_avd = True if not os.path.exists(settings.AVD_EMULATOR): return print_n_send_error_response(request, "Cannot Find AVD Emulator") if not refresh_avd(): return print_n_send_error_response(request, "Cannot Refresh AVD") else: # Refersh VM refresh_vm(settings.UUID, settings.SUUID, settings.VBOX) context = {'md5': md5_hash, 'pkg': package, 'lng': launcher, 'title': 'Start Testing', 'AVD': is_avd, } template = "dynamic_analysis/start_test.html" return render(request, template, context) else: return print_n_send_error_response(request, "Invalid Scan Hash") else: return print_n_send_error_response(request, "Only POST allowed") except: PrintException("DynamicAnalyzer") return print_n_send_error_response(request, "Dynamic Analysis Failed.")
def screen_cast(request): """Start or Stop ScreenCast Feature""" logger.info("Invoking ScreenCast Service in VM/Device") try: global TCP_SERVER_MODE data = {} if request.method == 'POST': mode = request.POST['mode'] if settings.ANDROID_DYNAMIC_ANALYZER == "MobSF_AVD": ip_address = '10.0.2.2' else: ip_address = settings.SCREEN_IP port = str(settings.SCREEN_PORT) if mode == "on": args = ["am", "startservice", "-a", ip_address + ":" + port, "opensecurity.screencast/.StartScreenCast"] data = {'status': 'on'} TCP_SERVER_MODE = "on" elif mode == "off": args = ["am", "force-stop", "opensecurity.screencast"] data = {'status': 'off'} TCP_SERVER_MODE = "off" if (mode in ["on", "off"]): try: adb_command(args, True) screen_trd = threading.Thread(target=screencast_service) screen_trd.setDaemon(True) screen_trd.start() except: PrintException("Casting Screen") data = {'status': 'error'} return HttpResponse(json.dumps(data), content_type='application/json') else: data = {'status': 'failed'} else: data = {'status': 'failed'} return HttpResponse(json.dumps(data), content_type='application/json') except: PrintException("Casting Screen") return print_n_send_error_response(request, "Error Casting Screen", True)
def clip_dump(request): """Dump Android ClipBoard""" logger.info("Starting Clipboard Dump Service in VM/Device") try: data = {} if request.method == 'POST': adb = getADB() args = ["am", "startservice", "opensecurity.clipdump/.ClipDumper"] try: adb_command(args, True) data = {'status': 'success'} except: PrintException("Dumping Clipboard") data = {'status': 'error'} else: data = {'status': 'failed'} return HttpResponse(json.dumps(data), content_type='application/json') except: PrintException("Dumping Clipboard") return print_n_send_error_response(request, "Error Dumping Clipboard", True)
def pdf(request, api=False, jsonres=False): try: if api: checksum = request.POST['hash'] else: checksum = request.GET['md5'] hash_match = re.match('^[0-9a-f]{32}$', checksum) if hash_match: android_static_db = StaticAnalyzerAndroid.objects.filter( MD5=checksum) ios_static_db = StaticAnalyzerIOS.objects.filter(MD5=checksum) win_static_db = StaticAnalyzerWindows.objects.filter(MD5=checksum) if android_static_db.exists(): context, template = handle_pdf_android(android_static_db) elif ios_static_db.exists(): context, template = handle_pdf_ios(ios_static_db) elif win_static_db.exists(): context, template = handle_pdf_win(win_static_db) else: if api: return {'report': 'Report not Found'} else: return HttpResponse( json.dumps({'report': 'Report not Found'}), content_type='application/json; charset=utf-8', status=500) context['virus_total'] = None if settings.VT_ENABLED: file_extension = os.path.splitext( context['file_name'].lower())[1] app_dir = os.path.join(settings.UPLD_DIR, checksum + '/') vt = VirusTotal.VirusTotal() if file_extension.lower() == '.zip': context['virus_total'] = None else: context['virus_total'] = vt.get_result( os.path.join(app_dir, checksum) + file_extension.lower(), checksum) try: if api and jsonres: return {'report_dat': context} else: options = { 'page-size': 'A4', 'quiet': '', 'no-collate': '', 'margin-top': '0.50in', 'margin-right': '0.50in', 'margin-bottom': '0.50in', 'margin-left': '0.50in', 'encoding': 'UTF-8', 'custom-header': [ ('Accept-Encoding', 'gzip'), ], 'no-outline': None, } html = template.render(context) pdf_dat = pdfkit.from_string(html, False, options=options) if api: return {'pdf_dat': pdf_dat} return HttpResponse(pdf_dat, content_type='application/pdf') except Exception as exp: logger.exception('Error Generating PDF Report') if api: return { 'error': 'Cannot Generate PDF/JSON', 'err_details': str(exp) } else: return HttpResponse( json.dumps({ 'pdf_error': 'Cannot Generate PDF', 'err_details': str(exp) }), content_type='application/json; charset=utf-8', status=500) else: if api: return {'error': 'Invalid scan hash'} else: return HttpResponse( json.dumps({'md5': 'Invalid MD5'}), content_type='application/json; charset=utf-8', status=500) except Exception as exp: logger.exception('Error Generating PDF Report') msg = str(exp) exp = exp.__doc__ if api: return print_n_send_error_response(request, msg, True, exp) else: return print_n_send_error_response(request, msg, False, exp)
def static_analyzer_ios(request, api=False): """Module that performs iOS IPA/ZIP Static Analysis""" try: print "[INFO] iOS Static Analysis Started" if api: file_type = request.POST['scan_type'] checksum = request.POST['hash'] rescan = str(request.POST.get('re_scan', 0)) filename = request.POST['file_name'] else: file_type = request.GET['type'] checksum = request.GET['checksum'] rescan = str(request.GET.get('rescan', 0)) filename = request.GET['name'] md5_match = re.match('^[0-9a-f]{32}$', checksum) if ((md5_match) and (filename.lower().endswith('.ipa') or filename.lower().endswith('.zip')) and (file_type in ['ipa', 'ios'])): app_dict = {} app_dict["directory"] = settings.BASE_DIR # BASE DIR app_dict["app_name"] = filename # APP ORGINAL NAME app_dict["md5_hash"] = checksum # MD5 app_dict["app_dir"] = os.path.join( settings.UPLD_DIR, app_dict["md5_hash"] + '/') # APP DIRECTORY tools_dir = os.path.join(app_dict["directory"], 'StaticAnalyzer/tools/mac/') # TOOLS DIR if file_type == 'ipa': # DB ipa_db = StaticAnalyzerIPA.objects.filter( MD5=app_dict["md5_hash"]) if ipa_db.exists() and rescan == '0': context = get_context_from_db_entry_ipa(ipa_db) else: print "[INFO] iOS Binary (IPA) Analysis Started" app_dict["app_file"] = app_dict[ "md5_hash"] + '.ipa' # NEW FILENAME app_dict["app_path"] = app_dict["app_dir"] + \ app_dict["app_file"] # APP PATH app_dict["bin_dir"] = os.path.join(app_dict["app_dir"], "Payload/") app_dict["size"] = str(file_size( app_dict["app_path"])) + 'MB' # FILE SIZE app_dict["sha1"], app_dict["sha256"] = hash_gen( app_dict["app_path"]) # SHA1 & SHA256 HASHES print "[INFO] Extracting IPA" # EXTRACT IPA unzip(app_dict["app_path"], app_dict["app_dir"]) # Get Files, normalize + to x, # and convert binary plist -> xml files, sfiles = ios_list_files(app_dict["bin_dir"], app_dict["md5_hash"], True, 'ipa') infoplist_dict = plist_analysis(app_dict["bin_dir"], False) bin_analysis_dict = binary_analysis( app_dict["bin_dir"], tools_dir, app_dict["app_dir"]) # Saving to DB print "\n[INFO] Connecting to DB" if rescan == '1': print "\n[INFO] Updating Database..." update_db_entry_ipa(app_dict, infoplist_dict, bin_analysis_dict, files, sfiles) elif rescan == '0': print "\n[INFO] Saving to Database" create_db_entry_ipa(app_dict, infoplist_dict, bin_analysis_dict, files, sfiles) context = get_context_from_analysis_ipa( app_dict, infoplist_dict, bin_analysis_dict, files, sfiles) context['VT_RESULT'] = None if settings.VT_ENABLED: vt = VirusTotal.VirusTotal() context['VT_RESULT'] = vt.get_result( os.path.join(app_dict['app_dir'], app_dict['md5_hash']) + '.ipa', app_dict['md5_hash']) template = "static_analysis/ios_binary_analysis.html" if api: return context else: return render(request, template, context) elif file_type == 'ios': ios_zip_db = StaticAnalyzerIOSZIP.objects.filter( MD5=app_dict["md5_hash"]) if ios_zip_db.exists() and rescan == '0': context = get_context_from_db_entry_ios(ios_zip_db) else: print "[INFO] iOS Source Code Analysis Started" app_dict["app_file"] = app_dict[ "md5_hash"] + '.zip' # NEW FILENAME app_dict["app_path"] = app_dict["app_dir"] + \ app_dict["app_file"] # APP PATH # ANALYSIS BEGINS - Already Unzipped print "[INFO] ZIP Already Extracted" app_dict["size"] = str(file_size( app_dict["app_path"])) + 'MB' # FILE SIZE app_dict["sha1"], app_dict["sha256"] = hash_gen( app_dict["app_path"]) # SHA1 & SHA256 HASHES files, sfiles = ios_list_files(app_dict["app_dir"], app_dict["md5_hash"], False, 'ios') infoplist_dict = plist_analysis(app_dict["app_dir"], True) code_analysis_dic = ios_source_analysis( app_dict["app_dir"]) # Saving to DB print "\n[INFO] Connecting to DB" if rescan == '1': print "\n[INFO] Updating Database..." update_db_entry_ios(app_dict, infoplist_dict, code_analysis_dic, files, sfiles) elif rescan == '0': print "\n[INFO] Saving to Database" create_db_entry_ios(app_dict, infoplist_dict, code_analysis_dic, files, sfiles) context = get_context_from_analysis_ios( app_dict, infoplist_dict, code_analysis_dic, files, sfiles) template = "static_analysis/ios_source_analysis.html" if api: return context else: return render(request, template, context) else: msg = "File Type not supported!" if api: return print_n_send_error_response(request, msg, True) else: return print_n_send_error_response(request, msg, False) else: msg = "Hash match failed or Invalid file extension or file type" if api: return print_n_send_error_response(request, msg, True) else: return print_n_send_error_response(request, msg, False) except Exception as exp: msg = str(exp) exp_doc = exp.__doc__ if api: return print_n_send_error_response(request, msg, True, exp_doc) else: return print_n_send_error_response(request, msg, False, exp_doc)
def run(request, api=False): """View iOS Files""" try: print("[INFO] View iOS Source File") file_format = "cpp" if api: fil = request.POST['file'] md5_hash = request.POST['hash'] mode = request.POST['type'] viewsource_form = ViewSourceIOSApiForm(request.POST) else: fil = request.GET['file'] md5_hash = request.GET['md5'] mode = request.GET['type'] viewsource_form = ViewSourceIOSForm(request.GET) typ = set_ext_api(fil) if not viewsource_form.is_valid(): err = FormUtil.errors_message(viewsource_form) if api: return err context = { 'title': 'Error', 'exp': 'Error Description', 'doc': err } template = "general/error.html" return render(request, template, context, status=400) if mode == 'ipa': src = os.path.join(settings.UPLD_DIR, md5_hash + '/Payload/') elif mode == 'ios': src = os.path.join(settings.UPLD_DIR, md5_hash + '/') sfile = os.path.join(src, fil) dat = '' if typ == 'm': file_format = 'cpp' with io.open(sfile, mode='r', encoding="utf8", errors="ignore") as flip: dat = flip.read() elif typ == 'xml': file_format = 'xml' with io.open(sfile, mode='r', encoding="utf8", errors="ignore") as flip: dat = flip.read() elif typ == 'db': file_format = 'asciidoc' dat = read_sqlite(sfile) elif typ == 'txt' and fil == "classdump.txt": file_format = 'cpp' app_dir = os.path.join(settings.UPLD_DIR, md5_hash + '/') cls_dump_file = os.path.join(app_dir, "classdump.txt") if isFileExists(cls_dump_file): with io.open(cls_dump_file, mode='r', encoding="utf8", errors="ignore" ) as flip: dat = flip.read() else: dat = "Class Dump result not Found" else: if api: return {"error": "Invalid Parameters"} return HttpResponseRedirect('/error/') context = {'title': escape(ntpath.basename(fil)), 'file': escape(ntpath.basename(fil)), 'type': file_format, 'dat': dat} template = "general/view.html" if api: return context return render(request, template, context) except Exception as exp: msg = str(exp) exp = exp.__doc__ if api: return print_n_send_error_response(request, msg, True, exp) else: return print_n_send_error_response(request, msg, False, exp)
def compare_apps(request, hash1: str, hash2: str, api=False): if hash1 == hash2: error_msg = 'Results with same hash cannot be compared' return print_n_send_error_response(request, error_msg, api) logger.info('Starting App compare for %s and %s', hash1, hash2) return generic_compare(request, hash1, hash2, api)
def static_analyzer_ios(request, api=False): """Module that performs iOS IPA/ZIP Static Analysis.""" try: logger.info('iOS Static Analysis Started') if api: file_type = request.POST['scan_type'] checksum = request.POST['hash'] rescan = str(request.POST.get('re_scan', 0)) filename = request.POST['file_name'] else: file_type = request.GET['type'] checksum = request.GET['checksum'] rescan = str(request.GET.get('rescan', 0)) filename = request.GET['name'] md5_match = re.match('^[0-9a-f]{32}$', checksum) if ((md5_match) and (filename.lower().endswith('.ipa') or filename.lower().endswith('.zip')) and (file_type in ['ipa', 'ios'])): app_dict = {} app_dict['directory'] = Path(settings.BASE_DIR) # BASE DIR app_dict['file_name'] = filename # APP ORGINAL NAME app_dict['md5_hash'] = checksum # MD5 app_dir = Path(settings.UPLD_DIR) / checksum tools_dir = app_dict[ 'directory'] / 'StaticAnalyzer' / 'tools' / 'ios' tools_dir = tools_dir.as_posix() if file_type == 'ipa': app_dict[ 'app_file'] = app_dict['md5_hash'] + '.ipa' # NEW FILENAME 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']) if ipa_db.exists() and rescan == '0': context = get_context_from_db_entry(ipa_db) else: logger.info('iOS Binary (IPA) Analysis Started') app_dict['app_dir'] = app_dir.as_posix() + '/' app_dict['size'] = str(file_size( app_dict['app_path'])) + 'MB' # FILE SIZE app_dict['sha1'], app_dict['sha256'] = hash_gen( app_dict['app_path']) # SHA1 & SHA256 HASHES logger.info('Extracting IPA') # EXTRACT IPA unzip(app_dict['app_path'], app_dict['app_dir']) # Identify Payload directory dirs = app_dir.glob('**/*') for _dir in dirs: if 'payload' in _dir.as_posix().lower(): app_dict['bin_dir'] = app_dict['app_dir'] / _dir break else: msg = ('IPA is malformed! ' 'MobSF cannot find Payload directory') if api: return print_n_send_error_response( request, msg, True) else: return print_n_send_error_response( request, msg, 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') infoplist_dict = plist_analysis(app_dict['bin_dir'], False) app_dict['appstore'] = app_search(infoplist_dict.get('id')) bin_analysis_dict = binary_analysis( app_dict['bin_dir'], tools_dir, app_dict['app_dir'], infoplist_dict.get('bin')) # Get Icon app_dict['icon_found'] = get_icon( app_dict['md5_hash'], app_dict['bin_dir'], infoplist_dict.get('bin')) # IPA URL and Email Extract recon = extract_urls_n_email(app_dict['bin_dir'], all_files['files_long'], bin_analysis_dict['strings']) code_dict = { 'api': {}, 'code_anal': {}, 'urlnfile': recon['urlnfile'], 'domains': recon['domains'], 'emailnfile': recon['emailnfile'], 'firebase': firebase_analysis(recon['urls_list']), } # Saving to DB logger.info('Connecting to DB') if rescan == '1': logger.info('Updating Database...') save_or_update('update', app_dict, infoplist_dict, code_dict, bin_analysis_dict, all_files) update_scan_timestamp(app_dict['md5_hash']) elif rescan == '0': logger.info('Saving to Database') save_or_update('save', app_dict, infoplist_dict, code_dict, bin_analysis_dict, all_files) context = get_context_from_analysis( app_dict, infoplist_dict, code_dict, bin_analysis_dict, all_files) context['virus_total'] = None if settings.VT_ENABLED: vt = VirusTotal.VirusTotal() context['virus_total'] = vt.get_result( app_dict['app_path'], app_dict['md5_hash']) context['average_cvss'], context['security_score'] = score( context['binary_analysis']) template = 'static_analysis/ios_binary_analysis.html' if api: return context else: return render(request, template, context) elif file_type == 'ios': ios_zip_db = StaticAnalyzerIOS.objects.filter( MD5=app_dict['md5_hash']) if ios_zip_db.exists() and rescan == '0': context = get_context_from_db_entry(ios_zip_db) else: logger.info('iOS Source Code Analysis Started') app_dict['app_file'] = app_dict[ 'md5_hash'] + '.zip' # NEW FILENAME app_dict['app_path'] = app_dir / app_dict['app_file'] app_dict['app_path'] = app_dict['app_path'].as_posix() app_dict['app_dir'] = app_dir.as_posix() + '/' # ANALYSIS BEGINS - Already Unzipped app_dict['size'] = str(file_size( app_dict['app_path'])) + 'MB' # FILE SIZE app_dict['sha1'], app_dict['sha256'] = hash_gen( app_dict['app_path']) # SHA1 & SHA256 HASHES all_files = ios_list_files(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')) code_analysis_dic = ios_source_analysis( app_dict['app_dir']) # Get App Icon app_dict['icon_found'] = get_icon_source( app_dict['md5_hash'], app_dict['app_dir']) # Firebase DB Check code_analysis_dic['firebase'] = firebase_analysis( list(set(code_analysis_dic['urls_list']))) fake_bin_dict = { 'checksec': {}, 'libraries': [], 'bin_code_analysis': {}, 'strings': [], 'bin_info': {}, 'bin_type': code_analysis_dic['source_type'], } # Saving to DB logger.info('Connecting to DB') if rescan == '1': logger.info('Updating Database...') save_or_update('update', app_dict, infoplist_dict, code_analysis_dic, fake_bin_dict, all_files) update_scan_timestamp(app_dict['md5_hash']) elif rescan == '0': logger.info('Saving to Database') save_or_update('save', app_dict, infoplist_dict, code_analysis_dic, fake_bin_dict, all_files) context = get_context_from_analysis( app_dict, infoplist_dict, code_analysis_dic, fake_bin_dict, all_files) context['average_cvss'], context['security_score'] = score( context['code_analysis']) template = 'static_analysis/ios_source_analysis.html' if api: return context else: return render(request, template, context) else: msg = 'File Type not supported!' if api: return print_n_send_error_response(request, msg, True) else: return print_n_send_error_response(request, msg, False) else: msg = 'Hash match failed or Invalid file extension or file type' if api: return print_n_send_error_response(request, msg, True) else: return print_n_send_error_response(request, msg, False) except Exception as exp: logger.exception('Error Perfroming Static Analysis') msg = str(exp) exp_doc = exp.__doc__ if api: return print_n_send_error_response(request, msg, True, exp_doc) else: return print_n_send_error_response(request, msg, False, exp_doc)
def staticanalyzer_windows(request, api=False): """Analyse a windows app.""" try: # Input validation logger.info("Windows Static Analysis Started") app_dic = {} # Dict to store the binary attributes if api: typ = request.POST['scan_type'] rescan = str(request.POST.get('re_scan', 0)) checksum = request.POST['hash'] filename = request.POST['file_name'] else: typ = request.GET['type'] rescan = str(request.GET.get('rescan', 0)) checksum = request.GET['checksum'] filename = request.GET['name'] md5_regex = re.match('^[0-9a-f]{32}$', checksum) if (md5_regex) and (typ in ['appx']): app_dic['app_name'] = filename # APP ORGINAL NAME app_dic['md5'] = checksum app_dic['app_dir'] = os.path.join( settings.UPLD_DIR, app_dic['md5'] + '/') app_dic['tools_dir'] = os.path.join( settings.BASE_DIR, 'StaticAnalyzer/tools/windows/') if typ == 'appx': # DB db_entry = StaticAnalyzerWindows.objects.filter( # pylint: disable-msg=E1101 MD5=app_dic['md5'] ) if db_entry.exists() and rescan == '0': logger.info("Analysis is already Done. Fetching data from the DB...") context = { 'title': db_entry[0].TITLE, 'name': db_entry[0].APP_NAME, 'pub_name': db_entry[0].PUB_NAME, 'size': db_entry[0].SIZE, 'md5': db_entry[0].MD5, 'sha1': db_entry[0].SHA1, 'sha256': db_entry[0].SHA256, 'bin_name': db_entry[0].BINNAME, 'version': db_entry[0].VERSION, 'arch': db_entry[0].ARCH, 'compiler_version': db_entry[0].COMPILER_VERSION, 'visual_studio_version': db_entry[0].VISUAL_STUDIO_VERSION, 'visual_studio_edition': db_entry[0].VISUAL_STUDIO_EDITION, 'target_os': db_entry[0].TARGET_OS, 'appx_dll_version': db_entry[0].APPX_DLL_VERSION, 'proj_guid': db_entry[0].PROJ_GUID, 'opti_tool': db_entry[0].OPTI_TOOL, 'target_run': db_entry[0].TARGET_RUN, 'files': python_list(db_entry[0].FILES), 'strings': python_list(db_entry[0].STRINGS), 'bin_an_results': python_list(db_entry[0].BIN_AN_RESULTS), 'bin_an_warnings': python_list(db_entry[0].BIN_AN_WARNINGS) } else: logger.info("Windows Binary Analysis Started") app_dic['app_path'] = os.path.join( app_dic['app_dir'], app_dic['md5'] + '.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']) # 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']) bin_an_dic = _binary_analysis(app_dic) # Saving to db logger.info("Connecting to DB") if rescan == '1': logger.info("Updating Database...") StaticAnalyzerWindows.objects.filter( # pylint: disable-msg=E1101 MD5=app_dic['md5'] ).update( TITLE='Static Analysis', APP_NAME=app_dic['app_name'], PUB_NAME=xml_dic['pub_name'], SIZE=app_dic['size'], MD5=app_dic['md5'], SHA1=app_dic['sha1'], SHA256=app_dic['sha256'], BINNAME=bin_an_dic['bin_name'], VERSION=xml_dic['version'], ARCH=xml_dic['arch'], COMPILER_VERSION=xml_dic['compiler_version'], VISUAL_STUDIO_VERSION=xml_dic[ 'visual_studio_version'], VISUAL_STUDIO_EDITION=xml_dic[ 'visual_studio_edition'], TARGET_OS=xml_dic['target_os'], APPX_DLL_VERSION=xml_dic['appx_dll_version'], PROJ_GUID=xml_dic['proj_guid'], OPTI_TOOL=xml_dic['opti_tool'], TARGET_RUN=xml_dic['target_run'], FILES=app_dic['files'], STRINGS=bin_an_dic['strings'], BIN_AN_RESULTS=bin_an_dic['results'], BIN_AN_WARNINGS=bin_an_dic['warnings'], ) elif rescan == '0': logger.info("Saving to Database") db_item = StaticAnalyzerWindows( TITLE='Static Analysis', APP_NAME=app_dic['app_name'], PUB_NAME=xml_dic['pub_name'], SIZE=app_dic['size'], MD5=app_dic['md5'], SHA1=app_dic['sha1'], SHA256=app_dic['sha256'], BINNAME=bin_an_dic['bin_name'], VERSION=xml_dic['version'], ARCH=xml_dic['arch'], COMPILER_VERSION=xml_dic['compiler_version'], VISUAL_STUDIO_VERSION=xml_dic[ 'visual_studio_version'], VISUAL_STUDIO_EDITION=xml_dic[ 'visual_studio_edition'], TARGET_OS=xml_dic['target_os'], APPX_DLL_VERSION=xml_dic['appx_dll_version'], PROJ_GUID=xml_dic['proj_guid'], OPTI_TOOL=xml_dic['opti_tool'], TARGET_RUN=xml_dic['target_run'], FILES=app_dic['files'], STRINGS=bin_an_dic['strings'], BIN_AN_RESULTS=bin_an_dic['results'], BIN_AN_WARNINGS=bin_an_dic['warnings'], ) db_item.save() context = { 'title': 'Static Analysis', 'name': app_dic['app_name'], 'pub_name': xml_dic['pub_name'], 'size': app_dic['size'], 'md5': app_dic['md5'], 'sha1': app_dic['sha1'], 'sha256': app_dic['sha256'], 'bin_name': bin_an_dic['bin_name'], 'version': xml_dic['version'], 'arch': xml_dic['arch'], 'compiler_version': xml_dic['compiler_version'], 'visual_studio_version': xml_dic['visual_studio_version'], 'visual_studio_edition': xml_dic['visual_studio_edition'], 'target_os': xml_dic['target_os'], 'appx_dll_version': xml_dic['appx_dll_version'], 'proj_guid': xml_dic['proj_guid'], 'opti_tool': xml_dic['opti_tool'], 'target_run': xml_dic['target_run'], 'files': app_dic['files'], 'strings': bin_an_dic['strings'], 'bin_an_results': bin_an_dic['results'], 'bin_an_warnings': bin_an_dic['warnings'], } template = "static_analysis/windows_binary_analysis.html" if api: return context else: return render(request, template, context) else: msg = "File type not supported" if api: return print_n_send_error_response(request, msg, True) else: return print_n_send_error_response(request, msg, False) else: msg = "Hash match failed or Invalid file extension" if api: return print_n_send_error_response(request, msg, True) else: return print_n_send_error_response(request, msg, False) except Exception as exception: msg = str(exception) exp_doc = exception.__doc__ if api: return print_n_send_error_response(request, msg, True, exp_doc) else: return print_n_send_error_response(request, msg, False, exp_doc)
def pdf(request, api=False, jsonres=False): try: if api: checksum = request.POST['hash'] else: checksum = request.GET['md5'] hash_match = re.match('^[0-9a-f]{32}$', checksum) if not hash_match: if api: return {'error': 'Invalid scan hash'} else: return HttpResponse(json.dumps({'md5': 'Invalid scan hash'}), content_type=ctype, status=500) # Do Lookups android_static_db = StaticAnalyzerAndroid.objects.filter(MD5=checksum) ios_static_db = StaticAnalyzerIOS.objects.filter(MD5=checksum) win_static_db = StaticAnalyzerWindows.objects.filter(MD5=checksum) if android_static_db.exists(): context, template = handle_pdf_android(android_static_db) elif ios_static_db.exists(): context, template = handle_pdf_ios(ios_static_db) elif win_static_db.exists(): context, template = handle_pdf_win(win_static_db) else: if api: return {'report': 'Report not Found'} else: return HttpResponse(json.dumps({'report': 'Report not Found'}), content_type=ctype, status=500) # Do VT Scan only on binaries context['virus_total'] = None ext = os.path.splitext(context['file_name'].lower())[1] if settings.VT_ENABLED and ext != '.zip': app_bin = os.path.join(settings.UPLD_DIR, checksum + '/', checksum + ext) vt = VirusTotal.VirusTotal() context['virus_total'] = vt.get_result(app_bin, checksum) # Get Local Base URL proto = 'file://' host_os = 'nix' if platform.system() == 'Windows': proto = 'file:///' host_os = 'windows' context['base_url'] = proto + settings.BASE_DIR context['dwd_dir'] = proto + settings.DWD_DIR context['host_os'] = host_os context['timestamp'] = RecentScansDB.objects.get( MD5=checksum).TIMESTAMP try: if api and jsonres: return {'report_dat': context} else: options = { 'page-size': 'Letter', 'quiet': '', 'enable-local-file-access': '', 'no-collate': '', 'margin-top': '0.50in', 'margin-right': '0.50in', 'margin-bottom': '0.50in', 'margin-left': '0.50in', 'encoding': 'UTF-8', 'custom-header': [ ('Accept-Encoding', 'gzip'), ], 'no-outline': None, } # Added proxy support to wkhtmltopdf proxies, _ = upstream_proxy('https') if proxies['https']: options['proxy'] = proxies['https'] html = template.render(context) pdf_dat = pdfkit.from_string(html, False, options=options) if api: return {'pdf_dat': pdf_dat} return HttpResponse(pdf_dat, content_type='application/pdf') except Exception as exp: logger.exception('Error Generating PDF Report') if api: return { 'error': 'Cannot Generate PDF/JSON', 'err_details': str(exp) } else: return HttpResponse(json.dumps({ 'pdf_error': 'Cannot Generate PDF', 'err_details': str(exp) }), content_type=ctype, status=500) except Exception as exp: logger.exception('Error Generating PDF Report') msg = str(exp) exp = exp.__doc__ if api: return print_n_send_error_response(request, msg, True, exp) else: return print_n_send_error_response(request, msg, False, exp)
def activity_tester(request): """Activity Tester""" logger.info("Activity Tester") try: md5_hash = request.POST['md5'] package = request.POST['pkg'] if re.match('^[0-9a-f]{32}$', md5_hash): if re.findall(r";|\$\(|\|\||&&", package): return print_n_send_error_response(request, "Possible RCE Attack", True) if request.method == 'POST': base_dir = settings.BASE_DIR 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) data = {} adb = getADB() static_android_db = StaticAnalyzerAndroid.objects.filter( MD5=md5_hash) if static_android_db.exists(): logger.info("Fetching Activity List from DB") activities = python_list(static_android_db[0].ACTIVITIES) if activities: act_no = 0 logger.info("Starting Activity Tester...") logger.info("" + str(len(activities)) + " Activities Identified") for line in activities: try: act_no += 1 logger.info("Launching Activity - " + str(act_no) + ". " + line) adb_command( ["am", "start", "-n", package + "/" + line], True) # AVD is much slower, it should get extra time if settings.ANDROID_DYNAMIC_ANALYZER == "MobSF_AVD": wait(8) else: wait(4) adb_command( ["screencap", "-p", "/data/local/screen.png"], True) #? get appended from Air :-() if activity names are used adb_command(["pull", "/data/local/screen.png", screen_dir + "act-" + str(act_no) + ".png"]) logger.info("Activity Screenshot Taken") adb_command( ["am", "force-stop", package], True) logger.info("Stopping App") except: PrintException("Activity Tester") data = {'acttest': 'done'} else: logger.info("Activity Tester - No Activity Found!") data = {'acttest': 'noact'} return HttpResponse(json.dumps(data), content_type='application/json') else: return print_n_send_error_response(request, "Entry does not exist in DB", True) else: return print_n_send_error_response(request, "Only POST allowed", True) else: return print_n_send_error_response(request, "Invalid Scan Hash", True) except: PrintException("Activity Tester") return print_n_send_error_response(request, "Error Running Activity Tester", True)
def run(request, api=False): """View iOS Files.""" try: logger.info('View iOS Source File') file_format = 'cpp' if api: fil = request.POST['file'] md5_hash = request.POST['hash'] mode = request.POST['type'] viewsource_form = ViewSourceIOSApiForm(request.POST) else: fil = request.GET['file'] md5_hash = request.GET['md5'] mode = request.GET['type'] viewsource_form = ViewSourceIOSForm(request.GET) typ = set_ext_api(fil) if not viewsource_form.is_valid(): err = FormUtil.errors_message(viewsource_form) if api: return err context = { 'title': 'Error', 'exp': 'Error Description', 'doc': err, } template = 'general/error.html' return render(request, template, context, status=400) if mode == 'ipa': src = os.path.join(settings.UPLD_DIR, md5_hash + '/Payload/') elif mode == 'ios': src = os.path.join(settings.UPLD_DIR, md5_hash + '/') sfile = os.path.join(src, fil) dat = '' sql_dump = {} if typ == 'm': file_format = 'cpp' with io.open(sfile, mode='r', encoding='utf8', errors='ignore') as flip: dat = flip.read() elif typ == 'xml': file_format = 'xml' with io.open(sfile, mode='r', encoding='utf8', errors='ignore') as flip: dat = flip.read() elif typ == 'plist': file_format = 'json' dat = biplist.readPlist(sfile) try: dat = json.dumps(dat, indent=4, sort_keys=True) except Exception: pass elif typ == 'db': file_format = 'asciidoc' sql_dump = read_sqlite(sfile) elif typ == 'txt' and fil == 'classdump.txt': file_format = 'cpp' app_dir = os.path.join(settings.UPLD_DIR, md5_hash + '/') cls_dump_file = os.path.join(app_dir, 'classdump.txt') if is_file_exists(cls_dump_file): with io.open(cls_dump_file, mode='r', encoding='utf8', errors='ignore') as flip: dat = flip.read() else: dat = 'Class Dump result not Found' elif typ == 'txt': file_format = 'text' with io.open(sfile, mode='r', encoding='utf8', errors='ignore') as flip: dat = flip.read() else: if api: return {'error': 'Invalid Parameters'} return HttpResponseRedirect('/error/') context = { 'title': escape(ntpath.basename(fil)), 'file': escape(ntpath.basename(fil)), 'type': file_format, 'dat': dat, 'sql': sql_dump } template = 'general/view.html' if api: return context return render(request, template, context) except Exception as exp: logger.exception('Error Viewing Source') msg = str(exp) exp = exp.__doc__ if api: return print_n_send_error_response(request, msg, True, exp) else: return print_n_send_error_response(request, msg, False, exp)
def run(request, api=False): """View the source of a file.""" try: print("[INFO] View Android Source File") if api: fil = request.POST['file'] md5 = request.POST['hash'] typ = request.POST['type'] viewsource_form = ViewSourceAndroidApiForm(request.POST) else: fil = request.GET['file'] md5 = request.GET['md5'] typ = request.GET['type'] viewsource_form = ViewSourceAndroidForm(request.GET) if not viewsource_form.is_valid(): err = FormUtil.errors_message(viewsource_form) if api: return err context = { 'title': 'Error', 'exp': 'Error Description', 'doc': err } template = "general/error.html" return render(request, template, context, status=400) if fil.endswith('.java'): if typ == 'eclipse': src = os.path.join(settings.UPLD_DIR, md5 + '/src/') elif typ == 'studio': src = os.path.join( settings.UPLD_DIR, md5 + '/app/src/main/java/') elif typ == 'apk': src = os.path.join( settings.UPLD_DIR, md5 + '/java_source/') elif fil.endswith('.smali'): src = os.path.join(settings.UPLD_DIR, md5 + '/smali_source/') sfile = os.path.join(src, fil) dat = '' with io.open( sfile, mode='r', encoding="utf8", errors="ignore" ) as file_pointer: dat = file_pointer.read() context = { 'title': escape(ntpath.basename(fil)), 'file': escape(ntpath.basename(fil)), 'dat': dat } template = "static_analysis/view_source.html" if api: return context return render(request, template, context) except Exception as exp: msg = str(exp) exp = exp.__doc__ if api: return print_n_send_error_response(request, msg, True, exp) else: return print_n_send_error_response(request, msg, False, exp)
def staticanalyzer_windows(request, api=False): """Analyse a windows app.""" try: # Input validation print "[INFO] Windows Static Analysis Started" app_dic = {} # Dict to store the binary attributes if api: typ = request.POST['scan_type'] rescan = str(request.POST.get('re_scan', 0)) checksum = request.POST['hash'] filename = request.POST['file_name'] else: typ = request.GET['type'] rescan = str(request.GET.get('rescan', 0)) checksum = request.GET['checksum'] filename = request.GET['name'] md5_regex = re.match('^[0-9a-f]{32}$', checksum) if (md5_regex) and (typ in ['appx']): app_dic['app_name'] = filename # APP ORGINAL NAME app_dic['md5'] = checksum app_dic['app_dir'] = os.path.join( settings.UPLD_DIR, app_dic['md5'] + '/') app_dic['tools_dir'] = os.path.join( settings.BASE_DIR, 'StaticAnalyzer/tools/windows/') if typ == 'appx': # DB db_entry = StaticAnalyzerWindows.objects.filter( # pylint: disable-msg=E1101 MD5=app_dic['md5'] ) if db_entry.exists() and rescan == '0': print "\n[INFO] Analysis is already Done. Fetching data from the DB..." context = { 'title': db_entry[0].TITLE, 'name': db_entry[0].APP_NAME, 'pub_name': db_entry[0].PUB_NAME, 'size': db_entry[0].SIZE, 'md5': db_entry[0].MD5, 'sha1': db_entry[0].SHA1, 'sha256': db_entry[0].SHA256, 'bin_name': db_entry[0].BINNAME, 'version': db_entry[0].VERSION, 'arch': db_entry[0].ARCH, 'compiler_version': db_entry[0].COMPILER_VERSION, 'visual_studio_version': db_entry[0].VISUAL_STUDIO_VERSION, 'visual_studio_edition': db_entry[0].VISUAL_STUDIO_EDITION, 'target_os': db_entry[0].TARGET_OS, 'appx_dll_version': db_entry[0].APPX_DLL_VERSION, 'proj_guid': db_entry[0].PROJ_GUID, 'opti_tool': db_entry[0].OPTI_TOOL, 'target_run': db_entry[0].TARGET_RUN, 'files': python_list(db_entry[0].FILES), 'strings': python_list(db_entry[0].STRINGS), 'bin_an_results': python_list(db_entry[0].BIN_AN_RESULTS), 'bin_an_warnings': python_list(db_entry[0].BIN_AN_WARNINGS) } else: print "[INFO] Windows Binary Analysis Started" app_dic['app_path'] = os.path.join( app_dic['app_dir'], app_dic['md5'] + '.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']) # EXTRACT APPX print "[INFO] Extracting APPX" app_dic['files'] = unzip( app_dic['app_path'], app_dic['app_dir']) xml_dic = _parse_xml(app_dic['app_dir']) bin_an_dic = _binary_analysis(app_dic) # Saving to db print "\n[INFO] Connecting to DB" if rescan == '1': print "\n[INFO] Updating Database..." StaticAnalyzerWindows.objects.filter( # pylint: disable-msg=E1101 MD5=app_dic['md5'] ).update( TITLE='Static Analysis', APP_NAME=app_dic['app_name'], PUB_NAME=xml_dic['pub_name'], SIZE=app_dic['size'], MD5=app_dic['md5'], SHA1=app_dic['sha1'], SHA256=app_dic['sha256'], BINNAME=bin_an_dic['bin_name'], VERSION=xml_dic['version'], ARCH=xml_dic['arch'], COMPILER_VERSION=xml_dic['compiler_version'], VISUAL_STUDIO_VERSION=xml_dic[ 'visual_studio_version'], VISUAL_STUDIO_EDITION=xml_dic[ 'visual_studio_edition'], TARGET_OS=xml_dic['target_os'], APPX_DLL_VERSION=xml_dic['appx_dll_version'], PROJ_GUID=xml_dic['proj_guid'], OPTI_TOOL=xml_dic['opti_tool'], TARGET_RUN=xml_dic['target_run'], FILES=app_dic['files'], STRINGS=bin_an_dic['strings'], BIN_AN_RESULTS=bin_an_dic['results'], BIN_AN_WARNINGS=bin_an_dic['warnings'], ) elif rescan == '0': print "\n[INFO] Saving to Database" db_item = StaticAnalyzerWindows( TITLE='Static Analysis', APP_NAME=app_dic['app_name'], PUB_NAME=xml_dic['pub_name'], SIZE=app_dic['size'], MD5=app_dic['md5'], SHA1=app_dic['sha1'], SHA256=app_dic['sha256'], BINNAME=bin_an_dic['bin_name'], VERSION=xml_dic['version'], ARCH=xml_dic['arch'], COMPILER_VERSION=xml_dic['compiler_version'], VISUAL_STUDIO_VERSION=xml_dic[ 'visual_studio_version'], VISUAL_STUDIO_EDITION=xml_dic[ 'visual_studio_edition'], TARGET_OS=xml_dic['target_os'], APPX_DLL_VERSION=xml_dic['appx_dll_version'], PROJ_GUID=xml_dic['proj_guid'], OPTI_TOOL=xml_dic['opti_tool'], TARGET_RUN=xml_dic['target_run'], FILES=app_dic['files'], STRINGS=bin_an_dic['strings'], BIN_AN_RESULTS=bin_an_dic['results'], BIN_AN_WARNINGS=bin_an_dic['warnings'], ) db_item.save() context = { 'title': 'Static Analysis', 'name': app_dic['app_name'], 'pub_name': xml_dic['pub_name'], 'size': app_dic['size'], 'md5': app_dic['md5'], 'sha1': app_dic['sha1'], 'sha256': app_dic['sha256'], 'bin_name': bin_an_dic['bin_name'], 'version': xml_dic['version'], 'arch': xml_dic['arch'], 'compiler_version': xml_dic['compiler_version'], 'visual_studio_version': xml_dic['visual_studio_version'], 'visual_studio_edition': xml_dic['visual_studio_edition'], 'target_os': xml_dic['target_os'], 'appx_dll_version': xml_dic['appx_dll_version'], 'proj_guid': xml_dic['proj_guid'], 'opti_tool': xml_dic['opti_tool'], 'target_run': xml_dic['target_run'], 'files': app_dic['files'], 'strings': bin_an_dic['strings'], 'bin_an_results': bin_an_dic['results'], 'bin_an_warnings': bin_an_dic['warnings'], } template = "static_analysis/windows_binary_analysis.html" if api: return context else: return render(request, template, context) else: msg = "File type not supported" if api: return print_n_send_error_response(request, msg, True) else: return print_n_send_error_response(request, msg, False) else: msg = "Hash match failed or Invalid file extension" if api: return print_n_send_error_response(request, msg, True) else: return print_n_send_error_response(request, msg, False) except Exception as exception: msg = str(exception) exp_doc = exception.__doc__ if api: return print_n_send_error_response(request, msg, True, exp_doc) else: return print_n_send_error_response(request, msg, False, exp_doc)
def generic_compare(request, first_hash: str, second_hash: str, api: bool = False): # This context consists of specific lists and analysis that is done on the classic ones # it will be filled during the different diff analysis context = { 'title': 'Compare report', 'first_app': dict(), 'second_app': dict(), 'urls': dict(), 'api': dict(), 'permissions': dict() } static_fields = ['md5', 'name', 'size', 'icon_found', 'icon_hidden', 'act_count', 'e_act', 'serv_count', 'e_ser', 'bro_count', 'e_bro', 'prov_count', 'e_cnt', 'apkid'] # For now - support only android db_entry = StaticAnalyzerAndroid.objects.filter(MD5=first_hash) db_entry2 = StaticAnalyzerAndroid.objects.filter(MD5=second_hash) if not (db_entry.exists() and db_entry2.exists()): return print_n_send_error_response(request, "One of the Hashes wasn't found in the Android-results DB, make sure " "both of the apps finished analysis & they are both Android", False) # First fetch the already done analysis on each of the apps # We don't want to return this whole context back to the user because its a lot of data we don't use # it should help the performance I guess first_app = deepcopy(get_context_from_db_entry(db_entry)) second_app = deepcopy(get_context_from_db_entry(db_entry2)) # Second, fill the mutual static parts that are missing in the classic analysis for curr_app, db_context in [('first_app', first_app), ('second_app', second_app)]: # format informative title context[curr_app]['name_ver'] = "{0} - {1}".format(db_context['packagename'], db_context['androvername']) # Fill all the static information for static_attr in static_fields: context[curr_app][static_attr] = db_context[static_attr] # Get only the subject of the cert subject_regex = re.compile(r'.*Subject:([^<]+)</br>.*', re.DOTALL) match = subject_regex.match(db_context['certinfo']) if match: context[curr_app]['cert_subject'] = match.group(1) else: context[curr_app]['cert_subject'] = "No subject" # Some preparations so we have some sort of same structures # (urls are lists inside the list which mess things up...) tmp_list = list() for url_obj in db_context['urls']: for url in url_obj['urls']: # urls can mess up the table because they can be really long, so let's cut them tmp_url = url[:70] while len(url) > 70: url = url[70:] tmp_url += '<br />' tmp_url += url[:70] tmp_list.append(tmp_url) db_context['urls'] = list(set(deepcopy(tmp_list))) tmp_list.clear() # apkid check - we do it here just because its really ugly inside the template # I split it into these lines just it to be really clear what I'm checking first_error = context['first_app']['apkid'].get('error', False) second_error = context['second_app']['apkid'].get('error', False) first_has_version = context['first_app']['apkid'].get('apkid_version', False) second_has_version = context['second_app']['apkid'].get('apkid_version', False) context['apkid_error'] = (first_error or not first_has_version) or (second_error or not second_has_version) # Third, calculate some diffs for section, is_tuples in [ ('permissions', True), ('api', True), ('urls', False) ]: if is_tuples: context[section]['mutual'] = [(x, y) for (x, y) in first_app[section].items() if x in second_app[section].keys()] # Only first context[section]['only_first'] = [(x, y) for (x, y) in first_app[section].items() if x not in second_app[section].keys()] # Only Second context[section]['only_second'] = [(x, y) for (x, y) in second_app[section].items() if x not in first_app[section].keys()] else: context[section]['mutual'] = [x for x in first_app[section] if x in second_app[section]] context[section]['only_first'] = [x for x in first_app[section] if x not in second_app[section]] context[section]['only_second'] = [x for x in second_app[section] if x not in first_app[section]] template = "static_analysis/compare.html" if api: return context else: return render(request, template, context)
def report(request): """Dynamic Analysis Report Generation""" logger.info("Dynamic Analysis Report Generation") try: if request.method == 'GET': md5_hash = request.GET['md5'] package = request.GET['pkg'] if re.findall(r";|\$\(|\|\||&&", package): return print_n_send_error_response(request, "Possible RCE Attack") if re.match('^[0-9a-f]{32}$', md5_hash): app_dir = os.path.join( settings.UPLD_DIR, md5_hash + '/') # APP DIRECTORY download_dir = settings.DWD_DIR droidmon_api_loc = os.path.join(app_dir, 'x_logcat.txt') api_analysis_result = api_analysis(package, droidmon_api_loc) analysis_result = run_analysis(app_dir, md5_hash, package) download(md5_hash, download_dir, app_dir, package) # Only After Download Process is Done imgs = [] act_imgs = [] act = {} expact_imgs = [] exp_act = {} if os.path.exists(os.path.join(download_dir, md5_hash + "-screenshots-apk/")): try: imp_path = os.path.join( download_dir, md5_hash + "-screenshots-apk/") for img in os.listdir(imp_path): if img.endswith(".png"): if img.startswith("act"): act_imgs.append(img) elif img.startswith("expact"): expact_imgs.append(img) else: imgs.append(img) static_android_db = StaticAnalyzerAndroid.objects.filter( MD5=md5_hash) if static_android_db.exists(): logger.info( "\nFetching Exported Activity & Activity List from DB") exported_act = python_list( static_android_db[0].EXPORTED_ACT) act_desc = python_list( static_android_db[0].ACTIVITIES) if act_imgs: if len(act_imgs) == len(act_desc): act = dict(list(zip(act_imgs, act_desc))) if expact_imgs: if len(expact_imgs) == len(exported_act): exp_act = dict( list(zip(expact_imgs, exported_act))) else: logger.warning("Entry does not exists in the DB.") except: PrintException("Screenshot Sorting") context = {'md5': md5_hash, 'emails': analysis_result["emails"], 'urls': analysis_result["urls"], 'domains': analysis_result["domains"], 'clipboard': analysis_result["clipboard"], 'http': analysis_result["web_data"], 'xml': analysis_result["xmlfiles"], 'sqlite': analysis_result["sqlite_db"], 'others': analysis_result["other_files"], 'imgs': imgs, 'acttest': act, 'expacttest': exp_act, 'net': api_analysis_result["api_net"], 'base64': api_analysis_result["api_base64"], 'crypto': api_analysis_result["api_crypto"], 'fileio': api_analysis_result["api_fileio"], 'binder': api_analysis_result["api_binder"], 'divinfo': api_analysis_result["api_deviceinfo"], 'cntval': api_analysis_result["api_cntvl"], 'sms': api_analysis_result["api_sms"], 'sysprop': api_analysis_result["api_sysprop"], 'dexload': api_analysis_result["api_dexloader"], 'reflect': api_analysis_result["api_reflect"], 'sysman': api_analysis_result["api_acntmnger"], 'process': api_analysis_result["api_cmd"], 'pkg': package, 'title': 'Dynamic Analysis'} template = "dynamic_analysis/dynamic_analysis.html" return render(request, template, context) else: return print_n_send_error_response(request, "Invalid Scan Hash") else: return print_n_send_error_response(request, "Only GET allowed") except: PrintException("Dynamic Analysis Report Generation") return print_n_send_error_response(request, "Error Geneating Dynamic Analysis Report")
def static_analyzer(request, api=False): """Do static analysis on an request and save to db.""" try: if api: typ = request.POST['scan_type'] checksum = request.POST['hash'] filename = request.POST['file_name'] rescan = str(request.POST.get('re_scan', 0)) else: typ = request.GET['type'] checksum = request.GET['checksum'] filename = request.GET['name'] rescan = str(request.GET.get('rescan', 0)) # Input validation app_dic = {} match = re.match('^[0-9a-f]{32}$', checksum) if ((match) and (filename.lower().endswith('.apk') or filename.lower().endswith('.zip')) and (typ in ['zip', 'apk'])): app_dic['dir'] = settings.BASE_DIR # BASE DIR app_dic['app_name'] = filename # APP ORGINAL NAME app_dic['md5'] = checksum # MD5 app_dic['app_dir'] = os.path.join( settings.UPLD_DIR, app_dic['md5'] + '/') # APP DIRECTORY app_dic['tools_dir'] = os.path.join( app_dic['dir'], 'StaticAnalyzer/tools/') # TOOLS DIR # DWD_DIR = settings.DWD_DIR # not needed? Var is never used. print "[INFO] Starting Analysis on : " + app_dic['app_name'] if typ == 'apk': # Check if in DB # pylint: disable=E1101 db_entry = StaticAnalyzerAndroid.objects.filter( MD5=app_dic['md5']) if db_entry.exists() and rescan == '0': context = get_context_from_db_entry(db_entry) else: app_dic[ 'app_file'] = app_dic['md5'] + '.apk' # NEW FILENAME app_dic['app_path'] = app_dic['app_dir'] + \ app_dic['app_file'] # APP PATH # ANALYSIS BEGINS 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['files'] = unzip(app_dic['app_path'], app_dic['app_dir']) app_dic['certz'] = get_hardcoded_cert_keystore( app_dic['files']) print "[INFO] APK Extracted" # Manifest XML app_dic['parsed_xml'] = get_manifest( app_dic['app_dir'], app_dic['tools_dir'], '', True) # Get icon res_path = os.path.join(app_dic['app_dir'], 'res') app_dic['icon_hidden'] = True app_dic[ 'icon_found'] = False # Even if the icon is hidden, try to guess it by the default paths app_dic['icon_path'] = '' if os.path.exists( res_path ): # TODO: Check for possible different names for resource folder? icon_dic = get_icon(app_dic['app_path'], res_path, app_dic['tools_dir']) if icon_dic: app_dic['icon_hidden'] = icon_dic['hidden'] app_dic['icon_found'] = bool(icon_dic['path']) app_dic['icon_path'] = icon_dic['path'] # Set Manifest link app_dic['mani'] = '../ManifestView/?md5=' + \ app_dic['md5'] + '&type=apk&bin=1' man_data_dic = manifest_data(app_dic['parsed_xml']) man_an_dic = manifest_analysis(app_dic['parsed_xml'], man_data_dic) bin_an_buff = [] bin_an_buff += elf_analysis(app_dic['app_dir'], "apk") bin_an_buff += res_analysis(app_dic['app_dir'], "apk") cert_dic = cert_info(app_dic['app_dir'], app_dic['tools_dir']) apkid_results = apkid_analysis(app_dic['app_dir']) dex_2_jar(app_dic['app_path'], app_dic['app_dir'], app_dic['tools_dir']) dex_2_smali(app_dic['app_dir'], app_dic['tools_dir']) jar_2_java(app_dic['app_dir'], app_dic['tools_dir']) code_an_dic = code_analysis(app_dic['app_dir'], man_an_dic['permissons'], "apk") print "\n[INFO] Generating Java and Smali Downloads" gen_downloads(app_dic['app_dir'], app_dic['md5'], app_dic['icon_path']) # Get the strings app_dic['strings'] = strings(app_dic['app_file'], app_dic['app_dir'], app_dic['tools_dir']) app_dic['zipped'] = '&type=apk' print "\n[INFO] Connecting to Database" try: # SAVE TO DB if rescan == '1': print "\n[INFO] Updating Database..." update_db_entry( app_dic, man_data_dic, man_an_dic, code_an_dic, cert_dic, bin_an_buff, apkid_results, ) elif rescan == '0': print "\n[INFO] Saving to Database" create_db_entry( app_dic, man_data_dic, man_an_dic, code_an_dic, cert_dic, bin_an_buff, apkid_results, ) except: PrintException("[ERROR] Saving to Database Failed") context = get_context_from_analysis( app_dic, man_data_dic, man_an_dic, code_an_dic, cert_dic, bin_an_buff, apkid_results, ) context['dynamic_analysis_done'] = os.path.exists( os.path.join(app_dic['app_dir'], 'logcat.txt')) template = "static_analysis/static_analysis.html" if api: return context else: return render(request, template, context) elif typ == 'zip': # Check if in DB # pylint: disable=E1101 cert_dic = {} cert_dic['cert_info'] = '' cert_dic['issued'] = '' bin_an_buff = [] app_dic['strings'] = '' app_dic['zipped'] = '' # Above fields are only available for APK and not ZIP db_entry = StaticAnalyzerAndroid.objects.filter( MD5=app_dic['md5']) if db_entry.exists() and rescan == '0': context = get_context_from_db_entry(db_entry) else: app_dic[ 'app_file'] = app_dic['md5'] + '.zip' # NEW FILENAME app_dic['app_path'] = app_dic['app_dir'] + \ app_dic['app_file'] # APP PATH print "[INFO] Extracting ZIP" app_dic['files'] = unzip(app_dic['app_path'], app_dic['app_dir']) # Check if Valid Directory Structure and get ZIP Type pro_type, valid = valid_android_zip(app_dic['app_dir']) if valid and pro_type == 'ios': print "[INFO] Redirecting to iOS Source Code Analyzer" if api: return {"type": "ios"} else: return HttpResponseRedirect( '/StaticAnalyzer_iOS/?name=' + app_dic['app_name'] + '&type=ios&checksum=' + app_dic['md5']) app_dic['certz'] = get_hardcoded_cert_keystore( app_dic['files']) app_dic['zipped'] = pro_type print "[INFO] ZIP Type - " + pro_type if valid and (pro_type in ['eclipse', 'studio']): # ANALYSIS BEGINS 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']) # Manifest XML app_dic['persed_xml'] = get_manifest( app_dic['app_dir'], app_dic['tools_dir'], pro_type, False) # Set manifest view link app_dic['mani'] = ('../ManifestView/?md5=' + app_dic['md5'] + '&type=' + pro_type + '&bin=0') man_data_dic = manifest_data(app_dic['persed_xml']) man_an_dic = manifest_analysis(app_dic['persed_xml'], man_data_dic) # Get icon eclipse_res_path = os.path.join( app_dic['app_dir'], 'res') studio_res_path = os.path.join(app_dic['app_dir'], 'app', 'src', 'main', 'res') if os.path.exists(eclipse_res_path): res_path = eclipse_res_path elif os.path.exists(studio_res_path): res_path = studio_res_path else: res_path = '' app_dic['icon_hidden'] = man_an_dic['icon_hidden'] app_dic['icon_found'] = False app_dic['icon_path'] = '' if res_path: app_dic['icon_path'] = find_icon_path_zip( res_path, man_data_dic['icons']) if app_dic['icon_path']: app_dic['icon_found'] = True if app_dic['icon_path']: if os.path.exists(app_dic['icon_path']): shutil.copy2( app_dic['icon_path'], os.path.join(settings.DWD_DIR, app_dic['md5'] + '-icon.png')) code_an_dic = code_analysis(app_dic['app_dir'], man_an_dic['permissons'], pro_type) print "\n[INFO] Connecting to Database" try: # SAVE TO DB if rescan == '1': print "\n[INFO] Updating Database..." update_db_entry( app_dic, man_data_dic, man_an_dic, code_an_dic, cert_dic, bin_an_buff, {}, ) elif rescan == '0': print "\n[INFO] Saving to Database" create_db_entry( app_dic, man_data_dic, man_an_dic, code_an_dic, cert_dic, bin_an_buff, {}, ) except: PrintException("[ERROR] Saving to Database Failed") context = get_context_from_analysis( app_dic, man_data_dic, man_an_dic, code_an_dic, cert_dic, bin_an_buff, {}, ) else: msg = "This ZIP Format is not supported" if api: return print_n_send_error_response( request, msg, True) else: print_n_send_error_response(request, msg, False) return HttpResponseRedirect('/zip_format/') template = "static_analysis/static_analysis_android_zip.html" if api: return context else: return render(request, template, context) else: print "\n[ERROR] Only APK,IPA and Zipped Android/iOS Source code supported now!" else: msg = "Hash match failed or Invalid file extension or file type" if api: return print_n_send_error_response(request, msg, True) else: return print_n_send_error_response(request, msg, False) except Exception as excep: msg = str(excep) exp = excep.__doc__ if api: return print_n_send_error_response(request, msg, True, exp) else: return print_n_send_error_response(request, msg, False, exp)
def dynamic_analyzer(request): """Android Dynamic Analyzer Environment.""" logger.info('Creating Dynamic Analysis Environment') try: bin_hash = request.GET['hash'] package = request.GET['package'] no_device = False if (is_attack_pattern(package) or not is_md5(bin_hash)): return print_n_send_error_response(request, 'Invalid Parameters') 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 MobSF/settings.py') return print_n_send_error_response(request, msg) env = Environment(identifier) if not env.connect_n_mount(): msg = 'Cannot Connect to ' + identifier return print_n_send_error_response(request, msg) 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.\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') 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) # Clean up previous analysis env.dz_cleanup(bin_hash) # 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() logger.info('Installing APK') app_dir = os.path.join(settings.UPLD_DIR, bin_hash + '/') # APP DIRECTORY apk_path = app_dir + bin_hash + '.apk' # APP PATH env.adb_command(['install', '-r', apk_path], False, True) logger.info('Testing Environment is Ready!') context = {'screen_witdth': screen_width, 'screen_height': screen_height, 'package': package, 'md5': bin_hash, 'android_version': version, 'version': settings.MOBSF_VER, 'title': 'Dynamic Analyzer'} template = 'dynamic_analysis/android/dynamic_analyzer.html' return render(request, template, context) except Exception: logger.exception('Dynamic Analyzer') return print_n_send_error_response(request, 'Dynamic Analysis Failed.')
def static_analyzer(request, api=False): """Do static analysis on an request and save to db.""" try: if api: typ = request.POST['scan_type'] checksum = request.POST['hash'] filename = request.POST['file_name'] rescan = str(request.POST.get('re_scan', 0)) else: typ = request.GET['type'] checksum = request.GET['checksum'] filename = request.GET['name'] rescan = str(request.GET.get('rescan', 0)) # Input validation app_dic = {} match = re.match('^[0-9a-f]{32}$', checksum) if ((match) and (filename.lower().endswith('.apk') or filename.lower().endswith('.zip')) and (typ in ['zip', 'apk'])): app_dic['dir'] = Path(settings.BASE_DIR) # BASE DIR app_dic['app_name'] = filename # APP ORGINAL NAME app_dic['md5'] = checksum # MD5 # 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() logger.info('Starting Analysis on : %s', app_dic['app_name']) if typ == 'apk': app_dic['app_file'] = app_dic['md5'] + '.apk' # 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() + '/' # Check if in DB # pylint: disable=E1101 db_entry = StaticAnalyzerAndroid.objects.filter( MD5=app_dic['md5']) if db_entry.exists() and rescan == '0': context = get_context_from_db_entry(db_entry) else: # ANALYSIS BEGINS 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['files'] = unzip(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' if api: return print_n_send_error_response( request, msg, True) else: return print_n_send_error_response( request, msg, False) app_dic['certz'] = get_hardcoded_cert_keystore( app_dic['files']) # Manifest XML mani_file, mani_xml = get_manifest( app_dic['app_path'], app_dic['app_dir'], app_dic['tools_dir'], '', True, ) 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'], app_dic['tools_dir'], True, ) # Get icon res_path = os.path.join(app_dic['app_dir'], 'res') app_dic['icon_hidden'] = True # Even if the icon is hidden, try to guess it by the # default paths app_dic['icon_found'] = False app_dic['icon_path'] = '' # TODO: Check for possible different names for resource # folder? if os.path.exists(res_path): icon_dic = get_icon(app_dic['app_path'], res_path) if icon_dic: app_dic['icon_hidden'] = icon_dic['hidden'] app_dic['icon_found'] = bool(icon_dic['path']) app_dic['icon_path'] = icon_dic['path'] # Set Manifest link app_dic['mani'] = ('../ManifestView/?md5=' + app_dic['md5'] + '&type=apk&bin=1') man_data_dic = manifest_data(app_dic['parsed_xml']) app_dic['playstore'] = get_app_details( man_data_dic['packagename']) man_an_dic = manifest_analysis( app_dic['parsed_xml'], man_data_dic, '', app_dic['app_dir'], ) elf_dict = elf_analysis(app_dic['app_dir']) cert_dic = cert_info(app_dic['app_dir'], app_dic['app_file']) apkid_results = apkid_analysis(app_dic['app_dir'], app_dic['app_path'], app_dic['app_name']) tracker = Trackers.Trackers(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']) code_an_dic = code_analysis(app_dic['app_dir'], 'apk', app_dic['manifest_file']) # Get the strings from android resource and shared objects string_res = strings_from_apk(app_dic['app_file'], app_dic['app_dir'], elf_dict['elf_strings']) if string_res: app_dic['strings'] = string_res['strings'] app_dic['secrets'] = string_res['secrets'] code_an_dic['urls_list'].extend( string_res['urls_list']) code_an_dic['urls'].extend(string_res['url_nf']) code_an_dic['emails'].extend(string_res['emails_nf']) else: app_dic['strings'] = [] app_dic['secrets'] = [] # Firebase DB Check code_an_dic['firebase'] = firebase_analysis( list(set(code_an_dic['urls_list']))) # Domain Extraction and Malware Check logger.info( 'Performing Malware Check on extracted Domains') code_an_dic['domains'] = malware_check( list(set(code_an_dic['urls_list']))) # Copy App icon copy_icon(app_dic['md5'], app_dic['icon_path']) app_dic['zipped'] = 'apk' logger.info('Connecting to Database') try: # SAVE TO DB if rescan == '1': logger.info('Updating Database...') save_or_update( 'update', app_dic, man_data_dic, man_an_dic, code_an_dic, cert_dic, elf_dict['elf_analysis'], apkid_results, tracker_res, ) update_scan_timestamp(app_dic['md5']) elif rescan == '0': logger.info('Saving to Database') save_or_update( 'save', app_dic, man_data_dic, man_an_dic, code_an_dic, cert_dic, elf_dict['elf_analysis'], apkid_results, tracker_res, ) except Exception: logger.exception('Saving to Database Failed') context = get_context_from_analysis( app_dic, man_data_dic, man_an_dic, code_an_dic, cert_dic, elf_dict['elf_analysis'], apkid_results, tracker_res, ) context['average_cvss'], context['security_score'] = score( context['code_analysis']) context['dynamic_analysis_done'] = is_file_exists( os.path.join(app_dic['app_dir'], 'logcat.txt')) context['virus_total'] = None if settings.VT_ENABLED: vt = VirusTotal.VirusTotal() context['virus_total'] = vt.get_result( app_dic['app_path'], app_dic['md5']) template = 'static_analysis/android_binary_analysis.html' if api: return context else: return render(request, template, context) elif typ == 'zip': ios_ret = HttpResponseRedirect('/StaticAnalyzer_iOS/?name=' + app_dic['app_name'] + '&type=ios&checksum=' + app_dic['md5']) # 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_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']) ios_db_entry = StaticAnalyzerIOS.objects.filter( MD5=app_dic['md5']) if db_entry.exists() and rescan == '0': context = get_context_from_db_entry(db_entry) elif ios_db_entry.exists() and rescan == '0': if api: return {'type': 'ios'} else: return ios_ret else: logger.info('Extracting ZIP') app_dic['files'] = unzip(app_dic['app_path'], app_dic['app_dir']) # Check if Valid Directory Structure and get ZIP Type pro_type, valid = valid_android_zip(app_dic['app_dir']) if valid and pro_type == 'ios': logger.info('Redirecting to iOS Source Code Analyzer') if api: return {'type': 'ios'} else: return ios_ret app_dic['certz'] = get_hardcoded_cert_keystore( app_dic['files']) app_dic['zipped'] = pro_type logger.info('ZIP Type - %s', pro_type) if valid and (pro_type in ['eclipse', 'studio']): # ANALYSIS BEGINS 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']) # Manifest XML mani_file, mani_xml = get_manifest( '', app_dic['app_dir'], app_dic['tools_dir'], pro_type, 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'], app_dic['tools_dir'], False, ) # Set manifest view link app_dic['mani'] = ('../ManifestView/?md5=' + app_dic['md5'] + '&type=' + pro_type + '&bin=0') man_data_dic = manifest_data(app_dic['parsed_xml']) app_dic['playstore'] = get_app_details( man_data_dic['packagename']) man_an_dic = manifest_analysis( app_dic['parsed_xml'], man_data_dic, pro_type, app_dic['app_dir'], ) # Get icon eclipse_res_path = os.path.join( app_dic['app_dir'], 'res') studio_res_path = os.path.join(app_dic['app_dir'], 'app', 'src', 'main', 'res') if os.path.exists(eclipse_res_path): res_path = eclipse_res_path elif os.path.exists(studio_res_path): res_path = studio_res_path else: res_path = '' app_dic['icon_hidden'] = man_an_dic['icon_hidden'] app_dic['icon_found'] = False app_dic['icon_path'] = '' if res_path: app_dic['icon_path'] = find_icon_path_zip( res_path, man_data_dic['icons']) if app_dic['icon_path']: app_dic['icon_found'] = True if app_dic['icon_path']: if os.path.exists(app_dic['icon_path']): shutil.copy2( app_dic['icon_path'], os.path.join(settings.DWD_DIR, app_dic['md5'] + '-icon.png')) code_an_dic = code_analysis(app_dic['app_dir'], pro_type, app_dic['manifest_file']) # Firebase DB Check code_an_dic['firebase'] = firebase_analysis( list(set(code_an_dic['urls_list']))) # Domain Extraction and Malware Check logger.info( 'Performing Malware Check on extracted Domains') code_an_dic['domains'] = malware_check( list(set(code_an_dic['urls_list']))) logger.info('Connecting to Database') try: # SAVE TO DB if rescan == '1': logger.info('Updating Database...') save_or_update( 'update', app_dic, man_data_dic, man_an_dic, code_an_dic, cert_dic, [], {}, {}, ) update_scan_timestamp(app_dic['md5']) elif rescan == '0': logger.info('Saving to Database') save_or_update( 'save', app_dic, man_data_dic, man_an_dic, code_an_dic, cert_dic, [], {}, {}, ) except Exception: logger.exception('Saving to Database Failed') context = get_context_from_analysis( app_dic, man_data_dic, man_an_dic, code_an_dic, cert_dic, [], {}, {}, ) else: msg = 'This ZIP Format is not supported' if api: return print_n_send_error_response( request, msg, True) else: print_n_send_error_response(request, msg, False) ctx = { 'title': 'Invalid ZIP archive', 'version': settings.MOBSF_VER, } template = 'general/zip.html' return render(request, template, ctx) context['average_cvss'], context['security_score'] = score( context['code_analysis']) template = 'static_analysis/android_source_analysis.html' if api: return context else: return render(request, template, context) else: err = ('Only APK,IPA and Zipped ' 'Android/iOS Source code supported now!') logger.error(err) else: msg = 'Hash match failed or Invalid file extension or file type' if api: return print_n_send_error_response(request, msg, True) else: return print_n_send_error_response(request, msg, False) except Exception as excep: logger.exception('Error Performing Static Analysis') msg = str(excep) exp = excep.__doc__ if api: return print_n_send_error_response(request, msg, True, exp) else: return print_n_send_error_response(request, msg, False, exp)
def static_analyzer(request, api=False): """Do static analysis on an request and save to db.""" try: if api: typ = request.POST['scan_type'] checksum = request.POST['hash'] filename = request.POST['file_name'] rescan = str(request.POST.get('re_scan', 0)) else: typ = request.GET['type'] checksum = request.GET['checksum'] filename = request.GET['name'] rescan = str(request.GET.get('rescan', 0)) # Input validation app_dic = {} match = re.match('^[0-9a-f]{32}$', checksum) if ( ( match ) and ( filename.lower().endswith('.apk') or filename.lower().endswith('.zip') ) and ( typ in ['zip', 'apk'] ) ): app_dic['dir'] = settings.BASE_DIR # BASE DIR app_dic['app_name'] = filename # APP ORGINAL NAME app_dic['md5'] = checksum # MD5 app_dic['app_dir'] = os.path.join(settings.UPLD_DIR, app_dic[ 'md5'] + '/') # APP DIRECTORY app_dic['tools_dir'] = os.path.join( app_dic['dir'], 'StaticAnalyzer/tools/') # TOOLS DIR # DWD_DIR = settings.DWD_DIR # not needed? Var is never used. logger.info("Starting Analysis on : " + app_dic['app_name']) if typ == 'apk': # Check if in DB # pylint: disable=E1101 db_entry = StaticAnalyzerAndroid.objects.filter( MD5=app_dic['md5']) if db_entry.exists() and rescan == '0': context = get_context_from_db_entry(db_entry) else: app_dic['app_file'] = app_dic[ 'md5'] + '.apk' # NEW FILENAME app_dic['app_path'] = app_dic['app_dir'] + \ app_dic['app_file'] # APP PATH # ANALYSIS BEGINS 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['files'] = unzip( app_dic['app_path'], app_dic['app_dir']) app_dic['certz'] = get_hardcoded_cert_keystore(app_dic[ 'files']) logger.info("APK Extracted") # Manifest XML app_dic['parsed_xml'] = get_manifest( app_dic['app_path'], app_dic['app_dir'], app_dic['tools_dir'], '', True ) # Get icon res_path = os.path.join(app_dic['app_dir'], 'res') app_dic['icon_hidden'] = True # Even if the icon is hidden, try to guess it by the # default paths app_dic['icon_found'] = False app_dic['icon_path'] = '' # TODO: Check for possible different names for resource # folder? if os.path.exists(res_path): icon_dic = get_icon( app_dic['app_path'], res_path) if icon_dic: app_dic['icon_hidden'] = icon_dic['hidden'] app_dic['icon_found'] = bool(icon_dic['path']) app_dic['icon_path'] = icon_dic['path'] # Set Manifest link app_dic['mani'] = '../ManifestView/?md5=' + \ app_dic['md5'] + '&type=apk&bin=1' man_data_dic = manifest_data(app_dic['parsed_xml']) app_dic['playstore'] = get_app_details( man_data_dic['packagename']) man_an_dic = manifest_analysis( app_dic['parsed_xml'], man_data_dic ) bin_an_buff = [] bin_an_buff += elf_analysis(app_dic['app_dir']) bin_an_buff += res_analysis(app_dic['app_dir']) cert_dic = cert_info( app_dic['app_dir'], app_dic['app_file'], app_dic['tools_dir']) apkid_results = apkid_analysis(app_dic[ 'app_dir'], app_dic['app_path'], app_dic['app_name']) dex_2_jar(app_dic['app_path'], app_dic[ 'app_dir'], app_dic['tools_dir']) dex_2_smali(app_dic['app_dir'], app_dic['tools_dir']) jar_2_java(app_dic['app_dir'], app_dic['tools_dir']) code_an_dic = code_analysis( app_dic['app_dir'], man_an_dic['permissons'], "apk" ) # Get the strings string_res = strings_jar( app_dic['app_file'], app_dic['app_dir'] ) if string_res: app_dic['strings'] = string_res['strings'] code_an_dic["urls_list"].extend( string_res['urls_list']) code_an_dic["urls"].extend(string_res['url_nf']) code_an_dic["emails"].extend(string_res['emails_nf']) else: app_dic['strings'] = [] # Firebase DB Check code_an_dic['firebase'] = firebase_analysis( list(set(code_an_dic["urls_list"]))) # Domain Extraction and Malware Check logger.info( "Performing Malware Check on extracted Domains") code_an_dic["domains"] = malware_check( list(set(code_an_dic["urls_list"]))) logger.info("Generating Java and Smali Downloads") gen_downloads(app_dic['app_dir'], app_dic[ 'md5'], app_dic['icon_path']) app_dic['zipped'] = '&type=apk' logger.info("Connecting to Database") try: # SAVE TO DB if rescan == '1': logger.info("Updating Database...") update_db_entry( app_dic, man_data_dic, man_an_dic, code_an_dic, cert_dic, bin_an_buff, apkid_results, ) update_scan_timestamp(app_dic['md5']) elif rescan == '0': logger.info("Saving to Database") create_db_entry( app_dic, man_data_dic, man_an_dic, code_an_dic, cert_dic, bin_an_buff, apkid_results, ) except: PrintException("Saving to Database Failed") context = get_context_from_analysis( app_dic, man_data_dic, man_an_dic, code_an_dic, cert_dic, bin_an_buff, apkid_results, ) context["average_cvss"], context[ "security_score"] = score(context["findings"]) context['dynamic_analysis_done'] = os.path.exists( os.path.join(app_dic['app_dir'], 'logcat.txt')) context['VT_RESULT'] = None if settings.VT_ENABLED: vt = VirusTotal.VirusTotal() context['VT_RESULT'] = vt.get_result( os.path.join(app_dic['app_dir'], app_dic['md5']) + '.apk', app_dic['md5'] ) template = "static_analysis/android_binary_analysis.html" if api: return context else: return render(request, template, context) elif typ == 'zip': # Check if in DB # pylint: disable=E1101 cert_dic = {} cert_dic['cert_info'] = '' cert_dic['issued'] = '' cert_dic['sha256Digest'] = False bin_an_buff = [] app_dic['strings'] = '' app_dic['zipped'] = '' # Above fields are only available for APK and not ZIP db_entry = StaticAnalyzerAndroid.objects.filter( MD5=app_dic['md5']) if db_entry.exists() and rescan == '0': context = get_context_from_db_entry(db_entry) else: app_dic['app_file'] = app_dic[ 'md5'] + '.zip' # NEW FILENAME app_dic['app_path'] = app_dic['app_dir'] + \ app_dic['app_file'] # APP PATH logger.info("Extracting ZIP") app_dic['files'] = unzip( app_dic['app_path'], app_dic['app_dir']) # Check if Valid Directory Structure and get ZIP Type pro_type, valid = valid_android_zip(app_dic['app_dir']) if valid and pro_type == 'ios': logger.info("Redirecting to iOS Source Code Analyzer") if api: return {"type": "ios"} else: return HttpResponseRedirect( '/StaticAnalyzer_iOS/?name=' + app_dic['app_name'] + '&type=ios&checksum=' + app_dic['md5'] ) app_dic['certz'] = get_hardcoded_cert_keystore(app_dic[ 'files']) app_dic['zipped'] = pro_type logger.info("ZIP Type - " + pro_type) if valid and (pro_type in ['eclipse', 'studio']): # ANALYSIS BEGINS 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']) # Manifest XML app_dic['persed_xml'] = get_manifest( "", app_dic['app_dir'], app_dic['tools_dir'], pro_type, False ) # Set manifest view link app_dic['mani'] = ( '../ManifestView/?md5=' + app_dic['md5'] + '&type=' + pro_type + '&bin=0' ) man_data_dic = manifest_data(app_dic['persed_xml']) app_dic['playstore'] = get_app_details( man_data_dic['packagename']) man_an_dic = manifest_analysis( app_dic['persed_xml'], man_data_dic ) # Get icon eclipse_res_path = os.path.join( app_dic['app_dir'], 'res') studio_res_path = os.path.join( app_dic['app_dir'], 'app', 'src', 'main', 'res') if os.path.exists(eclipse_res_path): res_path = eclipse_res_path elif os.path.exists(studio_res_path): res_path = studio_res_path else: res_path = '' app_dic['icon_hidden'] = man_an_dic['icon_hidden'] app_dic['icon_found'] = False app_dic['icon_path'] = '' if res_path: app_dic['icon_path'] = find_icon_path_zip( res_path, man_data_dic['icons']) if app_dic['icon_path']: app_dic['icon_found'] = True if app_dic['icon_path']: if os.path.exists(app_dic['icon_path']): shutil.copy2(app_dic['icon_path'], os.path.join( settings.DWD_DIR, app_dic['md5'] + '-icon.png')) code_an_dic = code_analysis( app_dic['app_dir'], man_an_dic['permissons'], pro_type ) # Firebase DB Check code_an_dic['firebase'] = firebase_analysis( list(set(code_an_dic["urls_list"]))) # Domain Extraction and Malware Check logger.info( "Performing Malware Check on extracted Domains") code_an_dic["domains"] = malware_check( list(set(code_an_dic["urls_list"]))) logger.info("Connecting to Database") try: # SAVE TO DB if rescan == '1': logger.info("Updating Database...") update_db_entry( app_dic, man_data_dic, man_an_dic, code_an_dic, cert_dic, bin_an_buff, {}, ) update_scan_timestamp(app_dic['md5']) elif rescan == '0': logger.info("Saving to Database") create_db_entry( app_dic, man_data_dic, man_an_dic, code_an_dic, cert_dic, bin_an_buff, {}, ) except: PrintException("Saving to Database Failed") context = get_context_from_analysis( app_dic, man_data_dic, man_an_dic, code_an_dic, cert_dic, bin_an_buff, {}, ) else: msg = "This ZIP Format is not supported" if api: return print_n_send_error_response(request, msg, True) else: print_n_send_error_response(request, msg, False) return HttpResponseRedirect('/zip_format/') context["average_cvss"], context[ "security_score"] = score(context["findings"]) template = "static_analysis/android_source_analysis.html" if api: return context else: return render(request, template, context) else: logger.error( "Only APK,IPA and Zipped Android/iOS Source code supported now!") else: msg = "Hash match failed or Invalid file extension or file type" if api: return print_n_send_error_response(request, msg, True) else: return print_n_send_error_response(request, msg, False) except Exception as excep: msg = str(excep) exp = excep.__doc__ if api: return print_n_send_error_response(request, msg, True, exp) else: return print_n_send_error_response(request, msg, False, exp)
def static_analyzer_ios(request, api=False): """Module that performs iOS IPA/ZIP Static Analysis.""" try: logger.info('iOS Static Analysis Started') if api: file_type = request.POST['scan_type'] checksum = request.POST['hash'] rescan = str(request.POST.get('re_scan', 0)) filename = request.POST['file_name'] else: file_type = request.GET['type'] checksum = request.GET['checksum'] rescan = str(request.GET.get('rescan', 0)) filename = request.GET['name'] md5_match = re.match('^[0-9a-f]{32}$', checksum) if ((md5_match) and (filename.lower().endswith('.ipa') or filename.lower().endswith('.zip')) and (file_type in ['ipa', 'ios'])): app_dict = {} app_dict['directory'] = settings.BASE_DIR # BASE DIR app_dict['file_name'] = filename # APP ORGINAL NAME app_dict['md5_hash'] = checksum # MD5 app_dict['app_dir'] = os.path.join( settings.UPLD_DIR, app_dict['md5_hash'] + '/') # APP DIRECTORY tools_dir = os.path.join( app_dict['directory'], 'StaticAnalyzer/tools/mac/') if file_type == 'ipa': # DB ipa_db = StaticAnalyzerIPA.objects.filter( MD5=app_dict['md5_hash']) if ipa_db.exists() and rescan == '0': context = get_context_from_db_entry_ipa(ipa_db) else: logger.info('iOS Binary (IPA) Analysis Started') app_dict['app_file'] = app_dict[ 'md5_hash'] + '.ipa' # NEW FILENAME app_dict['app_path'] = (app_dict['app_dir'] + app_dict['app_file']) app_dict['bin_dir'] = os.path.join( app_dict['app_dir'], 'Payload/') app_dict['size'] = str( file_size(app_dict['app_path'])) + 'MB' # FILE SIZE app_dict['sha1'], app_dict['sha256'] = hash_gen( app_dict['app_path']) # SHA1 & SHA256 HASHES logger.info('Extracting IPA') # EXTRACT IPA unzip(app_dict['app_path'], app_dict['app_dir']) # Get Files, normalize + to x, # and convert binary plist -> xml files, sfiles = ios_list_files( app_dict['bin_dir'], app_dict['md5_hash'], True, 'ipa') infoplist_dict = plist_analysis(app_dict['bin_dir'], False) app_dict['appstore'] = app_search(infoplist_dict.get('id')) bin_analysis_dict = binary_analysis( app_dict['bin_dir'], tools_dir, app_dict['app_dir'], infoplist_dict.get('bin')) # Saving to DB logger.info('Connecting to DB') if rescan == '1': logger.info('Updating Database...') update_db_entry_ipa( app_dict, infoplist_dict, bin_analysis_dict, files, sfiles) update_scan_timestamp(app_dict['md5_hash']) elif rescan == '0': logger.info('Saving to Database') create_db_entry_ipa( app_dict, infoplist_dict, bin_analysis_dict, files, sfiles) context = get_context_from_analysis_ipa( app_dict, infoplist_dict, bin_analysis_dict, files, sfiles) context['VT_RESULT'] = None if settings.VT_ENABLED: vt = VirusTotal.VirusTotal() context['VT_RESULT'] = vt.get_result( os.path.join(app_dict['app_dir'], app_dict[ 'md5_hash']) + '.ipa', app_dict['md5_hash']) context['average_cvss'], context[ 'security_score'] = score(context['bin_anal']) template = 'static_analysis/ios_binary_analysis.html' if api: return context else: return render(request, template, context) elif file_type == 'ios': ios_zip_db = StaticAnalyzerIOSZIP.objects.filter( MD5=app_dict['md5_hash']) if ios_zip_db.exists() and rescan == '0': context = get_context_from_db_entry_ios(ios_zip_db) else: logger.info('iOS Source Code Analysis Started') app_dict['app_file'] = app_dict[ 'md5_hash'] + '.zip' # NEW FILENAME app_dict['app_path'] = (app_dict['app_dir'] + app_dict['app_file']) # ANALYSIS BEGINS - Already Unzipped logger.info('ZIP Already Extracted') app_dict['size'] = str( file_size(app_dict['app_path'])) + 'MB' # FILE SIZE app_dict['sha1'], app_dict['sha256'] = hash_gen( app_dict['app_path']) # SHA1 & SHA256 HASHES files, sfiles = ios_list_files( 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')) code_analysis_dic = ios_source_analysis( app_dict['app_dir']) # Firebase DB Check code_analysis_dic['firebase'] = firebase_analysis( list(set(code_analysis_dic['urls_list']))) # Saving to DB logger.info('Connecting to DB') if rescan == '1': logger.info('Updating Database...') update_db_entry_ios( app_dict, infoplist_dict, code_analysis_dic, files, sfiles) update_scan_timestamp(app_dict['md5_hash']) elif rescan == '0': logger.info('Saving to Database') create_db_entry_ios( app_dict, infoplist_dict, code_analysis_dic, files, sfiles) context = get_context_from_analysis_ios( app_dict, infoplist_dict, code_analysis_dic, files, sfiles) context['average_cvss'], context[ 'security_score'] = score(context['insecure']) template = 'static_analysis/ios_source_analysis.html' if api: return context else: return render(request, template, context) else: msg = 'File Type not supported!' if api: return print_n_send_error_response(request, msg, True) else: return print_n_send_error_response(request, msg, False) else: msg = 'Hash match failed or Invalid file extension or file type' if api: return print_n_send_error_response(request, msg, True) else: return print_n_send_error_response(request, msg, False) except Exception as exp: logger.exception('Error Perfroming Static Analysis') msg = str(exp) exp_doc = exp.__doc__ if api: return print_n_send_error_response(request, msg, True, exp_doc) else: return print_n_send_error_response(request, msg, False, exp_doc)
def pdf(request, api=False): try: if api: checksum = request.POST['hash'] scan_type = request.POST['scan_type'] else: checksum = request.GET['md5'] scan_type = request.GET['type'] hash_match = re.match('^[0-9a-f]{32}$', checksum) if hash_match: if scan_type.lower() in ['apk', 'andzip']: static_db = StaticAnalyzerAndroid.objects.filter(MD5=checksum) if static_db.exists(): print "\n[INFO] Fetching data from DB for PDF Report Generation (Android)" context = get_context_from_db_entry(static_db) if scan_type.lower() == 'apk': template = get_template("pdf/static_analysis_pdf.html") else: template = get_template( "pdf/static_analysis_zip_pdf.html") else: if api: return {"report": "Report not Found"} else: return HttpResponse(json.dumps({"report": "Report not Found"}), content_type="application/json; charset=utf-8", status_code=500) elif re.findall('ipa|ioszip', scan_type.lower()): if scan_type.lower() == 'ipa': static_db = StaticAnalyzerIPA.objects.filter(MD5=checksum) if static_db.exists(): print "\n[INFO] Fetching data from DB for PDF Report Generation (IOS IPA)" context = get_context_from_db_entry_ipa(static_db) template = get_template( "pdf/ios_binary_analysis_pdf.html") else: if api: return {"report": "Report not Found"} else: return HttpResponse(json.dumps({"report": "Report not Found"}), content_type="application/json; charset=utf-8", status_code=500) elif scan_type.lower() == 'ioszip': static_db = StaticAnalyzerIOSZIP.objects.filter(MD5=checksum) if static_db.exists(): print "\n[INFO] Fetching data from DB for PDF Report Generation (IOS ZIP)" context = get_context_from_db_entry_ios(static_db) template = get_template( "pdf/ios_source_analysis_pdf.html") else: if api: return {"report": "Report not Found"} else: return HttpResponse(json.dumps({"report": "Report not Found"}), content_type="application/json; charset=utf-8", status_code=500) elif re.findall('appx', scan_type.lower()): if scan_type.lower() == 'appx': db_entry = StaticAnalyzerWindows.objects.filter(# pylint: disable-msg=E1101 MD5=checksum ) if db_entry.exists(): print "\n[INFO] Fetching data from DB for PDF Report Generation (APPX)" context = { 'title': db_entry[0].TITLE, 'name': db_entry[0].APP_NAME, 'pub_name': db_entry[0].PUB_NAME, 'size': db_entry[0].SIZE, 'md5': db_entry[0].MD5, 'sha1': db_entry[0].SHA1, 'sha256': db_entry[0].SHA256, 'bin_name': db_entry[0].BINNAME, 'version': db_entry[0].VERSION, 'arch': db_entry[0].ARCH, 'compiler_version': db_entry[0].COMPILER_VERSION, 'visual_studio_version': db_entry[0].VISUAL_STUDIO_VERSION, 'visual_studio_edition': db_entry[0].VISUAL_STUDIO_EDITION, 'target_os': db_entry[0].TARGET_OS, 'appx_dll_version': db_entry[0].APPX_DLL_VERSION, 'proj_guid': db_entry[0].PROJ_GUID, 'opti_tool': db_entry[0].OPTI_TOOL, 'target_run': db_entry[0].TARGET_RUN, 'files': python_list(db_entry[0].FILES), 'strings': python_list(db_entry[0].STRINGS), 'bin_an_results': python_list(db_entry[0].BIN_AN_RESULTS), 'bin_an_warnings': python_list(db_entry[0].BIN_AN_WARNINGS) } template = get_template( "pdf/windows_binary_analysis_pdf.html") else: if api: return {"scan_type": "Type is not Allowed"} else: return HttpResponse(json.dumps({"type": "Type is not Allowed"}), content_type="application/json; charset=utf-8", status_code=500) html = template.render(context) try: options = { 'page-size': 'A4', 'quiet': '', 'no-collate': '', 'margin-top': '0.50in', 'margin-right': '0.50in', 'margin-bottom': '0.50in', 'margin-left': '0.50in', 'encoding': "UTF-8", 'custom-header': [ ('Accept-Encoding', 'gzip') ], 'no-outline': None } pdf_dat = pdfkit.from_string(html, False, options=options) if api: return {"pdf_dat": pdf_dat} else: return HttpResponse(pdf_dat, content_type='application/pdf') except Exception as exp: if api: return {"error": "Cannot Generate PDF", "err_details": str(exp)} else: return HttpResponse(json.dumps({"pdf_error": "Cannot Generate PDF", "err_details": str(exp)}), content_type="application/json; charset=utf-8", status_code=500) else: if api: return {"error": "Invalid scan hash"} else: return HttpResponse(json.dumps({"md5": "Invalid MD5"}), content_type="application/json; charset=utf-8", status_code=500) except Exception as exp: msg = str(exp) exp = exp.__doc__ if api: return print_n_send_error_response(request, msg, True, exp) else: return print_n_send_error_response(request, msg, False, exp)
def report(request): """Dynamic Analysis Report Generation.""" logger.info('Dynamic Analysis Report Generation') try: if request.method == 'GET': md5_hash = request.GET['md5'] package = request.GET['pkg'] if re.findall(r';|\$\(|\|\||&&', package): return print_n_send_error_response(request, 'Possible RCE Attack') if re.match('^[0-9a-f]{32}$', md5_hash): app_dir = os.path.join(settings.UPLD_DIR, md5_hash + '/') # APP DIRECTORY download_dir = settings.DWD_DIR droidmon_api_loc = os.path.join(app_dir, 'x_logcat.txt') api_analysis_result = api_analysis(package, droidmon_api_loc) analysis_result = run_analysis(app_dir, md5_hash, package) download(md5_hash, download_dir, app_dir, package) # Only After Download Process is Done imgs = [] act_imgs = [] act = {} expact_imgs = [] exp_act = {} if os.path.exists( os.path.join(download_dir, md5_hash + '-screenshots-apk/')): try: imp_path = os.path.join(download_dir, md5_hash + '-screenshots-apk/') for img in os.listdir(imp_path): if img.endswith('.png'): if img.startswith('act'): act_imgs.append(img) elif img.startswith('expact'): expact_imgs.append(img) else: imgs.append(img) sadb = StaticAnalyzerAndroid.objects.filter( MD5=md5_hash) if sadb.exists(): logger.info('\nFetching Exported Activity' ' & Activity List from DB') exported_act = python_list(sadb[0].EXPORTED_ACT) act_desc = python_list(sadb[0].ACTIVITIES) if act_imgs: if len(act_imgs) == len(act_desc): act = dict(list(zip(act_imgs, act_desc))) if expact_imgs: if len(expact_imgs) == len(exported_act): exp_act = dict( list(zip(expact_imgs, exported_act))) else: logger.warning('Entry does not exists in the DB.') except Exception: logger.exception('Screenshot Sorting') context = { 'md5': md5_hash, 'emails': analysis_result['emails'], 'urls': analysis_result['urls'], 'domains': analysis_result['domains'], 'clipboard': analysis_result['clipboard'], 'http': analysis_result['web_data'], 'xml': analysis_result['xmlfiles'], 'sqlite': analysis_result['sqlite_db'], 'others': analysis_result['other_files'], 'imgs': imgs, 'acttest': act, 'expacttest': exp_act, 'net': api_analysis_result['api_net'], 'base64': api_analysis_result['api_base64'], 'crypto': api_analysis_result['api_crypto'], 'fileio': api_analysis_result['api_fileio'], 'binder': api_analysis_result['api_binder'], 'divinfo': api_analysis_result['api_deviceinfo'], 'cntval': api_analysis_result['api_cntvl'], 'sms': api_analysis_result['api_sms'], 'sysprop': api_analysis_result['api_sysprop'], 'dexload': api_analysis_result['api_dexloader'], 'reflect': api_analysis_result['api_reflect'], 'sysman': api_analysis_result['api_acntmnger'], 'process': api_analysis_result['api_cmd'], 'pkg': package, 'title': 'Dynamic Analysis' } template = 'dynamic_analysis/dynamic_analysis.html' return render(request, template, context) else: return print_n_send_error_response(request, 'Invalid Scan Hash') else: return print_n_send_error_response(request, 'Only GET allowed') except Exception: logger.exception('Dynamic Analysis Report Generation') err = 'Error Geneating Dynamic Analysis Report' return print_n_send_error_response(request, err)
def static_analyzer_ios(request, api=False): """Module that performs iOS IPA/ZIP Static Analysis""" try: logger.info("iOS Static Analysis Started") if api: file_type = request.POST['scan_type'] checksum = request.POST['hash'] rescan = str(request.POST.get('re_scan', 0)) filename = request.POST['file_name'] else: file_type = request.GET['type'] checksum = request.GET['checksum'] rescan = str(request.GET.get('rescan', 0)) filename = request.GET['name'] md5_match = re.match('^[0-9a-f]{32}$', checksum) if ((md5_match) and (filename.lower().endswith('.ipa') or filename.lower().endswith('.zip') ) and (file_type in ['ipa', 'ios']) ): app_dict = {} app_dict["directory"] = settings.BASE_DIR # BASE DIR app_dict["file_name"] = filename # APP ORGINAL NAME app_dict["md5_hash"] = checksum # MD5 app_dict["app_dir"] = os.path.join( settings.UPLD_DIR, app_dict["md5_hash"] + '/') # APP DIRECTORY tools_dir = os.path.join( app_dict["directory"], 'StaticAnalyzer/tools/mac/') # TOOLS DIR if file_type == 'ipa': # DB ipa_db = StaticAnalyzerIPA.objects.filter( MD5=app_dict["md5_hash"]) if ipa_db.exists() and rescan == '0': context = get_context_from_db_entry_ipa(ipa_db) else: logger.info("iOS Binary (IPA) Analysis Started") app_dict["app_file"] = app_dict[ "md5_hash"] + '.ipa' # NEW FILENAME app_dict["app_path"] = app_dict["app_dir"] + \ app_dict["app_file"] # APP PATH app_dict["bin_dir"] = os.path.join( app_dict["app_dir"], "Payload/") app_dict["size"] = str( file_size(app_dict["app_path"])) + 'MB' # FILE SIZE app_dict["sha1"], app_dict["sha256"] = hash_gen( app_dict["app_path"]) # SHA1 & SHA256 HASHES logger.info("Extracting IPA") # EXTRACT IPA unzip(app_dict["app_path"], app_dict["app_dir"]) # Get Files, normalize + to x, # and convert binary plist -> xml files, sfiles = ios_list_files( app_dict["bin_dir"], app_dict["md5_hash"], True, 'ipa') infoplist_dict = plist_analysis(app_dict["bin_dir"], False) app_dict["appstore"] = app_search(infoplist_dict.get("id")) bin_analysis_dict = binary_analysis( app_dict["bin_dir"], tools_dir, app_dict["app_dir"], infoplist_dict.get("bin")) # Saving to DB logger.info("Connecting to DB") if rescan == '1': logger.info("Updating Database...") update_db_entry_ipa( app_dict, infoplist_dict, bin_analysis_dict, files, sfiles) update_scan_timestamp(app_dict["md5_hash"]) elif rescan == '0': logger.info("Saving to Database") create_db_entry_ipa( app_dict, infoplist_dict, bin_analysis_dict, files, sfiles) context = get_context_from_analysis_ipa( app_dict, infoplist_dict, bin_analysis_dict, files, sfiles) context['VT_RESULT'] = None if settings.VT_ENABLED: vt = VirusTotal.VirusTotal() context['VT_RESULT'] = vt.get_result( os.path.join(app_dict['app_dir'], app_dict[ 'md5_hash']) + '.ipa', app_dict['md5_hash'] ) context["average_cvss"], context[ "security_score"] = score(context["bin_anal"]) template = "static_analysis/ios_binary_analysis.html" if api: return context else: return render(request, template, context) elif file_type == 'ios': ios_zip_db = StaticAnalyzerIOSZIP.objects.filter( MD5=app_dict["md5_hash"]) if ios_zip_db.exists() and rescan == '0': context = get_context_from_db_entry_ios(ios_zip_db) else: logger.info("iOS Source Code Analysis Started") app_dict["app_file"] = app_dict[ "md5_hash"] + '.zip' # NEW FILENAME app_dict["app_path"] = app_dict["app_dir"] + \ app_dict["app_file"] # APP PATH # ANALYSIS BEGINS - Already Unzipped logger.info("ZIP Already Extracted") app_dict["size"] = str( file_size(app_dict["app_path"])) + 'MB' # FILE SIZE app_dict["sha1"], app_dict["sha256"] = hash_gen( app_dict["app_path"]) # SHA1 & SHA256 HASHES files, sfiles = ios_list_files( 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")) code_analysis_dic = ios_source_analysis( app_dict["app_dir"]) # Firebase DB Check code_analysis_dic['firebase'] = firebase_analysis( list(set(code_analysis_dic["urls_list"]))) # Saving to DB logger.info("Connecting to DB") if rescan == '1': logger.info("Updating Database...") update_db_entry_ios( app_dict, infoplist_dict, code_analysis_dic, files, sfiles) update_scan_timestamp(app_dict["md5_hash"]) elif rescan == '0': logger.info("Saving to Database") create_db_entry_ios( app_dict, infoplist_dict, code_analysis_dic, files, sfiles) context = get_context_from_analysis_ios( app_dict, infoplist_dict, code_analysis_dic, files, sfiles) context["average_cvss"], context[ "security_score"] = score(context["insecure"]) template = "static_analysis/ios_source_analysis.html" if api: return context else: return render(request, template, context) else: msg = "File Type not supported!" if api: return print_n_send_error_response(request, msg, True) else: return print_n_send_error_response(request, msg, False) else: msg = "Hash match failed or Invalid file extension or file type" if api: return print_n_send_error_response(request, msg, True) else: return print_n_send_error_response(request, msg, False) except Exception as exp: msg = str(exp) exp_doc = exp.__doc__ if api: return print_n_send_error_response(request, msg, True, exp_doc) else: return print_n_send_error_response(request, msg, False, exp_doc)
def compare_apps(request, first_hash: str, second_hash: str): if first_hash == second_hash: error_msg = "Results with same hash cannot be compared" return print_n_send_error_response(request, error_msg, False) print("[INFO] Starting App compare for - {} and {}".format(first_hash, second_hash)) return generic_compare(request, first_hash, second_hash)
def activity_tester(request): """Activity Tester.""" logger.info('Activity Tester') try: md5_hash = request.POST['md5'] package = request.POST['pkg'] if re.match('^[0-9a-f]{32}$', md5_hash): if re.findall(r';|\$\(|\|\||&&', package): return print_n_send_error_response(request, 'Possible RCE Attack', True) if request.method == 'POST': 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) data = {} static_android_db = StaticAnalyzerAndroid.objects.filter( MD5=md5_hash) if static_android_db.exists(): logger.info('Fetching Activity List from DB') activities = python_list(static_android_db[0].ACTIVITIES) if activities: act_no = 0 logger.info('Starting Activity Tester...') logger.info('%s Activities Identified', str(len(activities))) for line in activities: try: act_no += 1 logger.info('Launching Activity - %s. %s', str(act_no), line) adb_command([ 'am', 'start', '-n', package + '/' + line ], True) wait(4) adb_command([ 'screencap', '-p', '/data/local/screen.png' ], True) # ? get appended from Air :-() # if activity names are used outfile = ('{}act-{}.png'.format( screen_dir, act_no)) adb_command([ 'pull', '/data/local/screen.png', outfile ]) logger.info('Activity Screenshot Taken') adb_command(['am', 'force-stop', package], True) logger.info('Stopping App') except Exception: logger.exception('Activity Tester') data = {'acttest': 'done'} else: logger.info('Activity Tester - No Activity Found!') data = {'acttest': 'noact'} return HttpResponse(json.dumps(data), content_type='application/json') else: err = 'Entry does not exist in DB' return print_n_send_error_response(request, err, True) else: return print_n_send_error_response(request, 'Only POST allowed', True) else: return print_n_send_error_response(request, 'Invalid Scan Hash', True) except Exception: logger.exception('Activity Tester') return print_n_send_error_response(request, 'Error Running Activity Tester', True)
def pdf(request, api=False): try: if api: checksum = request.POST['hash'] scan_type = request.POST['scan_type'] else: checksum = request.GET['md5'] scan_type = request.GET['type'] hash_match = re.match('^[0-9a-f]{32}$', checksum) if hash_match: if scan_type.lower() in ['apk', 'andzip']: static_db = StaticAnalyzerAndroid.objects.filter(MD5=checksum) if static_db.exists(): print( "\n[INFO] Fetching data from DB for PDF Report Generation (Android)" ) context = get_context_from_db_entry(static_db) if scan_type.lower() == 'apk': template = get_template("pdf/static_analysis_pdf.html") else: template = get_template( "pdf/static_analysis_zip_pdf.html") else: if api: return {"report": "Report not Found"} else: return HttpResponse( json.dumps({"report": "Report not Found"}), content_type="application/json; charset=utf-8", status=500) elif re.findall('ipa|ioszip', scan_type.lower()): if scan_type.lower() == 'ipa': static_db = StaticAnalyzerIPA.objects.filter(MD5=checksum) if static_db.exists(): print( "\n[INFO] Fetching data from DB for PDF Report Generation (IOS IPA)" ) context = get_context_from_db_entry_ipa(static_db) template = get_template( "pdf/ios_binary_analysis_pdf.html") else: if api: return {"report": "Report not Found"} else: return HttpResponse( json.dumps({"report": "Report not Found"}), content_type="application/json; charset=utf-8", status=500) elif scan_type.lower() == 'ioszip': static_db = StaticAnalyzerIOSZIP.objects.filter( MD5=checksum) if static_db.exists(): print( "\n[INFO] Fetching data from DB for PDF Report Generation (IOS ZIP)" ) context = get_context_from_db_entry_ios(static_db) template = get_template( "pdf/ios_source_analysis_pdf.html") else: if api: return {"report": "Report not Found"} else: return HttpResponse( json.dumps({"report": "Report not Found"}), content_type="application/json; charset=utf-8", status=500) elif re.findall('appx', scan_type.lower()): if scan_type.lower() == 'appx': db_entry = StaticAnalyzerWindows.objects.filter( # pylint: disable-msg=E1101 MD5=checksum) if db_entry.exists(): print( "\n[INFO] Fetching data from DB for PDF Report Generation (APPX)" ) context = { 'title': db_entry[0].TITLE, 'name': db_entry[0].APP_NAME, 'pub_name': db_entry[0].PUB_NAME, 'size': db_entry[0].SIZE, 'md5': db_entry[0].MD5, 'sha1': db_entry[0].SHA1, 'sha256': db_entry[0].SHA256, 'bin_name': db_entry[0].BINNAME, 'version': db_entry[0].VERSION, 'arch': db_entry[0].ARCH, 'compiler_version': db_entry[0].COMPILER_VERSION, 'visual_studio_version': db_entry[0].VISUAL_STUDIO_VERSION, 'visual_studio_edition': db_entry[0].VISUAL_STUDIO_EDITION, 'target_os': db_entry[0].TARGET_OS, 'appx_dll_version': db_entry[0].APPX_DLL_VERSION, 'proj_guid': db_entry[0].PROJ_GUID, 'opti_tool': db_entry[0].OPTI_TOOL, 'target_run': db_entry[0].TARGET_RUN, 'files': python_list(db_entry[0].FILES), 'strings': python_list(db_entry[0].STRINGS), 'bin_an_results': python_list(db_entry[0].BIN_AN_RESULTS), 'bin_an_warnings': python_list(db_entry[0].BIN_AN_WARNINGS) } template = get_template( "pdf/windows_binary_analysis_pdf.html") else: if api: return {"scan_type": "Type is not Allowed"} else: return HttpResponse( json.dumps({"type": "Type is not Allowed"}), content_type="application/json; charset=utf-8", status=500) context['VT_RESULT'] = None if settings.VT_ENABLED: app_dir = os.path.join(settings.UPLD_DIR, checksum + '/') vt = VirusTotal.VirusTotal() if "zip" in scan_type.lower(): context['VT_RESULT'] = None else: context['VT_RESULT'] = vt.get_result( os.path.join(app_dir, checksum) + '.' + scan_type.lower(), checksum) html = template.render(context) try: options = { 'page-size': 'A4', 'quiet': '', 'no-collate': '', 'margin-top': '0.50in', 'margin-right': '0.50in', 'margin-bottom': '0.50in', 'margin-left': '0.50in', 'encoding': "UTF-8", 'custom-header': [('Accept-Encoding', 'gzip')], 'no-outline': None } pdf_dat = pdfkit.from_string(html, False, options=options) if api: return {"pdf_dat": pdf_dat, "report_dat": context} else: return HttpResponse(pdf_dat, content_type='application/pdf') except Exception as exp: if api: return { "error": "Cannot Generate PDF", "err_details": str(exp) } else: return HttpResponse( json.dumps({ "pdf_error": "Cannot Generate PDF", "err_details": str(exp) }), content_type="application/json; charset=utf-8", status=500) else: if api: return {"error": "Invalid scan hash"} else: return HttpResponse( json.dumps({"md5": "Invalid MD5"}), content_type="application/json; charset=utf-8", status=500) except Exception as exp: msg = str(exp) exp = exp.__doc__ if api: return print_n_send_error_response(request, msg, True, exp) else: return print_n_send_error_response(request, msg, False, exp)