def class_dump(tools_dir, bin_path, app_dir, bin_type): '''Running Classdumpz on binary''' try: webview = {} if platform.system() == 'Darwin': logger.info('Dumping classes') if bin_type == "Swift": logger.info("Running class-dump-swift aganst binary") if len(settings.CLASSDUMP_SWIFT_BINARY) > 0 and isFileExists( settings.CLASSDUMP_SWIFT_BINARY): class_dump_bin = settings.CLASSDUMP_SWIFT_BINARY else: class_dump_bin = os.path.join(tools_dir, 'class-dump-swift') else: logger.info("Running class-dump-z aganst binary") if len(settings.CLASSDUMPZ_BINARY) > 0 and isFileExists( settings.CLASSDUMPZ_BINARY): class_dump_bin = settings.CLASSDUMPZ_BINARY else: class_dump_bin = os.path.join(tools_dir, 'class-dump-z') subprocess.call(['chmod', '777', class_dump_bin]) args = [class_dump_bin, bin_path] elif platform.system() == 'Linux': logger.info('Running jtool against the binary for dumping classes') if len(settings.JTOOL_BINARY) > 0 and isFileExists( settings.JTOOL_BINARY): jtool_bin = settings.JTOOL_BINARY else: jtool_bin = os.path.join(tools_dir, 'jtool.ELF64') subprocess.call(['chmod', '777', jtool_bin]) args = [jtool_bin, '-arch', 'arm', '-d', 'objc', '-v', bin_path] else: # Platform not supported logger.warning('class-dump is not supported in this platform') return {} with open(os.devnull, 'w') as devnull: classdump = subprocess.check_output(args, stderr=devnull) if b"Source: (null)" in classdump and platform.system() == 'Darwin': logger.info('Running fail safe class-dump-swift') class_dump_bin = os.path.join(tools_dir, 'class-dump-swift') args = [class_dump_bin, bin_path] classdump = subprocess.check_output(args) dump_file = os.path.join(app_dir, 'classdump.txt') with open(dump_file, 'w') as flip: flip.write(classdump.decode('utf-8', 'ignore')) if b'UIWebView' in classdump: webview = { 'issue': 'Binary uses WebView Component.', 'status': INFO, 'description': 'The binary may use WebView Component.', 'cvss': 0, 'cwe': '', } return webview except: logger.error('class-dump-z/class-dump-swift failed on this binary')
def class_dump(tools_dir, bin_path, app_dir, bin_type): '''Running Classdumpz on binary''' try: webview = {} if platform.system() == 'Darwin': logger.info('Dumping classes') if bin_type == "Swift": logger.info("Running class-dump-swift aganst binary") if len(settings.CLASSDUMP_SWIFT_BINARY) > 0 and isFileExists(settings.CLASSDUMP_SWIFT_BINARY): class_dump_bin = settings.CLASSDUMP_SWIFT_BINARY else: class_dump_bin = os.path.join( tools_dir, 'class-dump-swift') else: logger.info("Running class-dump-z aganst binary") if len(settings.CLASSDUMPZ_BINARY) > 0 and isFileExists(settings.CLASSDUMPZ_BINARY): class_dump_bin = settings.CLASSDUMPZ_BINARY else: class_dump_bin = os.path.join(tools_dir, 'class-dump-z') subprocess.call(['chmod', '777', class_dump_bin]) args = [class_dump_bin, bin_path] elif platform.system() == 'Linux': logger.info('Running jtool against the binary for dumping classes') if len(settings.JTOOL_BINARY) > 0 and isFileExists(settings.JTOOL_BINARY): jtool_bin = settings.JTOOL_BINARY else: jtool_bin = os.path.join(tools_dir, 'jtool.ELF64') subprocess.call(['chmod', '777', jtool_bin]) args = [jtool_bin, '-arch', 'arm', '-d', 'objc', '-v', bin_path] else: # Platform not supported logger.warning('class-dump is not supported in this platform') return {} with open(os.devnull, 'w') as devnull: classdump = subprocess.check_output(args, stderr=devnull) if b"Source: (null)" in classdump and platform.system() == 'Darwin': logger.info('Running fail safe class-dump-swift') class_dump_bin = os.path.join( tools_dir, 'class-dump-swift') args = [class_dump_bin, bin_path] classdump = subprocess.check_output(args) dump_file = os.path.join(app_dir, 'classdump.txt') with open(dump_file, 'w') as flip: flip.write(classdump.decode('utf-8', 'ignore')) if b'UIWebView' in classdump: webview = {'issue': 'Binary uses WebView Component.', 'status': INFO, 'description': 'The binary may use WebView Component.', 'cvss': 0, 'cwe': '', } return webview except: logger.error('class-dump-z/class-dump-swift failed on this binary')
def read_manifest(app_dir, tools_dir, typ, binary): """Read the manifest file.""" try: dat = '' if binary is True: print "[INFO] Getting Manifest from Binary" print "[INFO] AXML -> XML" manifest = os.path.join(app_dir, "AndroidManifest.xml") if len(settings.AXMLPRINTER_BINARY) > 0 and isFileExists( settings.AXMLPRINTER_BINARY): cp_path = settings.AXMLPRINTER_BINARY else: cp_path = os.path.join(tools_dir, 'AXMLPrinter2.jar') args = [settings.JAVA_PATH + 'java', '-jar', cp_path, manifest] dat = subprocess.check_output(args) else: print "[INFO] Getting Manifest from Source" if typ == "eclipse": manifest = os.path.join(app_dir, "AndroidManifest.xml") elif typ == "studio": manifest = os.path.join(app_dir, "app/src/main/AndroidManifest.xml") with io.open(manifest, mode='r', encoding="utf8", errors="ignore") as file_pointer: dat = file_pointer.read() return dat except: PrintException("[ERROR] Reading Manifest file")
def binary_analysis(src, tools_dir, app_dir): """Binary Analysis of IPA""" try: binary_analysis_dict = {} print "[INFO] Starting Binary Analysis" dirs = os.listdir(src) dot_app_dir = "" for dir_ in dirs: if dir_.endswith(".app"): dot_app_dir = dir_ break # Bin Dir - Dir/Payload/x.app/ bin_dir = os.path.join(src, dot_app_dir) bin_name = dot_app_dir.replace(".app", "") # Bin Path - Dir/Payload/x.app/x bin_path = os.path.join(bin_dir, bin_name) binary_analysis_dict["libs"] = '' binary_analysis_dict["bin_res"] = '' binary_analysis_dict["strings"] = '' if not isFileExists(bin_path): print "[WARNING] MobSF Cannot find binary in " + bin_path print "[WARNING] Skipping Otool, Classdump and Strings" else: otool_dict = otool_analysis(bin_name, bin_path, bin_dir) cls_dump = class_dump_z(tools_dir, bin_path, app_dir) strings_in_ipa = strings_on_ipa(bin_path) binary_analysis_dict["libs"] = otool_dict["libs"] binary_analysis_dict["bin_res"] = otool_dict["anal"] + cls_dump binary_analysis_dict["strings"] = strings_in_ipa return binary_analysis_dict except: PrintException("[ERROR] iOS Binary Analysis")
def class_dump_z(tools_dir, bin_path, app_dir): """Running Classdumpz on binary""" try: webview = '' print "[INFO] Running class-dump-z against the Binary" if len(settings.CLASSDUMPZ_BINARY) > 0 and isFileExists(settings.CLASSDUMPZ_BINARY): class_dump_z_bin = settings.CLASSDUMPZ_BINARY else: class_dump_z_bin = os.path.join(tools_dir, 'class-dump-z') subprocess.call(["chmod", "777", class_dump_z_bin]) dump_dir = os.path.join(app_dir, 'classdump/') readme_path = os.path.join(app_dir, 'classdump/' + "readme.txt") print "[INFO] Dumping classes to " + str(dump_dir) subprocess.call([class_dump_z_bin, "-H","-s", bin_path,"-o",dump_dir]) cmd='echo "Please select a head file of function to analysis ...">{}'.format(readme_path) subprocess.check_call(cmd, shell=True) class_dump = subprocess.check_output([class_dump_z_bin, bin_path]) dump_file = os.path.join(app_dir, "classdump.txt") with open(dump_file, "w") as flip: flip.write(class_dump) if "UIWebView" in class_dump: webview = "<tr><td>Binary uses WebView Component.</td><td>" +\ "<span class='label label-info'>Info</span></td><td>The binary" +\ " may use WebView Component.</td></tr>" return webview except: print "[INFO] class-dump-z does not work on iOS apps developed in Swift" PrintException("[ERROR] - Cannot perform class dump")
def delete_scan(request): """ Delete Scan from DB and remove the scan related files """ try: if request.method == 'POST': md5_hash = request.POST['md5'] data = {'deleted': 'no'} if re.match('[0-9a-f]{32}', md5_hash): # Delete DB Entries 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'} return HttpResponse(json.dumps(data), content_type='application/json') except: PrintException("Error Deleting Scan") return HttpResponseRedirect('/error/')
def dex_2_jar(app_path, app_dir, tools_dir): """Run dex2jar.""" try: print "[INFO] DEX -> JAR" working_dir = None args = [] if settings.JAR_CONVERTER == "d2j": print "[INFO] Using JAR converter - dex2jar" if len(settings.DEX2JAR_BINARY) > 0 and isFileExists(settings.DEX2JAR_BINARY): d2j = settings.DEX2JAR_BINARY else: if platform.system() == "Windows": win_fix_java(tools_dir) d2j = os.path.join(tools_dir, 'd2j2/d2j-dex2jar.bat') else: inv = os.path.join(tools_dir, 'd2j2/d2j_invoke.sh') d2j = os.path.join(tools_dir, 'd2j2/d2j-dex2jar.sh') subprocess.call(["chmod", "777", d2j]) subprocess.call(["chmod", "777", inv]) args = [ d2j, app_dir + 'classes.dex', '-f', '-o', app_dir + 'classes.jar' ] elif settings.JAR_CONVERTER == "enjarify": print "[INFO] Using JAR converter - Google enjarify" if len(settings.ENJARIFY_DIRECTORY) > 0 and isDirExists(settings.ENJARIFY_DIRECTORY): working_dir = settings.ENJARIFY_DIRECTORY else: working_dir = os.path.join(tools_dir, 'enjarify/') if platform.system() == "Windows": win_fix_python3(tools_dir) enjarify = os.path.join(working_dir, 'enjarify.bat') args = [enjarify, app_path, "-f", "-o", app_dir + 'classes.jar'] else: if len(settings.PYTHON3_PATH) > 2: python3 = os.path.join(settings.PYTHON3_PATH, "python3") else: python3 = "python3" args = [ python3, "-O", "-m", "enjarify.main", app_path, "-f", "-o", app_dir + 'classes.jar' ] if working_dir: subprocess.call(args, cwd=working_dir) else: subprocess.call(args) except: PrintException("[ERROR] Converting Dex to JAR")
def dex_2_jar(app_path, app_dir, tools_dir): """Run dex2jar.""" try: logger.info("DEX -> JAR") working_dir = None args = [] if settings.JAR_CONVERTER == "d2j": logger.info("Using JAR converter - dex2jar") dexes = get_dex_files(app_dir) for idx, dex in enumerate(dexes): logger.info("Converting " + filename_from_path(dex) + " to JAR") if len(settings.DEX2JAR_BINARY) > 0 and isFileExists( settings.DEX2JAR_BINARY): d2j = settings.DEX2JAR_BINARY else: if platform.system() == "Windows": win_fix_java(tools_dir) d2j = os.path.join(tools_dir, 'd2j2/d2j-dex2jar.bat') else: inv = os.path.join(tools_dir, 'd2j2/d2j_invoke.sh') d2j = os.path.join(tools_dir, 'd2j2/d2j-dex2jar.sh') subprocess.call(["chmod", "777", d2j]) subprocess.call(["chmod", "777", inv]) args = [ d2j, dex, '-f', '-o', app_dir + 'classes' + str(idx) + '.jar' ] subprocess.call(args) elif settings.JAR_CONVERTER == "enjarify": logger.info("Using JAR converter - Google enjarify") if len(settings.ENJARIFY_DIRECTORY) > 0 and isDirExists( settings.ENJARIFY_DIRECTORY): working_dir = settings.ENJARIFY_DIRECTORY else: working_dir = os.path.join(tools_dir, 'enjarify/') if platform.system() == "Windows": win_fix_python3(tools_dir) enjarify = os.path.join(working_dir, 'enjarify.bat') args = [ enjarify, app_path, "-f", "-o", app_dir + 'classes.jar' ] else: if len(settings.PYTHON3_PATH) > 2: python3 = os.path.join(settings.PYTHON3_PATH, "python3") else: python3 = get_python() args = [ python3, "-O", "-m", "enjarify.main", app_path, "-f", "-o", app_dir + 'classes.jar' ] subprocess.call(args, cwd=working_dir) except: PrintException("Converting Dex to JAR")
def get_otool_out(tools_dir, cmd_type, bin_path, bin_dir): '''Get otool args by OS and type''' if len(settings.OTOOL_BINARY) > 0 and isFileExists(settings.OTOOL_BINARY): otool_bin = settings.OTOOL_BINARY else: otool_bin = 'otool' if len(settings.JTOOL_BINARY) > 0 and isFileExists(settings.JTOOL_BINARY): jtool_bin = settings.JTOOL_BINARY else: jtool_bin = os.path.join(tools_dir, 'jtool.ELF64') plat = platform.system() if cmd_type == 'libs': if plat == 'Darwin': args = [otool_bin, '-L', bin_path] elif plat == 'Linux': args = [jtool_bin, '-arch', 'arm', '-L', '-v', bin_path] else: # Platform Not Supported return None libs = subprocess.check_output(args).decode('utf-8', 'ignore') libs = smart_text(escape(libs.replace(bin_dir + '/', ''))) return libs.split('\n') elif cmd_type == 'header': if plat == 'Darwin': args = [otool_bin, '-hv', bin_path] elif plat == 'Linux': args = [jtool_bin, '-arch', 'arm', '-h', '-v', bin_path] else: # Platform Not Supported return None return subprocess.check_output(args) elif cmd_type == 'symbols': if plat == 'Darwin': args = [otool_bin, '-Iv', bin_path] return subprocess.check_output(args) elif plat == 'Linux': arg1 = [jtool_bin, '-arch', 'arm', '-bind', '-v', bin_path] arg2 = [jtool_bin, '-arch', 'arm', '-lazy_bind', '-v', bin_path] return subprocess.check_output(arg1) + subprocess.check_output( arg2) else: # Platform Not Supported return None
def get_otool_out(tools_dir, cmd_type, bin_path, bin_dir): """Get otool args by OS and type""" if len(settings.OTOOL_BINARY) > 0 and isFileExists(settings.OTOOL_BINARY): otool_bin = settings.OTOOL_BINARY else: otool_bin = "otool" if len(settings.JTOOL_BINARY) > 0 and isFileExists(settings.JTOOL_BINARY): jtool_bin = settings.JTOOL_BINARY else: jtool_bin = os.path.join(tools_dir, 'jtool.ELF64') plat = platform.system() if cmd_type == "libs": if plat == "Darwin": args = [otool_bin, '-L', bin_path] elif plat == "Linux": args = [jtool_bin, '-arch', 'arm', '-L', '-v', bin_path] else: # Platform Not Supported return None libs = subprocess.check_output(args).decode("utf-8", "ignore") libs = smart_text(escape(libs.replace(bin_dir + "/", ""))) return libs.replace("\n", "</br>") elif cmd_type == "header": if plat == "Darwin": args = [otool_bin, '-hv', bin_path] elif plat == "Linux": args = [jtool_bin, '-arch', 'arm', '-h', '-v', bin_path] else: # Platform Not Supported return None return subprocess.check_output(args) elif cmd_type == "symbols": if plat == "Darwin": args = [otool_bin, '-Iv', bin_path] return subprocess.check_output(args) elif plat == "Linux": arg1 = [jtool_bin, '-arch', 'arm', '-bind', '-v', bin_path] arg2 = [jtool_bin, '-arch', 'arm', '-lazy_bind', '-v', bin_path] return subprocess.check_output(arg1) + subprocess.check_output( arg2) else: # Platform Not Supported return None
def class_dump_z(tools_dir, bin_path, app_dir): """Running Classdumpz on binary""" try: webview = {} if platform.system() == "Darwin": print( "[INFO] Running class-dump-z against the binary for dumping classes" ) if len(settings.CLASSDUMPZ_BINARY) > 0 and isFileExists( settings.CLASSDUMPZ_BINARY): class_dump_z_bin = settings.CLASSDUMPZ_BINARY else: class_dump_z_bin = os.path.join(tools_dir, 'class-dump-z') subprocess.call(["chmod", "777", class_dump_z_bin]) args = [class_dump_z_bin, bin_path] elif platform.system() == "Linux": print( "[INFO] Running jtool against the binary for dumping classes") if len(settings.JTOOL_BINARY) > 0 and isFileExists( settings.JTOOL_BINARY): jtool_bin = settings.JTOOL_BINARY else: jtool_bin = os.path.join(tools_dir, 'jtool.ELF64') subprocess.call(["chmod", "777", jtool_bin]) args = [jtool_bin, '-arch', 'arm', '-d', 'objc', '-v', bin_path] else: # Platform not supported return {} classdump = subprocess.check_output(args) dump_file = os.path.join(app_dir, "classdump.txt") with open(dump_file, "w") as flip: flip.write(classdump.decode("utf-8", "ignore")) if b"UIWebView" in classdump: webview = { "issue": "Binary uses WebView Component.", "status": INFO, "description": "The binary may use WebView Component." } return webview except: print( "[INFO] class-dump-z does not work on iOS apps developed in Swift") PrintException("[ERROR] - Cannot perform class dump")
def jar_2_java(app_dir, tools_dir): """Conver jar to java.""" try: logger.info("JAR -> JAVA") jar_files = get_jar_files(app_dir) output = os.path.join(app_dir, 'java_source/') for jar_path in jar_files: logger.info("Decompiling {} to Java Code".format( filename_from_path(jar_path))) if settings.DECOMPILER == 'jd-core': if (len(settings.JD_CORE_DECOMPILER_BINARY) > 0 and isFileExists(settings.JD_CORE_DECOMPILER_BINARY)): jd_path = settings.JD_CORE_DECOMPILER_BINARY else: jd_path = os.path.join(tools_dir, 'jd-core.jar') args = [ settings.JAVA_PATH + 'java', '-jar', jd_path, jar_path, output ] elif settings.DECOMPILER == 'cfr': if (len(settings.CFR_DECOMPILER_BINARY) > 0 and isFileExists(settings.CFR_DECOMPILER_BINARY)): jd_path = settings.CFR_DECOMPILER_BINARY else: jd_path = os.path.join(tools_dir, 'cfr-0.144.jar') args = [ settings.JAVA_PATH + 'java', '-jar', jd_path, jar_path, '--outputdir', output, '--silent', 'true' ] elif settings.DECOMPILER == "procyon": if (len(settings.PROCYON_DECOMPILER_BINARY) > 0 and isFileExists(settings.PROCYON_DECOMPILER_BINARY)): pd_path = settings.PROCYON_DECOMPILER_BINARY else: pd_path = os.path.join(tools_dir, 'procyon-decompiler-0.5.34.jar') args = [ settings.JAVA_PATH + 'java', '-jar', pd_path, jar_path, '-o', output ] subprocess.call(args) except: PrintException("Converting JAR to JAVA")
def get_otool_out(tools_dir, cmd_type, bin_path, bin_dir): '''Get otool args by OS and type''' if len(settings.OTOOL_BINARY) > 0 and isFileExists(settings.OTOOL_BINARY): otool_bin = settings.OTOOL_BINARY else: otool_bin = 'otool' if len(settings.JTOOL_BINARY) > 0 and isFileExists(settings.JTOOL_BINARY): jtool_bin = settings.JTOOL_BINARY else: jtool_bin = os.path.join(tools_dir, 'jtool.ELF64') plat = platform.system() if cmd_type == 'libs': if plat == 'Darwin': args = [otool_bin, '-L', bin_path] elif plat == 'Linux': args = [jtool_bin, '-arch', 'arm', '-L', '-v', bin_path] else: # Platform Not Supported return None libs = subprocess.check_output(args).decode('utf-8', 'ignore') libs = smart_text(escape(libs.replace(bin_dir + '/', ''))) return libs.split('\n') elif cmd_type == 'header': if plat == 'Darwin': args = [otool_bin, '-hv', bin_path] elif plat == 'Linux': args = [jtool_bin, '-arch', 'arm', '-h', '-v', bin_path] else: # Platform Not Supported return None return subprocess.check_output(args) elif cmd_type == 'symbols': if plat == 'Darwin': args = [otool_bin, '-Iv', bin_path] return subprocess.check_output(args) elif plat == 'Linux': arg1 = [jtool_bin, '-arch', 'arm', '-bind', '-v', bin_path] arg2 = [jtool_bin, '-arch', 'arm', '-lazy_bind', '-v', bin_path] return subprocess.check_output(arg1) + subprocess.check_output(arg2) else: # Platform Not Supported return None
def get_otool_out(tools_dir, cmd_type, bin_path, bin_dir): """Get otool args by OS and type""" if len(settings.OTOOL_BINARY) > 0 and isFileExists(settings.OTOOL_BINARY): otool_bin = settings.OTOOL_BINARY else: otool_bin = "otool" if len(settings.JTOOL_BINARY) > 0 and isFileExists(settings.JTOOL_BINARY): jtool_bin = settings.JTOOL_BINARY else: jtool_bin = os.path.join(tools_dir, 'jtool.ELF64') plat = platform.system() if cmd_type == "libs": if plat == "Darwin": args = [otool_bin, '-L', bin_path] elif plat == "Linux": args = [jtool_bin, '-arch', 'arm', '-L', '-v', bin_path] else: # Platform Not Supported return None libs = subprocess.check_output(args).decode("utf-8", "ignore") libs = smart_text(escape(libs.replace(bin_dir + "/", ""))) return libs.split("\n") elif cmd_type == "header": if plat == "Darwin": args = [otool_bin, '-hv', bin_path] elif plat == "Linux": args = [jtool_bin, '-arch', 'arm', '-h', '-v', bin_path] else: # Platform Not Supported return None return subprocess.check_output(args) elif cmd_type == "symbols": if plat == "Darwin": args = [otool_bin, '-Iv', bin_path] return subprocess.check_output(args) elif plat == "Linux": arg1 = [jtool_bin, '-arch', 'arm', '-bind', '-v', bin_path] arg2 = [jtool_bin, '-arch', 'arm', '-lazy_bind', '-v', bin_path] return subprocess.check_output(arg1) + subprocess.check_output(arg2) else: # Platform Not Supported return None
def jar_2_java(app_dir, tools_dir): """Conver jar to java.""" try: print("[INFO] JAR -> JAVA") jar_files = get_jar_files(app_dir) output = os.path.join(app_dir, 'java_source/') for jar_path in jar_files: print("[INFO] Decompiling " + jar_path + " to Java Code") if settings.DECOMPILER == 'jd-core': if (len(settings.JD_CORE_DECOMPILER_BINARY) > 0 and isFileExists(settings.JD_CORE_DECOMPILER_BINARY)): jd_path = settings.JD_CORE_DECOMPILER_BINARY else: jd_path = os.path.join(tools_dir, 'jd-core.jar') args = [ settings.JAVA_PATH + 'java', '-jar', jd_path, jar_path, output ] elif settings.DECOMPILER == 'cfr': if (len(settings.CFR_DECOMPILER_BINARY) > 0 and isFileExists(settings.CFR_DECOMPILER_BINARY)): jd_path = settings.CFR_DECOMPILER_BINARY else: jd_path = os.path.join(tools_dir, 'cfr_0_119.jar') args = [ settings.JAVA_PATH + 'java', '-jar', jd_path, jar_path, '--outputdir', output ] elif settings.DECOMPILER == "procyon": if (len(settings.PROCYON_DECOMPILER_BINARY) > 0 and isFileExists(settings.PROCYON_DECOMPILER_BINARY)): pd_path = settings.PROCYON_DECOMPILER_BINARY else: pd_path = os.path.join(tools_dir, 'procyon-decompiler-0.5.30.jar') args = [ settings.JAVA_PATH + 'java', '-jar', pd_path, jar_path, '-o', output ] subprocess.call(args) except: PrintException("[ERROR] Converting JAR to JAVA")
def jar_2_java(app_dir, tools_dir): """Conver jar to java.""" try: print("[INFO] JAR -> JAVA") jar_files = get_jar_files(app_dir) output = os.path.join(app_dir, 'java_source/') for jar_path in jar_files: print ("[INFO] Decompiling " + jar_path + " to Java Code") if settings.DECOMPILER == 'jd-core': if ( len(settings.JD_CORE_DECOMPILER_BINARY) > 0 and isFileExists(settings.JD_CORE_DECOMPILER_BINARY) ): jd_path = settings.JD_CORE_DECOMPILER_BINARY else: jd_path = os.path.join(tools_dir, 'jd-core.jar') args = [settings.JAVA_PATH + 'java', '-jar', jd_path, jar_path, output] elif settings.DECOMPILER == 'cfr': if ( len(settings.CFR_DECOMPILER_BINARY) > 0 and isFileExists(settings.CFR_DECOMPILER_BINARY) ): jd_path = settings.CFR_DECOMPILER_BINARY else: jd_path = os.path.join(tools_dir, 'cfr_0_132.jar') args = [settings.JAVA_PATH + 'java', '-jar', jd_path, jar_path, '--outputdir', output] elif settings.DECOMPILER == "procyon": if ( len(settings.PROCYON_DECOMPILER_BINARY) > 0 and isFileExists(settings.PROCYON_DECOMPILER_BINARY) ): pd_path = settings.PROCYON_DECOMPILER_BINARY else: pd_path = os.path.join( tools_dir, 'procyon-decompiler-0.5.30.jar') args = [settings.JAVA_PATH + 'java', '-jar', pd_path, jar_path, '-o', output] subprocess.call(args) except: PrintException("[ERROR] Converting JAR to JAVA")
def dex_2_jar(app_path, app_dir, tools_dir): """Run dex2jar.""" try: print "[INFO] DEX -> JAR" working_dir = None args = [] if settings.JAR_CONVERTER == "d2j": print "[INFO] Using JAR converter - dex2jar" if platform.system() == "Windows": win_fix_java(tools_dir) d2j = os.path.join(tools_dir, 'd2j2/d2j-dex2jar.bat') else: inv = os.path.join(tools_dir, 'd2j2/d2j_invoke.sh') d2j = os.path.join(tools_dir, 'd2j2/d2j-dex2jar.sh') subprocess.call(["chmod", "777", d2j]) subprocess.call(["chmod", "777", inv]) if len(settings.DEX2JAR_BINARY) > 0 and isFileExists( settings.DEX2JAR_BINARY): d2j = settings.DEX2JAR_BINARY args = [ d2j, app_dir + 'classes.dex', '-f', '-o', app_dir + 'classes.jar' ] elif settings.JAR_CONVERTER == "enjarify": print "[INFO] Using JAR converter - Google enjarify" if len(settings.ENJARIFY_DIRECTORY) > 0 and isDirExists( settings.ENJARIFY_DIRECTORY): working_dir = settings.ENJARIFY_DIRECTORY else: working_dir = os.path.join(tools_dir, 'enjarify/') if platform.system() == "Windows": win_fix_python3(tools_dir) enjarify = os.path.join(working_dir, 'enjarify.bat') args = [ enjarify, app_path, "-f", "-o", app_dir + 'classes.jar' ] else: if len(settings.PYTHON3_PATH) > 2: python3 = os.path.join(settings.PYTHON3_PATH, "python3") else: python3 = "python3" args = [ python3, "-O", "-m", "enjarify.main", app_path, "-f", "-o", app_dir + 'classes.jar' ] if working_dir: subprocess.call(args, cwd=working_dir) else: subprocess.call(args) except: PrintException("[ERROR] Converting Dex to JAR")
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() 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 class_dump_z(tools_dir, bin_path, app_dir): """Running Classdumpz on binary""" try: webview = {} if platform.system() == "Darwin": print("[INFO] Running class-dump-z against the binary for dumping classes") if len(settings.CLASSDUMPZ_BINARY) > 0 and isFileExists(settings.CLASSDUMPZ_BINARY): class_dump_z_bin = settings.CLASSDUMPZ_BINARY else: class_dump_z_bin = os.path.join(tools_dir, 'class-dump-z') subprocess.call(["chmod", "777", class_dump_z_bin]) args = [class_dump_z_bin, bin_path] elif platform.system() == "Linux": print("[INFO] Running jtool against the binary for dumping classes") if len(settings.JTOOL_BINARY) > 0 and isFileExists(settings.JTOOL_BINARY): jtool_bin = settings.JTOOL_BINARY else: jtool_bin = os.path.join(tools_dir, 'jtool.ELF64') subprocess.call(["chmod", "777", jtool_bin]) args = [jtool_bin, '-arch', 'arm', '-d', 'objc', '-v', bin_path] else: # Platform not supported return {} classdump = subprocess.check_output(args) dump_file = os.path.join(app_dir, "classdump.txt") with open(dump_file, "w") as flip: flip.write(classdump.decode("utf-8", "ignore")) if b"UIWebView" in classdump: webview = {"issue": "Binary uses WebView Component.", "status": INFO, "description": "The binary may use WebView Component." } return webview except: print("[INFO] class-dump-z does not work on iOS apps developed in Swift") PrintException("[ERROR] - Cannot perform class dump")
def api(self): """ For REST API """ request = self.request fil = request.GET['file'] typ = request.GET['type'] md5_hash = request.GET['md5'] mode = request.GET['mode'] md5_match = re.match('^[0-9a-f]{32}$', md5_hash) ext = fil.split('.')[-1] ext_type = re.search("plist|db|sqlitedb|sqlite|txt|m", ext) if not md5_match or not ext_type or not re.findall('xml|db|txt|m', typ) or not re.findall('ios|ipa', mode): return HttpResponseBadRequest() 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 = '' file_format = 'txt' 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 = views.ios.static_analyzer.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 not Found" context = {'title': escape(ntpath.basename(fil)), 'file': escape(ntpath.basename(fil)), 'type': file_format, 'dat': dat} return JsonResponse(context)
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 dex_2_smali(app_dir, tools_dir): """Run dex2smali""" try: print "[INFO] DEX -> SMALI" dex_path = app_dir + 'classes.dex' if len(settings.BACKSMALI_BINARY) > 0 and isFileExists(settings.BACKSMALI_BINARY): bs_path = settings.BACKSMALI_BINARY else: bs_path = os.path.join(tools_dir, 'baksmali.jar') output = os.path.join(app_dir, 'smali_source/') args = [ settings.JAVA_PATH + 'java', '-jar', bs_path, dex_path, '-o', output ] subprocess.call(args) except: PrintException("[ERROR] Converting DEX to SMALI")
def dex_2_smali(app_dir, tools_dir): """Run dex2smali""" try: print "[INFO] DEX -> SMALI" dex_path = app_dir + 'classes.dex' if len(settings.BACKSMALI_BINARY) > 0 and isFileExists( settings.BACKSMALI_BINARY): bs_path = settings.BACKSMALI_BINARY else: bs_path = os.path.join(tools_dir, 'baksmali.jar') output = os.path.join(app_dir, 'smali_source/') args = [ settings.JAVA_PATH + 'java', '-jar', bs_path, dex_path, '-o', output ] subprocess.call(args) except: PrintException("[ERROR] Converting DEX to SMALI")
def binary_analysis(src, tools_dir, app_dir, executable_name): '''Binary Analysis of IPA''' try: binary_analysis_dict = {} logger.info('Starting Binary Analysis') dirs = os.listdir(src) dot_app_dir = '' for dir_ in dirs: if dir_.endswith('.app'): dot_app_dir = dir_ break # Bin Dir - Dir/Payload/x.app/ bin_dir = os.path.join(src, dot_app_dir) if executable_name is None: bin_name = dot_app_dir.replace('.app', '') else: bin_name = executable_name # Bin Path - Dir/Payload/x.app/x bin_path = os.path.join(bin_dir, bin_name) binary_analysis_dict['libs'] = [] binary_analysis_dict['bin_res'] = [] binary_analysis_dict['strings'] = [] if not isFileExists(bin_path): logger.warning('MobSF Cannot find binary in ' + bin_path) logger.warning('Skipping Otool, Classdump and Strings') else: bin_info = get_bin_info(bin_path) otool_dict = otool_analysis(tools_dir, bin_name, bin_path, bin_dir) bin_type = detect_bin_type(otool_dict["libs"]) cls_dump = class_dump(tools_dir, bin_path, app_dir, bin_type) if not cls_dump: cls_dump = {} strings_in_ipa = strings_on_ipa(bin_path) otool_dict['anal'] = list( filter(None, otool_dict['anal'] + [cls_dump])) binary_analysis_dict['libs'] = otool_dict['libs'] binary_analysis_dict['bin_res'] = otool_dict['anal'] binary_analysis_dict['strings'] = strings_in_ipa binary_analysis_dict['macho'] = bin_info binary_analysis_dict['bin_type'] = bin_type return binary_analysis_dict except: PrintException('iOS Binary Analysis')
def dex_2_smali(app_dir, tools_dir): """Run dex2smali""" try: logger.info("DEX -> SMALI") dexes = get_dex_files(app_dir) for dex_path in dexes: logger.info("Converting " + filename_from_path(dex_path) + " to Smali Code") if len(settings.BACKSMALI_BINARY) > 0 and isFileExists(settings.BACKSMALI_BINARY): bs_path = settings.BACKSMALI_BINARY else: bs_path = os.path.join(tools_dir, 'baksmali-2.2.7.jar') output = os.path.join(app_dir, 'smali_source/') args = [ settings.JAVA_PATH + 'java', '-jar', bs_path, 'd', dex_path, '-o', output ] subprocess.call(args) except: PrintException("Converting DEX to SMALI")
def getADB(TOOLSDIR): print "\n[INFO] Getting ADB Location" try: if len(settings.ADB_BINARY) > 0 and isFileExists(settings.ADB_BINARY): return settings.ADB_BINARY else: adb = 'adb' if platform.system() == "Darwin": adb_dir = os.path.join(TOOLSDIR, 'adb/mac/') subprocess.call(["chmod", "777", adb_dir]) adb = os.path.join(TOOLSDIR, 'adb/mac/adb') elif platform.system() == "Linux": adb_dir = os.path.join(TOOLSDIR, 'adb/linux/') subprocess.call(["chmod", "777", adb_dir]) adb = os.path.join(TOOLSDIR, 'adb/linux/adb') elif platform.system() == "Windows": adb = os.path.join(TOOLSDIR, 'adb/windows/adb.exe') return adb except: PrintException("[ERROR] Getting ADB Location") return "adb"
def getADB(TOOLSDIR): print "\n[INFO] Getting ADB Location" try: if len(settings.ADB_BINARY) > 0 and isFileExists(settings.ADB_BINARY): return settings.ADB_BINARY else: adb = "adb" if platform.system() == "Darwin": adb_dir = os.path.join(TOOLSDIR, "adb/mac/") subprocess.call(["chmod", "777", adb_dir]) adb = os.path.join(TOOLSDIR, "adb/mac/adb") elif platform.system() == "Linux": adb_dir = os.path.join(TOOLSDIR, "adb/linux/") subprocess.call(["chmod", "777", adb_dir]) adb = os.path.join(TOOLSDIR, "adb/linux/adb") elif platform.system() == "Windows": adb = os.path.join(TOOLSDIR, "adb/windows/adb.exe") return adb except: PrintException("[ERROR] Getting ADB Location") return "adb"
def class_dump_z(tools_dir, bin_path, app_dir): """Running Classdumpz on binary""" try: webview = '' print "[INFO] Running class-dump-z against the Binary" if len(settings.CLASSDUMPZ_BINARY) > 0 and isFileExists(settings.CLASSDUMPZ_BINARY): class_dump_z_bin = settings.CLASSDUMPZ_BINARY else: class_dump_z_bin = os.path.join(tools_dir, 'class-dump-z') subprocess.call(["chmod", "777", class_dump_z_bin]) class_dump = subprocess.check_output([class_dump_z_bin, bin_path]) dump_file = os.path.join(app_dir, "classdump.txt") with open(dump_file, "w") as flip: flip.write(class_dump) if "UIWebView" in class_dump: webview = "<tr><td>Binary uses WebView Component.</td><td>" +\ "<span class='label label-info'>Info</span></td><td>The binary" +\ " may use WebView Component.</td></tr>" return webview except: PrintException("[ERROR] - Cannot perform class dump")
def binary_analysis(src, tools_dir, app_dir, executable_name): """Binary Analysis of IPA""" try: binary_analysis_dict = {} print("[INFO] Starting Binary Analysis") dirs = os.listdir(src) dot_app_dir = "" for dir_ in dirs: if dir_.endswith(".app"): dot_app_dir = dir_ break # Bin Dir - Dir/Payload/x.app/ bin_dir = os.path.join(src, dot_app_dir) if executable_name is None: bin_name = dot_app_dir.replace(".app", "") else: bin_name = executable_name # Bin Path - Dir/Payload/x.app/x bin_path = os.path.join(bin_dir, bin_name) binary_analysis_dict["libs"] = [] binary_analysis_dict["bin_res"] = [] binary_analysis_dict["strings"] = [] if not isFileExists(bin_path): print("[WARNING] MobSF Cannot find binary in " + bin_path) print("[WARNING] Skipping Otool, Classdump and Strings") else: otool_dict = otool_analysis(tools_dir, bin_name, bin_path, bin_dir) cls_dump = class_dump_z(tools_dir, bin_path, app_dir) # Classdumpz can fail on swift coded binaries if not cls_dump: cls_dump = {} strings_in_ipa = strings_on_ipa(bin_path) otool_dict["anal"] = list( filter(None, otool_dict["anal"] + [cls_dump])) binary_analysis_dict["libs"] = otool_dict["libs"] binary_analysis_dict["bin_res"] = otool_dict["anal"] binary_analysis_dict["strings"] = strings_in_ipa return binary_analysis_dict except: PrintException("[ERROR] iOS Binary Analysis")
def class_dump_z(tools_dir, bin_path, app_dir): """Running Classdumpz on binary""" try: webview = '' print "[INFO] Running class-dump-z against the Binary" if len(settings.CLASSDUMPZ_BINARY) > 0 and isFileExists( settings.CLASSDUMPZ_BINARY): class_dump_z_bin = settings.CLASSDUMPZ_BINARY else: class_dump_z_bin = os.path.join(tools_dir, 'class-dump-z') subprocess.call(["chmod", "777", class_dump_z_bin]) class_dump = subprocess.check_output([class_dump_z_bin, bin_path]) dump_file = os.path.join(app_dir, "classdump.txt") with open(dump_file, "w") as flip: flip.write(class_dump) if "UIWebView" in class_dump: webview = "<tr><td>Binary uses WebView Component.</td><td>" +\ "<span class='label label-info'>Info</span></td><td>The binary" +\ " may use WebView Component.</td></tr>" return webview except: PrintException("[ERROR] - Cannot perform class dump")
def binary_analysis(src, tools_dir, app_dir, executable_name): """Binary Analysis of IPA""" try: binary_analysis_dict = {} print("[INFO] Starting Binary Analysis") dirs = os.listdir(src) dot_app_dir = "" for dir_ in dirs: if dir_.endswith(".app"): dot_app_dir = dir_ break # Bin Dir - Dir/Payload/x.app/ bin_dir = os.path.join(src, dot_app_dir) if executable_name is None: bin_name = dot_app_dir.replace(".app", "") else: bin_name = executable_name # Bin Path - Dir/Payload/x.app/x bin_path = os.path.join(bin_dir, bin_name) binary_analysis_dict["libs"] = [] binary_analysis_dict["bin_res"] = [] binary_analysis_dict["strings"] = [] if not isFileExists(bin_path): print("[WARNING] MobSF Cannot find binary in " + bin_path) print("[WARNING] Skipping Otool, Classdump and Strings") else: otool_dict = otool_analysis(tools_dir, bin_name, bin_path, bin_dir) cls_dump = class_dump_z(tools_dir, bin_path, app_dir) # Classdumpz can fail on swift coded binaries if not cls_dump: cls_dump = {} strings_in_ipa = strings_on_ipa(bin_path) otool_dict["anal"] = list(filter(None, otool_dict["anal"] + [cls_dump])) binary_analysis_dict["libs"] = otool_dict["libs"] binary_analysis_dict["bin_res"] = otool_dict["anal"] binary_analysis_dict["strings"] = strings_in_ipa return binary_analysis_dict except: PrintException("[ERROR] iOS Binary Analysis")
def get_embedded_classes(self): """ Get the list of Java classes from all DEX files. :return: list of Java classes """ if self.classes is not None: return self.classes for dex_file in glob.iglob(os.path.join(self.apk_dir, '*.dex')): if len(settings.BACKSMALI_BINARY) > 0 and isFileExists( settings.BACKSMALI_BINARY): bs_path = settings.BACKSMALI_BINARY else: bs_path = os.path.join(self.tools_dir, 'baksmali-2.2.7.jar') args = [ settings.JAVA_PATH + 'java', '-jar', bs_path, 'list', 'classes', dex_file ] classes = subprocess.check_output( args, universal_newlines=True).splitlines() if self.classes is not None: self.classes = self.classes + classes else: self.classes = classes return self.classes
def otool_analysis(bin_name, bin_path, bin_dir): """OTOOL Analysis of Binary""" try: print "[INFO] Starting Otool Analysis" otool_dict = {} otool_dict["libs"] = '' otool_dict["anal"] = '' print "[INFO] Running otool against Binary : " + bin_name if len(settings.OTOOL_BINARY) > 0 and isFileExists( settings.OTOOL_BINARY): otool_bin = settings.OTOOL_BINARY else: otool_bin = "otool" args = [otool_bin, '-L', bin_path] libs = unicode(subprocess.check_output(args), 'utf-8') libs = smart_text(escape(libs.replace(bin_dir + "/", ""))) otool_dict["libs"] = libs.replace("\n", "</br>") # PIE args = [otool_bin, '-hv', bin_path] pie_dat = subprocess.check_output(args) if "PIE" in pie_dat: pie_flag = "<tr><td><strong>fPIE -pie</strong> flag is Found</td><td>" + \ "<span class='label label-success'>Secure</span>" + \ "</td><td>App is compiled with Position Independent Executable (PIE) flag. " + \ "This enables Address Space Layout Randomization (ASLR), a memory protection" +\ " mechanism for exploit mitigation.</td></tr>" else: pie_flag = "<tr><td><strong>fPIE -pie</strong> flag is not Found</td><td>" +\ "<span class='label label-danger'>Insecure</span></td><td>App is not compiled" +\ " with Position Independent Executable (PIE) flag. So Address Space Layout " +\ "Randomization (ASLR) is missing. ASLR is a memory protection mechanism for" +\ " exploit mitigation.</td></tr>" # Stack Smashing Protection & ARC args = [otool_bin, '-Iv', bin_path] dat = subprocess.check_output(args) if "stack_chk_guard" in dat: ssmash = "<tr><td><strong>fstack-protector-all</strong> flag is Found</td><td>" +\ "<span class='label label-success'>Secure</span></td><td>App is compiled with" +\ " Stack Smashing Protector (SSP) flag and is having protection against Stack" +\ " Overflows/Stack Smashing Attacks.</td></tr>" else: ssmash = "<tr><td><strong>fstack-protector-all</strong> flag is not Found</td><td>" +\ "<span class='label label-danger'>Insecure</span></td><td>App is " +\ "not compiled with Stack Smashing Protector (SSP) flag. It is vulnerable to " +\ "Stack Overflows/Stack Smashing Attacks.</td></tr>" # ARC if "_objc_release" in dat: arc_flag = "<tr><td><strong>fobjc-arc</strong> flag is Found</td><td>" +\ "<span class='label label-success'>Secure</span></td><td>App is compiled " +\ "with Automatic Reference Counting (ARC) flag. ARC is a compiler feature " +\ "that provides automatic memory management of Objective-C objects and is an" +\ " exploit mitigation mechanism against memory corruption vulnerabilities.</td></tr>" else: arc_flag = "<tr><td><strong>fobjc-arc</strong> flag is not Found</td><td>" +\ "<span class='label label-danger'>Insecure</span></td><td>App is not compiled" +\ " with Automatic Reference Counting (ARC) flag. ARC is a compiler feature that" +\ " provides automatic memory management of Objective-C objects and protects from" +\ " memory corruption vulnerabilities.</td></tr>" banned_apis = '' baned = re.findall( "alloca|gets|memcpy|printf|scanf|sprintf|sscanf|strcat|StrCat|strcpy|" + "StrCpy|strlen|StrLen|strncat|StrNCat|strncpy|StrNCpy|strtok|swprintf|vsnprintf|" + "vsprintf|vswprintf|wcscat|wcscpy|wcslen|wcsncat|wcsncpy|wcstok|wmemcpy", dat) baned = list(set(baned)) baned_s = ', '.join(baned) if len(baned_s) > 1: banned_apis = "<tr><td>Binary make use of banned API(s)</td><td>" +\ "<span class='label label-danger'>Insecure</span></td><td>The binary " +\ "may contain the following banned API(s) </br><strong>" + \ str(baned_s) + "</strong>.</td></tr>" weak_cryptos = '' weak_algo = re.findall( "kCCAlgorithmDES|kCCAlgorithm3DES||kCCAlgorithmRC2|kCCAlgorithmRC4|" + "kCCOptionECBMode|kCCOptionCBCMode", dat) weak_algo = list(set(weak_algo)) weak_algo_s = ', '.join(weak_algo) if len(weak_algo_s) > 1: weak_cryptos = "<tr><td>Binary make use of some Weak Crypto API(s)</td><td>" +\ "<span class='label label-danger'>Insecure</span></td><td>The binary may use " +\ "the following weak crypto API(s)</br><strong>" + \ str(weak_algo_s) + "</strong>.</td></tr>" crypto = '' crypto_algo = re.findall( "CCKeyDerivationPBKDF|CCCryptorCreate|CCCryptorCreateFromData|" + "CCCryptorRelease|CCCryptorUpdate|CCCryptorFinal|CCCryptorGetOutputLength|" + "CCCryptorReset|CCCryptorRef|kCCEncrypt|kCCDecrypt|kCCAlgorithmAES128|" + "kCCKeySizeAES128|kCCKeySizeAES192|kCCKeySizeAES256|kCCAlgorithmCAST|" + "SecCertificateGetTypeID|SecIdentityGetTypeID|SecKeyGetTypeID|SecPolicyGetTypeID|" + "SecTrustGetTypeID|SecCertificateCreateWithData|SecCertificateCreateFromData|" + "SecCertificateCopyData|SecCertificateAddToKeychain|SecCertificateGetData|" + "SecCertificateCopySubjectSummary|SecIdentityCopyCertificate|" + "SecIdentityCopyPrivateKey|SecPKCS12Import|SecKeyGeneratePair|SecKeyEncrypt|" + "SecKeyDecrypt|SecKeyRawSign|SecKeyRawVerify|SecKeyGetBlockSize|" + "SecPolicyCopyProperties|SecPolicyCreateBasicX509|SecPolicyCreateSSL|" + "SecTrustCopyCustomAnchorCertificates|SecTrustCopyExceptions|" + "SecTrustCopyProperties|SecTrustCopyPolicies|SecTrustCopyPublicKey|" + "SecTrustCreateWithCertificates|SecTrustEvaluate|SecTrustEvaluateAsync|" + "SecTrustGetCertificateCount|SecTrustGetCertificateAtIndex|SecTrustGetTrustResult|" + "SecTrustGetVerifyTime|SecTrustSetAnchorCertificates|" + "SecTrustSetAnchorCertificatesOnly|SecTrustSetExceptions|SecTrustSetPolicies|" + "SecTrustSetVerifyDate|SecCertificateRef|" + "SecIdentityRef|SecKeyRef|SecPolicyRef|SecTrustRef", dat) crypto_algo = list(set(crypto_algo)) crypto_algo_s = ', '.join(crypto_algo) if len(crypto_algo_s) > 1: crypto = "<tr><td>Binary make use of the following Crypto API(s)</td><td>" +\ "<span class='label label-info'>Info</span></td><td>The binary may use the" +\ " following crypto API(s)</br><strong>" + \ str(crypto_algo_s) + "</strong>.</td></tr>" weak_hashes = '' weak_hash_algo = re.findall( "CC_MD2_Init|CC_MD2_Update|CC_MD2_Final|CC_MD2|MD2_Init|" + "MD2_Update|MD2_Final|CC_MD4_Init|CC_MD4_Update|CC_MD4_Final|CC_MD4|MD4_Init|" + "MD4_Update|MD4_Final|CC_MD5_Init|CC_MD5_Update|CC_MD5_Final|CC_MD5|MD5_Init|" + "MD5_Update|MD5_Final|MD5Init|MD5Update|MD5Final|CC_SHA1_Init|CC_SHA1_Update|" + "CC_SHA1_Final|CC_SHA1|SHA1_Init|SHA1_Update|SHA1_Final", dat) weak_hash_algo = list(set(weak_hash_algo)) weak_hash_algo_s = ', '.join(weak_hash_algo) if len(weak_hash_algo_s) > 1: weak_hashes = "<tr><td>Binary make use of the following Weak HASH API(s)</td><td>" +\ "<span class='label label-danger'>Insecure</span></td><td>The binary " +\ "may use the following weak hash API(s)</br><strong>" + \ str(weak_hash_algo_s) + "</strong>.</td></tr>" hashes = '' hash_algo = re.findall( "CC_SHA224_Init|CC_SHA224_Update|CC_SHA224_Final|CC_SHA224|" + "SHA224_Init|SHA224_Update|SHA224_Final|CC_SHA256_Init|CC_SHA256_Update|" + "CC_SHA256_Final|CC_SHA256|SHA256_Init|SHA256_Update|SHA256_Final|" + "CC_SHA384_Init|CC_SHA384_Update|CC_SHA384_Final|CC_SHA384|SHA384_Init|" + "SHA384_Update|SHA384_Final|CC_SHA512_Init|CC_SHA512_Update|CC_SHA512_Final|" + "CC_SHA512|SHA512_Init|SHA512_Update|SHA512_Final", dat) hash_algo = list(set(hash_algo)) hash_algo_s = ', '.join(hash_algo) if len(hash_algo_s) > 1: hashes = "<tr><td>Binary make use of the following HASH API(s)</td><td>" +\ "<span class='label label-info'>Info</span></td><td>The binary may use the" +\ " following hash API(s)</br><strong>" + \ str(hash_algo_s) + "</strong>.</td></tr>" randoms = '' rand_algo = re.findall("srand|random", dat) rand_algo = list(set(rand_algo)) rand_algo_s = ', '.join(rand_algo) if len(rand_algo_s) > 1: randoms = "<tr><td>Binary make use of the insecure Random Function(s)</td><td>" +\ "<span class='label label-danger'>Insecure</span></td><td>The binary may " +\ "use the following insecure Random Function(s)</br><strong>" + \ str(rand_algo_s) + "</strong>.</td></tr>" logging = '' log = re.findall("NSLog", dat) log = list(set(log)) log_s = ', '.join(log) if len(log_s) > 1: logging = "<tr><td>Binary make use of Logging Function</td><td>" +\ "<span class='label label-info'>Info</span></td><td>The binary may " +\ "use <strong>NSLog</strong> function for logging.</td></tr>" malloc = '' mal = re.findall("malloc", dat) mal = list(set(mal)) mal_s = ', '.join(mal) if len(mal_s) > 1: malloc = "<tr><td>Binary make use of <strong>malloc</strong> Function</td><td>" +\ "<span class='label label-danger'>Insecure</span></td><td>The binary may use " +\ "<strong>malloc</strong> function instead of <strong>calloc</strong>.</td></tr>" debug = '' ptrace = re.findall("ptrace", dat) ptrace = list(set(ptrace)) ptrace_s = ', '.join(ptrace) if len(ptrace_s) > 1: debug = "<tr><td>Binary calls <strong>ptrace</strong> Function for anti-debugging." +\ "</td><td><span class='label label-warning'>warning</span></td><td>The binary" +\ " may use <strong>ptrace</strong> function. It can be used to detect and prevent" +\ " debuggers. Ptrace is not a public API and Apps that use non-public APIs will" +\ " be rejected from AppStore. </td></tr>" otool_dict["anal"] = pie_flag + ssmash + arc_flag + banned_apis + weak_cryptos + \ crypto + weak_hashes + hashes + randoms + logging + malloc + \ debug return otool_dict except: PrintException("[ERROR] Performing Otool Analysis of Binary")
def plist_analysis(src, is_source): """Plist Analysis""" try: print "[INFO] iOS Info.plist Analysis Started" plist_info = {} plist_info["bin_name"] = "" plist_info["bin"] = "" plist_info["id"] = "" plist_info["ver"] = "" plist_info["sdk"] = "" plist_info["pltfm"] = "" plist_info["min"] = "" plist_info["plist_xml"] = "" plist_info["permissions"] = [] plist_info["inseccon"] = [] info_plist_content = '' if is_source: info_plist = '' print "[INFO] Finding Info.plist in iOS Source" for ifile in os.listdir(src): if ifile.endswith(".xcodeproj"): app_name = ifile.replace(".xcodeproj", "") break plist_file = app_name + "-Info.plist" for dirname, _, files in os.walk(src): for jfile in files: if (plist_file in jfile) and ("__MACOSX" not in dirname): info_plist = os.path.join(src, dirname, jfile) break if not isFileExists(info_plist): print "[WARNING] Cannot find Info.plist file. Skipping Plist analysis" else: with io.open(info_plist, mode='r', encoding="utf8", errors="ignore") as flip: info_plist_content = flip.read() else: print "[INFO] Finding Info.plist in iOS Binary" dirs = os.listdir(src) dot_app_dir = "" for dir_ in dirs: if dir_.endswith(".app"): dot_app_dir = dir_ break bin_dir = os.path.join(src, dot_app_dir) # Full Dir/Payload/x.app xml_file = os.path.join(bin_dir, "Info.plist") if not isFileExists(xml_file): print "[WARNING] Cannot find Info.plist file. Skipping Plist Analysis." else: info_plist_content = convert_bin_xml(xml_file) #Generic Plist Analysis plist_info["plist_xml"] = info_plist_content if isinstance(info_plist_content, unicode): info_plist_content = info_plist_content.encode("utf-8", "replace") plist_obj = plistlib.readPlistFromString(info_plist_content) if "CFBundleDisplayName" in plist_obj: plist_info["bin_name"] = plist_obj["CFBundleDisplayName"] else: if not is_source: #For iOS IPA plist_info["bin_name"] = dot_app_dir.replace(".app", "") if "CFBundleExecutable" in plist_obj: plist_info["bin"] = plist_obj["CFBundleExecutable"] if "CFBundleIdentifier" in plist_obj: plist_info["id"] = plist_obj["CFBundleIdentifier"] if "CFBundleVersion" in plist_obj: plist_info["ver"] = plist_obj["CFBundleVersion"] if "DTSDKName" in plist_obj: plist_info["sdk"] = plist_obj["DTSDKName"] if "DTPlatformVersion" in plist_obj: plist_info["pltfm"] = plist_obj["DTPlatformVersion"] if "MinimumOSVersion" in plist_obj: plist_info["min"] = plist_obj["MinimumOSVersion"] # Check possible app-permissions plist_info["permissions"] = __check_permissions(plist_obj) plist_info["inseccon"] = __check_insecure_connections(plist_obj) return plist_info except: PrintException("[ERROR] - Reading from Info.plist")
def iOS_Source_Analysis(SRC, MD5): try: print "[INFO] Starting iOS Source Code and PLIST Analysis" ALLURLSLST = list() DOMAINS = dict() APP = '' InfoP = '' BIN_NAME = '' BIN = '' ID = '' VER = '' SDK = '' PLTFM = '' MIN = '' XML = '' for f in os.listdir(SRC): if f.endswith(".xcodeproj"): APP = f.replace(".xcodeproj", "") PlistFile = APP + "-Info.plist" for dirName, subDir, files in os.walk(SRC): for jfile in files: if PlistFile in jfile: InfoP = os.path.join(SRC, dirName, jfile) break if isFileExists(InfoP): with io.open(InfoP, mode='r', encoding="utf8", errors="ignore") as f: XML = f.read() if XML: p = plistlib.readPlistFromString(XML) BIN_NAME = p["CFBundleDisplayName"] BIN = p["CFBundleExecutable"] ID = p["CFBundleIdentifier"] VER = p["CFBundleVersion"] SDK = '' # p["DTSDKName"] PLTFM = '' # p["DTPlatformVersion"] MIN = '' # p["MinimumOSVersion"] # Code Analysis EmailnFile = '' URLnFile = '' c = { key: [] for key in ('i_buf', 'webv', 'i_log', 'net', 'i_sqlite', 'fileio', 'ssl_bypass', 'ssl_uiwebview', 'path_traversal') } for dirName, subDir, files in os.walk(SRC): for jfile in files: if jfile.endswith(".m"): jfile_path = os.path.join(SRC, dirName, jfile) if "+" in jfile: p2 = os.path.join(SRC, dirName, jfile.replace("+", "x")) shutil.move(jfile_path, p2) jfile_path = p2 repath = dirName.replace(SRC, '') dat = '' with io.open(jfile_path, mode='r', encoding="utf8", errors="ignore") as f: dat = f.read() URLS = [] EMAILS = [] # API if (re.findall("NSURL|CFStream|NSStream", dat)): c['net'].append(jfile_path.replace(SRC, '')) if (re.findall( "Keychain|kSecAttrAccessibleWhenUnlocked|kSecAttrAccessibleAfterFirstUnlock|SecItemAdd|SecItemUpdate|NSDataWritingFileProtectionComplete", dat)): c['fileio'].append(jfile_path.replace(SRC, '')) if (re.findall("WebView|UIWebView", dat)): c['webv'].append(jfile_path.replace(SRC, '')) # SECURITY ANALYSIS if (re.findall( "strcpy|memcpy|strcat|strncat|strncpy|sprintf|vsprintf|gets", dat)): c['i_buf'].append(jfile_path.replace(SRC, '')) if (re.findall("NSLog", dat)): c['i_log'].append(jfile_path.replace(SRC, '')) if (re.findall("sqlite3_exec", dat)): c['i_sqlite'].append(jfile_path.replace(SRC, '')) if re.findall( 'canAuthenticateAgainstProtectionSpace|continueWithoutCredentialForAuthenticationChallenge|kCFStreamSSLAllowsExpiredCertificates|kCFStreamSSLAllowsAnyRoot|kCFStreamSSLAllowsExpiredRoots|allowInvalidCertificates\s*=\s*(YES|yes)', dat): c['ssl_bypass'].append(jfile_path.replace(SRC, '')) if re.findall( 'setAllowsAnyHTTPSCertificate:\s*YES|allowsAnyHTTPSCertificateForHost|loadingUnvalidatedHTTPSPage\s*=\s*(YES|yes)', dat): c['ssl_uiwebview'].append(jfile_path.replace(SRC, '')) if "NSTemporaryDirectory()," in dat: c['path_traversal'].append(jfile_path.replace(SRC, '')) fl = jfile_path.replace(SRC, '') base_fl = ntpath.basename(fl) # URLs My Custom regex p = re.compile( ur'((?:https?://|s?ftps?://|file://|javascript:|data:|www\d{0,3}[.])[\w().=/;,#:@?&~*+!$%\'{}-]+)', re.UNICODE) urllist = re.findall(p, dat.lower()) ALLURLSLST.extend(urllist) uflag = 0 for url in urllist: if url not in URLS: URLS.append(url) uflag = 1 if uflag == 1: URLnFile += "<tr><td>" + "<br>".join( URLS ) + "</td><td><a href='../ViewFile/?file=" + escape( fl ) + "&type=m&mode=ios&md5=" + MD5 + "'>" + escape( base_fl) + "</a></td></tr>" # Email Etraction Regex regex = re.compile("[\w.-]+@[\w-]+\.[\w.]+") eflag = 0 for email in regex.findall(dat.lower()): if ((email not in EMAILS) and (not email.startswith('//'))): EMAILS.append(email) eflag = 1 if eflag == 1: EmailnFile += "<tr><td>" + "<br>".join( EMAILS ) + "</td><td><a href='../ViewFile/?file=" + escape( fl ) + "&type=m&mode=ios&md5=" + MD5 + "'>" + escape( base_fl) + "</a></td></tr>" # Domain Extraction and Malware Check print "[INFO] Performing Malware Check on extracted Domains" DOMAINS = MalwareCheck(ALLURLSLST) print "[INFO] Finished Code Analysis, Email and URL Extraction" dc = { 'webv': 'WebView Component', 'net': 'Network Calls', 'fileio': 'Local File I/O Operations.', } html = '' for ky in dc: if c[ky]: link = '' hd = "<tr><td>" + dc[ky] + "</td><td>" for l in c[ky]: link += "<a href='../ViewFile/?file=" + \ escape(l) + "&type=m&mode=ios&md5=" + MD5 + \ "'>" + escape(ntpath.basename(l)) + "</a> " html += hd + link + "</td></tr>" dg = { 'i_buf': 'The App may contain banned API(s). These API(s) are insecure and must not be used.', 'i_log': 'The App logs information. Sensitive information should never be logged.', 'i_sqlite': 'App uses SQLite Database. Sensitive Information should be encrypted.', 'ssl_bypass': '******', 'ssl_uiwebview': 'UIWebView in App ignore SSL errors and accept any SSL Certificate. App is vulnerable to MITM attacks.', 'path_traversal': 'Untrusted user input to "NSTemporaryDirectory()"" will result in path traversal vulnerability.', } dang = '' spn_dang = '<span class="label label-danger">high</span>' spn_info = '<span class="label label-info">info</span>' spn_sec = '<span class="label label-success">secure</span>' spn_warn = '<span class="label label-warning">warning</span>' for k in dg: if c[k]: link = '' if (re.findall('i_sqlite', k)): hd = '<tr><td>' + dg[k] + \ '</td><td>' + spn_info + '</td><td>' elif (re.findall('path_traversal', k)): hd = '<tr><td>' + dg[k] + \ '</td><td>' + spn_warn + '</td><td>' else: hd = '<tr><td>' + dg[k] + \ '</td><td>' + spn_dang + '</td><td>' for ll in c[k]: link += "<a href='../ViewFile/?file=" + \ escape(ll) + "&type=m&mode=ios&md5=" + MD5 + \ "'>" + escape(ntpath.basename(ll)) + "</a> " dang += hd + link + "</td></tr>" return html, dang, URLnFile, DOMAINS, EmailnFile, XML, BIN_NAME, ID, VER, SDK, PLTFM, MIN except: PrintException("[ERROR] iOS Source Code Analysis")
def BinaryAnalysis(SRC, TOOLS_DIR, APP_DIR): try: print "[INFO] Starting Binary Analysis" dirs = os.listdir(SRC) for d in dirs: if d.endswith(".app"): break BIN_DIR = os.path.join(SRC, d) # Full Dir/Payload/x.app XML_FILE = os.path.join(BIN_DIR, "Info.plist") BIN = d.replace(".app", "") BIN_NAME = BIN ID = "" VER = "" SDK = "" PLTFM = "" MIN = "" XML = "" try: print "[INFO] Reading Info.plist" XML = readBinXML(XML_FILE) if isinstance(XML, unicode): XML = XML.encode("utf-8", "replace") p = plistlib.readPlistFromString(XML) BIN_NAME = BIN = ID = VER = SDK = PLTFM = MIN = "" if "CFBundleDisplayName" in p: BIN_NAME = p["CFBundleDisplayName"] if "CFBundleExecutable" in p: BIN = p["CFBundleExecutable"] if "CFBundleIdentifier" in p: ID = p["CFBundleIdentifier"] if "CFBundleVersion" in p: VER = p["CFBundleVersion"] if "DTSDKName" in p: SDK = p["DTSDKName"] if "DTPlatformVersion" in p: PLTFM = p["DTPlatformVersion"] if "MinimumOSVersion" in p: MIN = p["MinimumOSVersion"] except: PrintException("[ERROR] - Reading from Info.plist") BIN_PATH = os.path.join(BIN_DIR, BIN) # Full Dir/Payload/x.app/x print "[INFO] iOS Binary : " + BIN print "[INFO] Running otool against the Binary" # Libs Used LIBS = '' if len(settings.OTOOL_BINARY) > 0 and isFileExists( settings.OTOOL_BINARY): OTOOL = settings.OTOOL_BINARY else: OTOOL = "otool" args = [OTOOL, '-L', BIN_PATH] dat = subprocess.check_output(args) dat = escape(dat.replace(BIN_DIR + "/", "")) LIBS = dat.replace("\n", "</br>") # PIE args = [OTOOL, '-hv', BIN_PATH] dat = subprocess.check_output(args) if "PIE" in dat: PIE = "<tr><td><strong>fPIE -pie</strong> flag is Found</td><td><span class='label label-success'>Secure</span></td><td>App is compiled with Position Independent Executable (PIE) flag. This enables Address Space Layout Randomization (ASLR), a memory protection mechanism for exploit mitigation.</td></tr>" else: PIE = "<tr><td><strong>fPIE -pie</strong> flag is not Found</td><td><span class='label label-danger'>Insecure</span></td><td>App is not compiled with Position Independent Executable (PIE) flag. So Address Space Layout Randomization (ASLR) is missing. ASLR is a memory protection mechanism for exploit mitigation.</td></tr>" # Stack Smashing Protection & ARC args = [OTOOL, '-Iv', BIN_PATH] dat = subprocess.check_output(args) if "stack_chk_guard" in dat: SSMASH = "<tr><td><strong>fstack-protector-all</strong> flag is Found</td><td><span class='label label-success'>Secure</span></td><td>App is compiled with Stack Smashing Protector (SSP) flag and is having protection against Stack Overflows/Stack Smashing Attacks.</td></tr>" else: SSMASH = "<tr><td><strong>fstack-protector-all</strong> flag is not Found</td><td><span class='label label-danger'>Insecure</span></td><td>App is not compiled with Stack Smashing Protector (SSP) flag. It is vulnerable to Stack Overflows/Stack Smashing Attacks.</td></tr>" # ARC if "_objc_release" in dat: ARC = "<tr><td><strong>fobjc-arc</strong> flag is Found</td><td><span class='label label-success'>Secure</span></td><td>App is compiled with Automatic Reference Counting (ARC) flag. ARC is a compiler feature that provides automatic memory management of Objective-C objects and is an exploit mitigation mechanism against memory corruption vulnerabilities.</td></tr>" else: ARC = "<tr><td><strong>fobjc-arc</strong> flag is not Found</td><td><span class='label label-danger'>Insecure</span></td><td>App is not compiled with Automatic Reference Counting (ARC) flag. ARC is a compiler feature that provides automatic memory management of Objective-C objects and protects from memory corruption vulnerabilities.</td></tr>" ########## BANNED_API = '' x = re.findall( "alloca|gets|memcpy|printf|scanf|sprintf|sscanf|strcat|StrCat|strcpy|StrCpy|strlen|StrLen|strncat|StrNCat|strncpy|StrNCpy|strtok|swprintf|vsnprintf|vsprintf|vswprintf|wcscat|wcscpy|wcslen|wcsncat|wcsncpy|wcstok|wmemcpy", dat) x = list(set(x)) x = ', '.join(x) if len(x) > 1: BANNED_API = "<tr><td>Binary make use of banned API(s)</td><td><span class='label label-danger'>Insecure</span></td><td>The binary may contain the following banned API(s) </br><strong>" + str( x) + "</strong>.</td></tr>" WEAK_CRYPTO = '' x = re.findall( "kCCAlgorithmDES|kCCAlgorithm3DES||kCCAlgorithmRC2|kCCAlgorithmRC4|kCCOptionECBMode|kCCOptionCBCMode", dat) x = list(set(x)) x = ', '.join(x) if len(x) > 1: WEAK_CRYPTO = "<tr><td>Binary make use of some Weak Crypto API(s)</td><td><span class='label label-danger'>Insecure</span></td><td>The binary may use the following weak crypto API(s)</br><strong>" + str( x) + "</strong>.</td></tr>" CRYPTO = '' x = re.findall( "CCKeyDerivationPBKDF|CCCryptorCreate|CCCryptorCreateFromData|CCCryptorRelease|CCCryptorUpdate|CCCryptorFinal|CCCryptorGetOutputLength|CCCryptorReset|CCCryptorRef|kCCEncrypt|kCCDecrypt|kCCAlgorithmAES128|kCCKeySizeAES128|kCCKeySizeAES192|kCCKeySizeAES256|kCCAlgorithmCAST|SecCertificateGetTypeID|SecIdentityGetTypeID|SecKeyGetTypeID|SecPolicyGetTypeID|SecTrustGetTypeID|SecCertificateCreateWithData|SecCertificateCreateFromData|SecCertificateCopyData|SecCertificateAddToKeychain|SecCertificateGetData|SecCertificateCopySubjectSummary|SecIdentityCopyCertificate|SecIdentityCopyPrivateKey|SecPKCS12Import|SecKeyGeneratePair|SecKeyEncrypt|SecKeyDecrypt|SecKeyRawSign|SecKeyRawVerify|SecKeyGetBlockSize|SecPolicyCopyProperties|SecPolicyCreateBasicX509|SecPolicyCreateSSL|SecTrustCopyCustomAnchorCertificates|SecTrustCopyExceptions|SecTrustCopyProperties|SecTrustCopyPolicies|SecTrustCopyPublicKey|SecTrustCreateWithCertificates|SecTrustEvaluate|SecTrustEvaluateAsync|SecTrustGetCertificateCount|SecTrustGetCertificateAtIndex|SecTrustGetTrustResult|SecTrustGetVerifyTime|SecTrustSetAnchorCertificates|SecTrustSetAnchorCertificatesOnly|SecTrustSetExceptions|SecTrustSetPolicies|SecTrustSetVerifyDate|SecCertificateRef|SecIdentityRef|SecKeyRef|SecPolicyRef|SecTrustRef", dat) x = list(set(x)) x = ', '.join(x) if len(x) > 1: CRYPTO = "<tr><td>Binary make use of the following Crypto API(s)</td><td><span class='label label-info'>Info</span></td><td>The binary may use the following crypto API(s)</br><strong>" + str( x) + "</strong>.</td></tr>" WEAK_HASH = '' x = re.findall( "CC_MD2_Init|CC_MD2_Update|CC_MD2_Final|CC_MD2|MD2_Init|MD2_Update|MD2_Final|CC_MD4_Init|CC_MD4_Update|CC_MD4_Final|CC_MD4|MD4_Init|MD4_Update|MD4_Final|CC_MD5_Init|CC_MD5_Update|CC_MD5_Final|CC_MD5|MD5_Init|MD5_Update|MD5_Final|MD5Init|MD5Update|MD5Final|CC_SHA1_Init|CC_SHA1_Update|CC_SHA1_Final|CC_SHA1|SHA1_Init|SHA1_Update|SHA1_Final", dat) x = list(set(x)) x = ', '.join(x) if len(x) > 1: WEAK_HASH = "<tr><td>Binary make use of the following Weak HASH API(s)</td><td><span class='label label-danger'>Insecure</span></td><td>The binary may use the following weak hash API(s)</br><strong>" + str( x) + "</strong>.</td></tr>" HASH = '' x = re.findall( "CC_SHA224_Init|CC_SHA224_Update|CC_SHA224_Final|CC_SHA224|SHA224_Init|SHA224_Update|SHA224_Final|CC_SHA256_Init|CC_SHA256_Update|CC_SHA256_Final|CC_SHA256|SHA256_Init|SHA256_Update|SHA256_Final|CC_SHA384_Init|CC_SHA384_Update|CC_SHA384_Final|CC_SHA384|SHA384_Init|SHA384_Update|SHA384_Final|CC_SHA512_Init|CC_SHA512_Update|CC_SHA512_Final|CC_SHA512|SHA512_Init|SHA512_Update|SHA512_Final", dat) x = list(set(x)) x = ', '.join(x) if len(x) > 1: HASH = "<tr><td>Binary make use of the following HASH API(s)</td><td><span class='label label-info'>Info</span></td><td>The binary may use the following hash API(s)</br><strong>" + str( x) + "</strong>.</td></tr>" RAND = '' x = re.findall("srand|random", dat) x = list(set(x)) x = ', '.join(x) if len(x) > 1: RAND = "<tr><td>Binary make use of the insecure Random Function(s)</td><td><span class='label label-danger'>Insecure</span></td><td>The binary may use the following insecure Random Function(s)</br><strong>" + str( x) + "</strong>.</td></tr>" LOG = '' x = re.findall("NSLog", dat) x = list(set(x)) x = ', '.join(x) if len(x) > 1: LOG = "<tr><td>Binary make use of Logging Function</td><td><span class='label label-info'>Info</span></td><td>The binary may use <strong>NSLog</strong> function for logging.</td></tr>" MALL = '' x = re.findall("malloc", dat) x = list(set(x)) x = ', '.join(x) if len(x) > 1: MALL = "<tr><td>Binary make use of <strong>malloc</strong> Function</td><td><span class='label label-danger'>Insecure</span></td><td>The binary may use <strong>malloc</strong> function instead of <strong>calloc</strong>.</td></tr>" DBG = '' x = re.findall("ptrace", dat) x = list(set(x)) x = ', '.join(x) if len(x) > 1: DBG = "<tr><td>Binary calls <strong>ptrace</strong> Function for anti-debugging.</td><td><span class='label label-warning'>warning</span></td><td>The binary may use <strong>ptrace</strong> function. It can be used to detect and prevent debuggers. Ptrace is not a public API and Apps that use non-public APIs will be rejected from AppStore. </td></tr>" CDUMP = '' WVIEW = '' try: print "[INFO] Running class-dump-z against the Binary" if len(settings.CLASSDUMPZ_BINARY) > 0 and isFileExists( settings.CLASSDUMPZ_BINARY): CLASSDUMPZ_BIN = settings.CLASSDUMPZ_BINARY else: CLASSDUMPZ_BIN = os.path.join(TOOLS_DIR, 'class-dump-z') subprocess.call(["chmod", "777", CLASSDUMPZ_BIN]) dat = subprocess.check_output([CLASSDUMPZ_BIN, BIN_PATH]) CDUMP = dat FILE = os.path.join(APP_DIR, "classdump.txt") with open(FILE, "w") as f: f.write(CDUMP) if "UIWebView" in CDUMP: WVIEW = "<tr><td>Binary uses WebView Component.</td><td><span class='label label-info'>Info</span></td><td>The binary may use WebView Component.</td></tr>" except: PrintException("[ERROR] - Cannot perform class dump") BIN_RES = PIE + SSMASH + ARC + BANNED_API + WEAK_CRYPTO + \ CRYPTO + WEAK_HASH + HASH + RAND + LOG + MALL + DBG + WVIEW # classdump # strings print "[INFO] Running strings against the Binary" STRINGS = "" sl = list(strings(BIN_PATH)) sl = set(sl) # Make unique sl = [ s if isinstance(s, unicode) else unicode( s, encoding="utf-8", errors="replace") for s in sl ] sl = [escape(s) for s in sl] # Escape evil strings STRINGS = "</br>".join(sl) return XML, BIN_NAME, ID, VER, SDK, PLTFM, MIN, LIBS, BIN_RES, STRINGS except: PrintException("[ERROR] iOS Binary Analysis")
def BinaryAnalysis(SRC,TOOLS_DIR,APP_DIR): try: print "[INFO] Starting Binary Analysis" dirs = os.listdir(SRC) for d in dirs: if d.endswith(".app"): break BIN_DIR=os.path.join(SRC,d) #Full Dir/Payload/x.app XML_FILE=os.path.join(BIN_DIR,"Info.plist") BIN=d.replace(".app","") BIN_NAME=BIN ID="" VER="" SDK="" PLTFM="" MIN="" XML="" try: print "[INFO] Reading Info.plist" XML=readBinXML(XML_FILE) p=plistlib.readPlistFromString(XML) BIN_NAME = BIN = ID = VER = SDK = PLTFM = MIN = "" if "CFBundleDisplayName" in p: BIN_NAME=p["CFBundleDisplayName"] if "CFBundleExecutable" in p: BIN=p["CFBundleExecutable"] if "CFBundleIdentifier" in p: ID=p["CFBundleIdentifier"] if "CFBundleVersion" in p: VER=p["CFBundleVersion"] if "DTSDKName" in p: SDK=p["DTSDKName"] if "DTPlatformVersion" in p: PLTFM=p["DTPlatformVersion"] if "MinimumOSVersion" in p: MIN=p["MinimumOSVersion"] except: PrintException("[ERROR] - Reading from Info.plist") BIN_PATH=os.path.join(BIN_DIR,BIN) #Full Dir/Payload/x.app/x print "[INFO] iOS Binary : " + BIN print "[INFO] Running otool against the Binary" #Libs Used LIBS='' if len(settings.OTOOL_BINARY) > 0 and isFileExists(OTOOL_BINARY): OTOOL = settings.OTOOL_BINARY else: OTOOL = "otool" args=[OTOOL,'-L',BIN_PATH] dat=subprocess.check_output(args) dat=escape(dat.replace(BIN_DIR + "/","")) LIBS=dat.replace("\n","</br>") #PIE args=[OTOOL,'-hv',BIN_PATH] dat=subprocess.check_output(args) if "PIE" in dat: PIE= "<tr><td><strong>fPIE -pie</strong> flag is Found</td><td><span class='label label-success'>Secure</span></td><td>App is compiled with Position Independent Executable (PIE) flag. This enables Address Space Layout Randomization (ASLR), a memory protection mechanism for exploit mitigation.</td></tr>" else: PIE="<tr><td><strong>fPIE -pie</strong> flag is not Found</td><td><span class='label label-danger'>Insecure</span></td><td>App is not compiled with Position Independent Executable (PIE) flag. So Address Space Layout Randomization (ASLR) is missing. ASLR is a memory protection mechanism for exploit mitigation.</td></tr>" #Stack Smashing Protection & ARC args=[OTOOL,'-Iv',BIN_PATH] dat=subprocess.check_output(args) if "stack_chk_guard" in dat: SSMASH="<tr><td><strong>fstack-protector-all</strong> flag is Found</td><td><span class='label label-success'>Secure</span></td><td>App is compiled with Stack Smashing Protector (SSP) flag and is having protection against Stack Overflows/Stack Smashing Attacks.</td></tr>" else: SSMASH= "<tr><td><strong>fstack-protector-all</strong> flag is not Found</td><td><span class='label label-danger'>Insecure</span></td><td>App is not compiled with Stack Smashing Protector (SSP) flag. It is vulnerable to Stack Overflows/Stack Smashing Attacks.</td></tr>" #ARC if "_objc_release" in dat: ARC="<tr><td><strong>fobjc-arc</strong> flag is Found</td><td><span class='label label-success'>Secure</span></td><td>App is compiled with Automatic Reference Counting (ARC) flag. ARC is a compiler feature that provides automatic memory management of Objective-C objects and is an exploit mitigation mechanism against memory corruption vulnerabilities.</td></tr>" else: ARC="<tr><td><strong>fobjc-arc</strong> flag is not Found</td><td><span class='label label-danger'>Insecure</span></td><td>App is not compiled with Automatic Reference Counting (ARC) flag. ARC is a compiler feature that provides automatic memory management of Objective-C objects and protects from memory corruption vulnerabilities.</td></tr>" ########## BANNED_API='' x=re.findall("alloca|gets|memcpy|printf|scanf|sprintf|sscanf|strcat|StrCat|strcpy|StrCpy|strlen|StrLen|strncat|StrNCat|strncpy|StrNCpy|strtok|swprintf|vsnprintf|vsprintf|vswprintf|wcscat|wcscpy|wcslen|wcsncat|wcsncpy|wcstok|wmemcpy",dat) x=list(set(x)) x=', '.join(x) if len(x)>1: BANNED_API="<tr><td>Binary make use of banned API(s)</td><td><span class='label label-danger'>Insecure</span></td><td>The binary may contain the following banned API(s) </br><strong>" + str(x) + "</strong>.</td></tr>" WEAK_CRYPTO='' x=re.findall("kCCAlgorithmDES|kCCAlgorithm3DES||kCCAlgorithmRC2|kCCAlgorithmRC4|kCCOptionECBMode|kCCOptionCBCMode",dat) x=list(set(x)) x=', '.join(x) if len(x)>1: WEAK_CRYPTO="<tr><td>Binary make use of some Weak Crypto API(s)</td><td><span class='label label-danger'>Insecure</span></td><td>The binary may use the following weak crypto API(s)</br><strong>" + str(x) + "</strong>.</td></tr>" CRYPTO='' x=re.findall("CCKeyDerivationPBKDF|CCCryptorCreate|CCCryptorCreateFromData|CCCryptorRelease|CCCryptorUpdate|CCCryptorFinal|CCCryptorGetOutputLength|CCCryptorReset|CCCryptorRef|kCCEncrypt|kCCDecrypt|kCCAlgorithmAES128|kCCKeySizeAES128|kCCKeySizeAES192|kCCKeySizeAES256|kCCAlgorithmCAST|SecCertificateGetTypeID|SecIdentityGetTypeID|SecKeyGetTypeID|SecPolicyGetTypeID|SecTrustGetTypeID|SecCertificateCreateWithData|SecCertificateCreateFromData|SecCertificateCopyData|SecCertificateAddToKeychain|SecCertificateGetData|SecCertificateCopySubjectSummary|SecIdentityCopyCertificate|SecIdentityCopyPrivateKey|SecPKCS12Import|SecKeyGeneratePair|SecKeyEncrypt|SecKeyDecrypt|SecKeyRawSign|SecKeyRawVerify|SecKeyGetBlockSize|SecPolicyCopyProperties|SecPolicyCreateBasicX509|SecPolicyCreateSSL|SecTrustCopyCustomAnchorCertificates|SecTrustCopyExceptions|SecTrustCopyProperties|SecTrustCopyPolicies|SecTrustCopyPublicKey|SecTrustCreateWithCertificates|SecTrustEvaluate|SecTrustEvaluateAsync|SecTrustGetCertificateCount|SecTrustGetCertificateAtIndex|SecTrustGetTrustResult|SecTrustGetVerifyTime|SecTrustSetAnchorCertificates|SecTrustSetAnchorCertificatesOnly|SecTrustSetExceptions|SecTrustSetPolicies|SecTrustSetVerifyDate|SecCertificateRef|SecIdentityRef|SecKeyRef|SecPolicyRef|SecTrustRef",dat) x=list(set(x)) x=', '.join(x) if len(x)>1: CRYPTO="<tr><td>Binary make use of the following Crypto API(s)</td><td><span class='label label-info'>Info</span></td><td>The binary may use the following crypto API(s)</br><strong>" + str(x) + "</strong>.</td></tr>" WEAK_HASH='' x=re.findall("CC_MD2_Init|CC_MD2_Update|CC_MD2_Final|CC_MD2|MD2_Init|MD2_Update|MD2_Final|CC_MD4_Init|CC_MD4_Update|CC_MD4_Final|CC_MD4|MD4_Init|MD4_Update|MD4_Final|CC_MD5_Init|CC_MD5_Update|CC_MD5_Final|CC_MD5|MD5_Init|MD5_Update|MD5_Final|MD5Init|MD5Update|MD5Final|CC_SHA1_Init|CC_SHA1_Update|CC_SHA1_Final|CC_SHA1|SHA1_Init|SHA1_Update|SHA1_Final",dat) x=list(set(x)) x=', '.join(x) if len(x)>1: WEAK_HASH="<tr><td>Binary make use of the following Weak HASH API(s)</td><td><span class='label label-danger'>Insecure</span></td><td>The binary may use the following weak hash API(s)</br><strong>" + str(x) + "</strong>.</td></tr>" HASH='' x=re.findall("CC_SHA224_Init|CC_SHA224_Update|CC_SHA224_Final|CC_SHA224|SHA224_Init|SHA224_Update|SHA224_Final|CC_SHA256_Init|CC_SHA256_Update|CC_SHA256_Final|CC_SHA256|SHA256_Init|SHA256_Update|SHA256_Final|CC_SHA384_Init|CC_SHA384_Update|CC_SHA384_Final|CC_SHA384|SHA384_Init|SHA384_Update|SHA384_Final|CC_SHA512_Init|CC_SHA512_Update|CC_SHA512_Final|CC_SHA512|SHA512_Init|SHA512_Update|SHA512_Final",dat) x=list(set(x)) x=', '.join(x) if len(x)>1: HASH="<tr><td>Binary make use of the following HASH API(s)</td><td><span class='label label-info'>Info</span></td><td>The binary may use the following hash API(s)</br><strong>" + str(x) + "</strong>.</td></tr>" RAND='' x=re.findall("srand|random",dat) x=list(set(x)) x=', '.join(x) if len(x)>1: RAND="<tr><td>Binary make use of the insecure Random Function(s)</td><td><span class='label label-danger'>Insecure</span></td><td>The binary may use the following insecure Random Function(s)</br><strong>" + str(x) + "</strong>.</td></tr>" LOG='' x=re.findall("NSLog",dat) x=list(set(x)) x=', '.join(x) if len(x)>1: LOG="<tr><td>Binary make use of Logging Function</td><td><span class='label label-info'>Info</span></td><td>The binary may use <strong>NSLog</strong> function for logging.</td></tr>" MALL='' x=re.findall("malloc",dat) x=list(set(x)) x=', '.join(x) if len(x)>1: MALL="<tr><td>Binary make use of <strong>malloc</strong> Function</td><td><span class='label label-danger'>Insecure</span></td><td>The binary may use <strong>malloc</strong> function instead of <strong>calloc</strong>.</td></tr>" DBG='' x=re.findall("ptrace",dat) x=list(set(x)) x=', '.join(x) if len(x)>1: DBG="<tr><td>Binary calls <strong>ptrace</strong> Function for anti-debugging.</td><td><span class='label label-warning'>warning</span></td><td>The binary may use <strong>ptrace</strong> function. It can be used to detect and prevent debuggers. Ptrace is not a public API and Apps that use non-public APIs will be rejected from AppStore. </td></tr>" CDUMP='' WVIEW='' try: print "[INFO] Running class-dump-z against the Binary" if len(settings.CLASSDUMPZ_BINARY) > 0 and isFileExists(settings.CLASSDUMPZ_BINARY): CLASSDUMPZ_BIN = settings.CLASSDUMPZ_BINARY else: CLASSDUMPZ_BIN = os.path.join(TOOLS_DIR,'class-dump-z') subprocess.call(["chmod", "777", CLASSDUMPZ_BIN]) dat=subprocess.check_output([CLASSDUMPZ_BIN,BIN_PATH]) CDUMP=dat FILE=os.path.join(APP_DIR,"classdump.txt") with open(FILE,"w") as f: f.write(CDUMP) if "UIWebView" in CDUMP: WVIEW="<tr><td>Binary uses WebView Component.</td><td><span class='label label-info'>Info</span></td><td>The binary may use WebView Component.</td></tr>" except: PrintException("[ERROR] - Cannot perform class dump") BIN_RES=PIE+SSMASH+ARC+BANNED_API+WEAK_CRYPTO+CRYPTO+WEAK_HASH+HASH+RAND+LOG+MALL+DBG+WVIEW #classdump # strings args=["strings", BIN_PATH] strings=subprocess.check_output(args) strings=escape(strings.replace(BIN_DIR + "/","")) STRINGS=strings.replace("\n","</br>") return XML,BIN_NAME,ID,VER,SDK,PLTFM,MIN,LIBS,BIN_RES,STRINGS except: PrintException("[ERROR] iOS Binary Analysis")
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 view_file(request): """View iOS Files""" try: print "[INFO] View iOS Files" fil = request.GET['file'] typ = request.GET['type'] md5_hash = request.GET['md5'] mode = request.GET['mode'] md5_match = re.match('^[0-9a-f]{32}$', md5_hash) ext = fil.split('.')[-1] ext_type = re.search("plist|db|sqlitedb|sqlite|txt|m", ext) if (md5_match and ext_type and re.findall('xml|db|txt|m', typ) and re.findall('ios|ipa', mode) ): if (("../" in fil) or ("%2e%2e" in fil) or (".." in fil) or ("%252e" in fil) ): return HttpResponseRedirect('/error/') else: 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 not Found" else: return HttpResponseRedirect('/error/') context = {'title': escape(ntpath.basename(fil)), 'file': escape(ntpath.basename(fil)), 'type': file_format, 'dat': dat} template = "general/view.html" return render(request, template, context) except: PrintException("[ERROR] View iOS Files") return HttpResponseRedirect('/error/')
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 plist_analysis(src, is_source): """Plist Analysis""" try: logger.info("iOS Info.plist Analysis Started") plist_info = { "bin_name": "", "bin": "", "id": "", "version": "", "build": "", "sdk": "", "pltfm": "", "min": "", "plist_xml": "", "permissions": [], "inseccon": [], "bundle_name": "", "build_version_name": "", "bundle_url_types": [], "bundle_supported_platforms": [], "bundle_localizations": [] } plist_file = None if is_source: logger.info("Finding Info.plist in iOS Source") for ifile in os.listdir(src): if ifile.endswith(".xcodeproj"): app_name = ifile.replace(".xcodeproj", "") break app_plist_file = "Info.plist" for dirpath, dirnames, files in os.walk(src): for name in files: if "__MACOSX" not in dirpath and name == app_plist_file: plist_file = os.path.join(dirpath, name) break else: logger.info("Finding Info.plist in iOS Binary") dirs = os.listdir(src) dot_app_dir = "" for dir_ in dirs: if dir_.endswith(".app"): dot_app_dir = dir_ break bin_dir = os.path.join(src, dot_app_dir) # Full Dir/Payload/x.app plist_file = os.path.join(bin_dir, "Info.plist") if not isFileExists(plist_file): logger.warning( "Cannot find Info.plist file. Skipping Plist Analysis.") else: #Generic Plist Analysis plist_obj = plistlib.readPlist(plist_file) plist_info["plist_xml"] = plistlib.writePlistToBytes( plist_obj).decode("utf-8", "ignore") if "CFBundleDisplayName" in plist_obj: plist_info["bin_name"] = plist_obj["CFBundleDisplayName"] else: if not is_source: #For iOS IPA plist_info["bin_name"] = dot_app_dir.replace(".app", "") if "CFBundleExecutable" in plist_obj: plist_info["bin"] = plist_obj["CFBundleExecutable"] if "CFBundleIdentifier" in plist_obj: plist_info["id"] = plist_obj["CFBundleIdentifier"] # build if "CFBundleVersion" in plist_obj: plist_info["build"] = plist_obj["CFBundleVersion"] if "DTSDKName" in plist_obj: plist_info["sdk"] = plist_obj["DTSDKName"] if "DTPlatformVersion" in plist_obj: plist_info["pltfm"] = plist_obj["DTPlatformVersion"] if "MinimumOSVersion" in plist_obj: plist_info["min"] = plist_obj["MinimumOSVersion"] plist_info["bundle_name"] = plist_obj.get("CFBundleName", "") plist_info["bundle_version_name"] = plist_obj.get( "CFBundleShortVersionString", "") plist_info["bundle_url_types"] = plist_obj.get( "CFBundleURLTypes", []) plist_info["bundle_supported_platforms"] = plist_obj.get( "CFBundleSupportedPlatforms", []) plist_info["bundle_localizations"] = plist_obj.get( "CFBundleLocalizations", []) # Check possible app-permissions plist_info["permissions"] = __check_permissions(plist_obj) plist_info["inseccon"] = __check_insecure_connections(plist_obj) return plist_info except: PrintException("Reading from Info.plist")
def plist_analysis(src, is_source): """Plist Analysis""" try: logger.info("iOS Info.plist Analysis Started") plist_info = { "bin_name": "", "bin": "", "id": "", "version": "", "build": "", "sdk": "", "pltfm": "", "min": "", "plist_xml": "", "permissions": [], "inseccon": [], "bundle_name": "", "build_version_name": "", "bundle_url_types": [], "bundle_supported_platforms": [], "bundle_localizations": [] } plist_file = None if is_source: logger.info("Finding Info.plist in iOS Source") for ifile in os.listdir(src): if ifile.endswith(".xcodeproj"): app_name = ifile.replace(".xcodeproj", "") break app_plist_file = "Info.plist" for dirpath, dirnames, files in os.walk(src): for name in files: if "__MACOSX" not in dirpath and name == app_plist_file: plist_file = os.path.join(dirpath, name) break else: logger.info("Finding Info.plist in iOS Binary") dirs = os.listdir(src) dot_app_dir = "" for dir_ in dirs: if dir_.endswith(".app"): dot_app_dir = dir_ break bin_dir = os.path.join(src, dot_app_dir) # Full Dir/Payload/x.app plist_file = os.path.join(bin_dir, "Info.plist") if not isFileExists(plist_file): logger.warning("Cannot find Info.plist file. Skipping Plist Analysis.") else: #Generic Plist Analysis plist_obj = plistlib.readPlist(plist_file) plist_info["plist_xml"] = plistlib.writePlistToBytes(plist_obj).decode("utf-8", "ignore") if "CFBundleDisplayName" in plist_obj: plist_info["bin_name"] = plist_obj["CFBundleDisplayName"] else: if not is_source: #For iOS IPA plist_info["bin_name"] = dot_app_dir.replace(".app", "") if "CFBundleExecutable" in plist_obj: plist_info["bin"] = plist_obj["CFBundleExecutable"] if "CFBundleIdentifier" in plist_obj: plist_info["id"] = plist_obj["CFBundleIdentifier"] # build if "CFBundleVersion" in plist_obj: plist_info["build"] = plist_obj["CFBundleVersion"] if "DTSDKName" in plist_obj: plist_info["sdk"] = plist_obj["DTSDKName"] if "DTPlatformVersion" in plist_obj: plist_info["pltfm"] = plist_obj["DTPlatformVersion"] if "MinimumOSVersion" in plist_obj: plist_info["min"] = plist_obj["MinimumOSVersion"] plist_info["bundle_name"] = plist_obj.get("CFBundleName", "") plist_info["bundle_version_name"] = plist_obj.get("CFBundleShortVersionString", "") plist_info["bundle_url_types"] = plist_obj.get("CFBundleURLTypes", []) plist_info["bundle_supported_platforms"] = plist_obj.get("CFBundleSupportedPlatforms", []) plist_info["bundle_localizations"] = plist_obj.get("CFBundleLocalizations", []) # Check possible app-permissions plist_info["permissions"] = __check_permissions(plist_obj) plist_info["inseccon"] = __check_insecure_connections(plist_obj) return plist_info except: PrintException("Reading from Info.plist")
def view_file(request): """View iOS Files""" try: print("[INFO] View iOS Files") fil = request.GET['file'] typ = request.GET['type'] md5_hash = request.GET['md5'] mode = request.GET['mode'] md5_match = re.match('^[0-9a-f]{32}$', md5_hash) ext = fil.split('.')[-1] ext_type = re.search("plist|db|sqlitedb|sqlite|txt|m", ext) if (md5_match and ext_type and re.findall('xml|db|txt|m', typ) and re.findall('ios|ipa', mode)): if (("../" in fil) or ("%2e%2e" in fil) or (".." in fil) or ("%252e" in fil)): return HttpResponseRedirect('/error/') else: 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 not Found" else: return HttpResponseRedirect('/error/') context = { 'title': escape(ntpath.basename(fil)), 'file': escape(ntpath.basename(fil)), 'type': file_format, 'dat': dat } template = "general/view.html" return render(request, template, context) except: PrintException("[ERROR] View iOS Files") return HttpResponseRedirect('/error/')
def iOS_Source_Analysis(SRC, MD5): try: print "[INFO] Starting iOS Source Code and PLIST Analysis" ALLURLSLST = list() DOMAINS = dict() APP = '' InfoP = '' BIN_NAME = '' BIN = '' ID = '' VER = '' SDK = '' PLTFM = '' MIN = '' XML = None for f in os.listdir(SRC): if f.endswith(".xcodeproj"): APP = f.replace(".xcodeproj", "") PlistFile = APP + "-Info.plist" for dirName, subDir, files in os.walk(SRC): for jfile in files: if PlistFile in jfile: InfoP = os.path.join(SRC, dirName, jfile) break if isFileExists(InfoP): with io.open(InfoP, mode='r', encoding="utf8", errors="ignore") as f: XML = f.read() if XML: p = plistlib.readPlistFromString(XML) BIN_NAME = p["CFBundleDisplayName"] BIN = p["CFBundleExecutable"] ID = p["CFBundleIdentifier"] VER = p["CFBundleVersion"] SDK = '' # p["DTSDKName"] PLTFM = '' # p["DTPlatformVersion"] MIN = '' # p["MinimumOSVersion"] # Code Analysis EmailnFile = '' URLnFile = '' c = {key: [] for key in ('i_buf', 'webv', 'i_log', 'net', 'i_sqlite', 'fileio', 'ssl_bypass', 'ssl_uiwebview', 'path_traversal')} for dirName, subDir, files in os.walk(SRC): for jfile in files: if jfile.endswith(".m"): jfile_path = os.path.join(SRC, dirName, jfile) if "+" in jfile: p2 = os.path.join( SRC, dirName, jfile.replace("+", "x")) shutil.move(jfile_path, p2) jfile_path = p2 repath = dirName.replace(SRC, '') dat = '' with io.open(jfile_path, mode='r', encoding="utf8", errors="ignore") as f: dat = f.read() URLS = [] EMAILS = [] # API if (re.findall("NSURL|CFStream|NSStream", dat)): c['net'].append(jfile_path.replace(SRC, '')) if (re.findall("Keychain|kSecAttrAccessibleWhenUnlocked|kSecAttrAccessibleAfterFirstUnlock|SecItemAdd|SecItemUpdate|NSDataWritingFileProtectionComplete", dat)): c['fileio'].append(jfile_path.replace(SRC, '')) if (re.findall("WebView|UIWebView", dat)): c['webv'].append(jfile_path.replace(SRC, '')) # SECURITY ANALYSIS if (re.findall("strcpy|memcpy|strcat|strncat|strncpy|sprintf|vsprintf|gets", dat)): c['i_buf'].append(jfile_path.replace(SRC, '')) if (re.findall("NSLog", dat)): c['i_log'].append(jfile_path.replace(SRC, '')) if (re.findall("sqlite3_exec", dat)): c['i_sqlite'].append(jfile_path.replace(SRC, '')) if re.findall('canAuthenticateAgainstProtectionSpace|continueWithoutCredentialForAuthenticationChallenge|kCFStreamSSLAllowsExpiredCertificates|kCFStreamSSLAllowsAnyRoot|kCFStreamSSLAllowsExpiredRoots|allowInvalidCertificates\s*=\s*(YES|yes)', dat): c['ssl_bypass'].append(jfile_path.replace(SRC, '')) if re.findall('setAllowsAnyHTTPSCertificate:\s*YES|allowsAnyHTTPSCertificateForHost|loadingUnvalidatedHTTPSPage\s*=\s*(YES|yes)', dat): c['ssl_uiwebview'].append(jfile_path.replace(SRC, '')) if "NSTemporaryDirectory()," in dat: c['path_traversal'].append(jfile_path.replace(SRC, '')) fl = jfile_path.replace(SRC, '') base_fl = ntpath.basename(fl) # URLs My Custom regex p = re.compile(ur'((?:https?://|s?ftps?://|file://|javascript:|data:|www\d{0,3}[.])[\w().=/;,#:@?&~*+!$%\'{}-]+)', re.UNICODE) urllist = re.findall(p, dat.lower()) ALLURLSLST.extend(urllist) uflag = 0 for url in urllist: if url not in URLS: URLS.append(url) uflag = 1 if uflag == 1: URLnFile += "<tr><td>" + "<br>".join(URLS) + "</td><td><a href='../ViewFile/?file=" + escape( fl) + "&type=m&mode=ios&md5=" + MD5 + "'>" + escape(base_fl) + "</a></td></tr>" # Email Etraction Regex regex = re.compile("[\w.-]+@[\w-]+\.[\w.]+") eflag = 0 for email in regex.findall(dat.lower()): if ((email not in EMAILS) and (not email.startswith('//'))): EMAILS.append(email) eflag = 1 if eflag == 1: EmailnFile += "<tr><td>" + "<br>".join(EMAILS) + "</td><td><a href='../ViewFile/?file=" + escape( fl) + "&type=m&mode=ios&md5=" + MD5 + "'>" + escape(base_fl) + "</a></td></tr>" # Domain Extraction and Malware Check print "[INFO] Performing Malware Check on extracted Domains" DOMAINS = MalwareCheck(ALLURLSLST) print "[INFO] Finished Code Analysis, Email and URL Extraction" dc = {'webv': 'WebView Component', 'net': 'Network Calls', 'fileio': 'Local File I/O Operations.', } html = '' for ky in dc: if c[ky]: link = '' hd = "<tr><td>" + dc[ky] + "</td><td>" for l in c[ky]: link += "<a href='../ViewFile/?file=" + \ escape(l) + "&type=m&mode=ios&md5=" + MD5 + \ "'>" + escape(ntpath.basename(l)) + "</a> " html += hd + link + "</td></tr>" dg = {'i_buf': 'The App may contain banned API(s). These API(s) are insecure and must not be used.', 'i_log': 'The App logs information. Sensitive information should never be logged.', 'i_sqlite': 'App uses SQLite Database. Sensitive Information should be encrypted.', 'ssl_bypass': '******', 'ssl_uiwebview': 'UIWebView in App ignore SSL errors and accept any SSL Certificate. App is vulnerable to MITM attacks.', 'path_traversal': 'Untrusted user input to "NSTemporaryDirectory()"" will result in path traversal vulnerability.', } dang = '' spn_dang = '<span class="label label-danger">high</span>' spn_info = '<span class="label label-info">info</span>' spn_sec = '<span class="label label-success">secure</span>' spn_warn = '<span class="label label-warning">warning</span>' for k in dg: if c[k]: link = '' if (re.findall('i_sqlite', k)): hd = '<tr><td>' + dg[k] + \ '</td><td>' + spn_info + '</td><td>' elif (re.findall('path_traversal', k)): hd = '<tr><td>' + dg[k] + \ '</td><td>' + spn_warn + '</td><td>' else: hd = '<tr><td>' + dg[k] + \ '</td><td>' + spn_dang + '</td><td>' for ll in c[k]: link += "<a href='../ViewFile/?file=" + \ escape(ll) + "&type=m&mode=ios&md5=" + MD5 + \ "'>" + escape(ntpath.basename(ll)) + "</a> " dang += hd + link + "</td></tr>" return html, dang, URLnFile, DOMAINS, EmailnFile, XML, BIN_NAME, ID, VER, SDK, PLTFM, MIN except: PrintException("[ERROR] iOS Source Code Analysis")
def plist_analysis(src, is_source): """Plist Analysis""" try: print("[INFO] iOS Info.plist Analysis Started") plist_info = {} plist_info["bin_name"] = "" plist_info["bin"] = "" plist_info["id"] = "" plist_info["ver"] = "" plist_info["sdk"] = "" plist_info["pltfm"] = "" plist_info["min"] = "" plist_info["plist_xml"] = "" plist_info["permissions"] = [] plist_info["inseccon"] = [] if is_source: print("[INFO] Finding Info.plist in iOS Source") for ifile in os.listdir(src): if ifile.endswith(".xcodeproj"): app_name = ifile.replace(".xcodeproj", "") break app_plist_file = "Info.plist" for dirpath, dirnames, files in os.walk(src): for name in files: if "__MACOSX" not in dirpath and name == app_plist_file: plist_file = os.path.join(dirpath, name) break else: print("[INFO] Finding Info.plist in iOS Binary") dirs = os.listdir(src) dot_app_dir = "" for dir_ in dirs: if dir_.endswith(".app"): dot_app_dir = dir_ break bin_dir = os.path.join(src, dot_app_dir) # Full Dir/Payload/x.app plist_file = os.path.join(bin_dir, "Info.plist") if not isFileExists(plist_file): print( "[WARNING] Cannot find Info.plist file. Skipping Plist Analysis." ) else: #Generic Plist Analysis plist_obj = plistlib.readPlist(plist_file) plist_info["plist_xml"] = plistlib.writePlistToBytes( plist_obj).decode("utf-8", "ignore") if "CFBundleDisplayName" in plist_obj: plist_info["bin_name"] = plist_obj["CFBundleDisplayName"] else: if not is_source: #For iOS IPA plist_info["bin_name"] = dot_app_dir.replace(".app", "") if "CFBundleExecutable" in plist_obj: plist_info["bin"] = plist_obj["CFBundleExecutable"] if "CFBundleIdentifier" in plist_obj: plist_info["id"] = plist_obj["CFBundleIdentifier"] if "CFBundleVersion" in plist_obj: plist_info["ver"] = plist_obj["CFBundleVersion"] if "DTSDKName" in plist_obj: plist_info["sdk"] = plist_obj["DTSDKName"] if "DTPlatformVersion" in plist_obj: plist_info["pltfm"] = plist_obj["DTPlatformVersion"] if "MinimumOSVersion" in plist_obj: plist_info["min"] = plist_obj["MinimumOSVersion"] # Check possible app-permissions plist_info["permissions"] = __check_permissions(plist_obj) plist_info["inseccon"] = __check_insecure_connections(plist_obj) return plist_info except: PrintException("[ERROR] - Reading from Info.plist")
def otool_analysis(bin_name, bin_path, bin_dir): """OTOOL Analysis of Binary""" try: print "[INFO] Starting Otool Analysis" otool_dict = {} otool_dict["libs"] = '' otool_dict["anal"] = '' print "[INFO] Running otool against Binary : " + bin_name if len(settings.OTOOL_BINARY) > 0 and isFileExists(settings.OTOOL_BINARY): otool_bin = settings.OTOOL_BINARY else: otool_bin = "otool" args = [otool_bin, '-L', bin_path] libs = unicode(subprocess.check_output(args), 'utf-8') libs = smart_text(escape(libs.replace(bin_dir + "/", ""))) otool_dict["libs"] = libs.replace("\n", "</br>") # PIE args = [otool_bin, '-hv', bin_path] pie_dat = subprocess.check_output(args) if "PIE" in pie_dat: pie_flag = "<tr><td><strong>fPIE -pie</strong> flag is Found</td><td>" + \ "<span class='label label-success'>Secure</span>" + \ "</td><td>App is compiled with Position Independent Executable (PIE) flag. " + \ "This enables Address Space Layout Randomization (ASLR), a memory protection" +\ " mechanism for exploit mitigation.</td></tr>" else: pie_flag = "<tr><td><strong>fPIE -pie</strong> flag is not Found</td><td>" +\ "<span class='label label-danger'>Insecure</span></td><td>App is not compiled" +\ " with Position Independent Executable (PIE) flag. So Address Space Layout " +\ "Randomization (ASLR) is missing. ASLR is a memory protection mechanism for" +\ " exploit mitigation.</td></tr>" # Stack Smashing Protection & ARC args = [otool_bin, '-Iv', bin_path] dat = subprocess.check_output(args) if "stack_chk_guard" in dat: ssmash = "<tr><td><strong>fstack-protector-all</strong> flag is Found</td><td>" +\ "<span class='label label-success'>Secure</span></td><td>App is compiled with" +\ " Stack Smashing Protector (SSP) flag and is having protection against Stack" +\ " Overflows/Stack Smashing Attacks.</td></tr>" else: ssmash = "<tr><td><strong>fstack-protector-all</strong> flag is not Found</td><td>" +\ "<span class='label label-danger'>Insecure</span></td><td>App is " +\ "not compiled with Stack Smashing Protector (SSP) flag. It is vulnerable to " +\ "Stack Overflows/Stack Smashing Attacks.</td></tr>" # ARC if "_objc_release" in dat: arc_flag = "<tr><td><strong>fobjc-arc</strong> flag is Found</td><td>" +\ "<span class='label label-success'>Secure</span></td><td>App is compiled " +\ "with Automatic Reference Counting (ARC) flag. ARC is a compiler feature " +\ "that provides automatic memory management of Objective-C objects and is an" +\ " exploit mitigation mechanism against memory corruption vulnerabilities.</td></tr>" else: arc_flag = "<tr><td><strong>fobjc-arc</strong> flag is not Found</td><td>" +\ "<span class='label label-danger'>Insecure</span></td><td>App is not compiled" +\ " with Automatic Reference Counting (ARC) flag. ARC is a compiler feature that" +\ " provides automatic memory management of Objective-C objects and protects from" +\ " memory corruption vulnerabilities.</td></tr>" banned_apis = '' baned = re.findall( "alloca|gets|memcpy|printf|scanf|sprintf|sscanf|strcat|StrCat|strcpy|" + "StrCpy|strlen|StrLen|strncat|StrNCat|strncpy|StrNCpy|strtok|swprintf|vsnprintf|" + "vsprintf|vswprintf|wcscat|wcscpy|wcslen|wcsncat|wcsncpy|wcstok|wmemcpy", dat) baned = list(set(baned)) baned_s = ', '.join(baned) if len(baned_s) > 1: banned_apis = "<tr><td>Binary make use of banned API(s)</td><td>" +\ "<span class='label label-danger'>Insecure</span></td><td>The binary " +\ "may contain the following banned API(s) </br><strong>" + \ str(baned_s) + "</strong>.</td></tr>" weak_cryptos = '' weak_algo = re.findall( "kCCAlgorithmDES|kCCAlgorithm3DES||kCCAlgorithmRC2|kCCAlgorithmRC4|" + "kCCOptionECBMode|kCCOptionCBCMode", dat) weak_algo = list(set(weak_algo)) weak_algo_s = ', '.join(weak_algo) if len(weak_algo_s) > 1: weak_cryptos = "<tr><td>Binary make use of some Weak Crypto API(s)</td><td>" +\ "<span class='label label-danger'>Insecure</span></td><td>The binary may use " +\ "the following weak crypto API(s)</br><strong>" + \ str(weak_algo_s) + "</strong>.</td></tr>" crypto = '' crypto_algo = re.findall( "CCKeyDerivationPBKDF|CCCryptorCreate|CCCryptorCreateFromData|" + "CCCryptorRelease|CCCryptorUpdate|CCCryptorFinal|CCCryptorGetOutputLength|" + "CCCryptorReset|CCCryptorRef|kCCEncrypt|kCCDecrypt|kCCAlgorithmAES128|" + "kCCKeySizeAES128|kCCKeySizeAES192|kCCKeySizeAES256|kCCAlgorithmCAST|" + "SecCertificateGetTypeID|SecIdentityGetTypeID|SecKeyGetTypeID|SecPolicyGetTypeID|" + "SecTrustGetTypeID|SecCertificateCreateWithData|SecCertificateCreateFromData|" + "SecCertificateCopyData|SecCertificateAddToKeychain|SecCertificateGetData|" + "SecCertificateCopySubjectSummary|SecIdentityCopyCertificate|" + "SecIdentityCopyPrivateKey|SecPKCS12Import|SecKeyGeneratePair|SecKeyEncrypt|" + "SecKeyDecrypt|SecKeyRawSign|SecKeyRawVerify|SecKeyGetBlockSize|" + "SecPolicyCopyProperties|SecPolicyCreateBasicX509|SecPolicyCreateSSL|" + "SecTrustCopyCustomAnchorCertificates|SecTrustCopyExceptions|" + "SecTrustCopyProperties|SecTrustCopyPolicies|SecTrustCopyPublicKey|" + "SecTrustCreateWithCertificates|SecTrustEvaluate|SecTrustEvaluateAsync|" + "SecTrustGetCertificateCount|SecTrustGetCertificateAtIndex|SecTrustGetTrustResult|" + "SecTrustGetVerifyTime|SecTrustSetAnchorCertificates|" + "SecTrustSetAnchorCertificatesOnly|SecTrustSetExceptions|SecTrustSetPolicies|" + "SecTrustSetVerifyDate|SecCertificateRef|" + "SecIdentityRef|SecKeyRef|SecPolicyRef|SecTrustRef", dat) crypto_algo = list(set(crypto_algo)) crypto_algo_s = ', '.join(crypto_algo) if len(crypto_algo_s) > 1: crypto = "<tr><td>Binary make use of the following Crypto API(s)</td><td>" +\ "<span class='label label-info'>Info</span></td><td>The binary may use the" +\ " following crypto API(s)</br><strong>" + \ str(crypto_algo_s) + "</strong>.</td></tr>" weak_hashes = '' weak_hash_algo = re.findall( "CC_MD2_Init|CC_MD2_Update|CC_MD2_Final|CC_MD2|MD2_Init|" + "MD2_Update|MD2_Final|CC_MD4_Init|CC_MD4_Update|CC_MD4_Final|CC_MD4|MD4_Init|" + "MD4_Update|MD4_Final|CC_MD5_Init|CC_MD5_Update|CC_MD5_Final|CC_MD5|MD5_Init|" + "MD5_Update|MD5_Final|MD5Init|MD5Update|MD5Final|CC_SHA1_Init|CC_SHA1_Update|" + "CC_SHA1_Final|CC_SHA1|SHA1_Init|SHA1_Update|SHA1_Final", dat) weak_hash_algo = list(set(weak_hash_algo)) weak_hash_algo_s = ', '.join(weak_hash_algo) if len(weak_hash_algo_s) > 1: weak_hashes = "<tr><td>Binary make use of the following Weak HASH API(s)</td><td>" +\ "<span class='label label-danger'>Insecure</span></td><td>The binary " +\ "may use the following weak hash API(s)</br><strong>" + \ str(weak_hash_algo_s) + "</strong>.</td></tr>" hashes = '' hash_algo = re.findall( "CC_SHA224_Init|CC_SHA224_Update|CC_SHA224_Final|CC_SHA224|" + "SHA224_Init|SHA224_Update|SHA224_Final|CC_SHA256_Init|CC_SHA256_Update|" + "CC_SHA256_Final|CC_SHA256|SHA256_Init|SHA256_Update|SHA256_Final|" + "CC_SHA384_Init|CC_SHA384_Update|CC_SHA384_Final|CC_SHA384|SHA384_Init|" + "SHA384_Update|SHA384_Final|CC_SHA512_Init|CC_SHA512_Update|CC_SHA512_Final|" + "CC_SHA512|SHA512_Init|SHA512_Update|SHA512_Final", dat) hash_algo = list(set(hash_algo)) hash_algo_s = ', '.join(hash_algo) if len(hash_algo_s) > 1: hashes = "<tr><td>Binary make use of the following HASH API(s)</td><td>" +\ "<span class='label label-info'>Info</span></td><td>The binary may use the" +\ " following hash API(s)</br><strong>" + \ str(hash_algo_s) + "</strong>.</td></tr>" randoms = '' rand_algo = re.findall("srand|random", dat) rand_algo = list(set(rand_algo)) rand_algo_s = ', '.join(rand_algo) if len(rand_algo_s) > 1: randoms = "<tr><td>Binary make use of the insecure Random Function(s)</td><td>" +\ "<span class='label label-danger'>Insecure</span></td><td>The binary may " +\ "use the following insecure Random Function(s)</br><strong>" + \ str(rand_algo_s) + "</strong>.</td></tr>" logging = '' log = re.findall("NSLog", dat) log = list(set(log)) log_s = ', '.join(log) if len(log_s) > 1: logging = "<tr><td>Binary make use of Logging Function</td><td>" +\ "<span class='label label-info'>Info</span></td><td>The binary may " +\ "use <strong>NSLog</strong> function for logging.</td></tr>" malloc = '' mal = re.findall("malloc", dat) mal = list(set(mal)) mal_s = ', '.join(mal) if len(mal_s) > 1: malloc = "<tr><td>Binary make use of <strong>malloc</strong> Function</td><td>" +\ "<span class='label label-danger'>Insecure</span></td><td>The binary may use " +\ "<strong>malloc</strong> function instead of <strong>calloc</strong>.</td></tr>" debug = '' ptrace = re.findall("ptrace", dat) ptrace = list(set(ptrace)) ptrace_s = ', '.join(ptrace) if len(ptrace_s) > 1: debug = "<tr><td>Binary calls <strong>ptrace</strong> Function for anti-debugging." +\ "</td><td><span class='label label-warning'>warning</span></td><td>The binary" +\ " may use <strong>ptrace</strong> function. It can be used to detect and prevent" +\ " debuggers. Ptrace is not a public API and Apps that use non-public APIs will" +\ " be rejected from AppStore. </td></tr>" otool_dict["anal"] = pie_flag + ssmash + arc_flag + banned_apis + weak_cryptos + \ crypto + weak_hashes + hashes + randoms + logging + malloc + \ debug return otool_dict except: PrintException("[ERROR] Performing Otool Analysis of Binary")