Example #1
0
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 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")
Example #7
0
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")
Example #9
0
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")
Example #10
0
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
Example #12
0
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")
Example #13
0
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
Example #16
0
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")
Example #19
0
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')
Example #26
0
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")
Example #31
0
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")
Example #34
0
 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")
Example #37
0
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")
Example #38
0
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 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 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)
Example #44
0
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")
Example #46
0
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")
Example #48
0
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")