예제 #1
0
    def run(self):
        Log.info("Preparing to re-compile the application")

        # get identifier
        manifest_module = ManifestModule()
        manifest_module.decompiled_apk = self.decompiled_apk
        self.manifest = manifest_module.run()
        if "print" not in self.manifest:
            identifier = self.manifest.popitem()[1].package()

        recompiled_apk = "{}/{}-recompiled.apk".format(self.output, identifier)

        # unzip
        Log.info("Re-compiling application")
        output = recompile(self.decompiled_apk, recompiled_apk)

        if "Exception" in output:
            return {
                "print":
                "Failed to re-compile the application:\n{}".format(output)
            }

        return {
            "{}_recompiled".format(identifier): recompiled_apk,
            "print": "Application re-compiled to {}".format(recompiled_apk)
        }
예제 #2
0
    def run(self):
        result = {
            "title": "Application Has Secret Codes",
            "details": "",
            "severity": "Low",
            "report": False
        }

        # create manifest
        manifest_module = ManifestModule()
        manifest_module.decompiled_apk = self.decompiled_apk
        self.manifest = manifest_module.run()
        if "print" in self.manifest:
            return {"print": "Could not get the manifest"}

        self.manifest = self.manifest.popitem()[1]

        Log.info("Analysing application's manifest for secret codes")
        secret_codes = self.manifest.secret_codes()

        if secret_codes:
            details = "* Secret Codes:\n * {}".format(
                "\n * ".join(secret_codes))
            details += """

* Test the secret codes using the following command:
adb shell su -c "am broadcast -a {} -d android_secret_code://CODE"
            """.format(self._secret_code_activity)
            result.update({"report": True, "details": details})

        return {
            "{}_result".format(self.name()): result,
            "{}_secret_codes".format(self.manifest.package()): secret_codes
        }
예제 #3
0
    def run(self):
        Log.info("Creating decompilation directory")

        # create decompiled directory
        identifier = self.apk.rsplit("/", 1)[-1].lower().rsplit(".", 1)[0]
        output_path = "{}/{}.decompiled".format(self.output, identifier)
        execute("mkdir -p {}".format(output_path))

        # unzip
        Log.info("Decompiling application")
        decompile(self.apk, output_path)

        # get identifier
        manifest_module = ManifestModule()
        manifest_module.decompiled_apk = output_path
        self.manifest = manifest_module.run()
        if "print" not in self.manifest:
            identifier = self.manifest.popitem()[1].package()

            # move decompiled app to new path
            old_output_path = output_path
            output_path = "{}/{}.decompiled".format(self.output, identifier)
            execute("mv {} {}".format(old_output_path, output_path))

        return {
            "{}_decompiled".format(identifier): output_path,
            "print": "Application decompiled to {}".format(output_path)
        }
예제 #4
0
    def run(self):
        result = {
            "title": "Application Allows Backups",
            "details": "",
            "severity": "Low",
            "report": False
        }

        # create manifest
        manifest_module = ManifestModule()
        manifest_module.decompiled_apk = self.decompiled_apk
        manifest = manifest_module.run()
        if "print" in manifest:
            return {"print": "Could not get the manifest"}

        manifest = manifest.popitem()[1]

        Log.info("Analysing application's manifest")

        if manifest.allow_backup():
            result.update({"report": True})

        return {
            "{}_result".format(self.name()): result
        }
예제 #5
0
    def run(self):
        result = {
            "title": "Application Has Browsable Activities",
            "details": "",
            "severity": "Informational",
            "report": False
        }

        # create manifest
        manifest_module = ManifestModule()
        manifest_module.decompiled_apk = self.decompiled_apk
        self.manifest = manifest_module.run()
        if "print" in self.manifest:
            return {"print": "Could not get the manifest"}

        self.manifest = self.manifest.popitem()[1]

        Log.info("Getting browsable activities and uris")

        browsable_classes = self.manifest.browsable_activities()
        browsable_uris = self.manifest.browsable_uris()

        if browsable_classes or browsable_uris:
            details = "* URIs:\n * {}".format("\n * ".join(browsable_uris))
            details += "\n\n* Classes:\n * {}".format(
                "\n * ".join(browsable_classes))

            result.update({"report": True, "details": details})

        return {
            "{}_result".format(self.name()): result,
            "{}_browsable_classes".format(self.manifest.package()):
            browsable_classes,
            "{}_browsable_uris".format(self.manifest.package()): browsable_uris
        }
예제 #6
0
    def run(self):
        result = {
            "title": "Application Has Inadequate Permissions",
            "details": "",
            "severity": "Medium",
            "report": False
        }

        # create manifest
        manifest_module = ManifestModule()
        manifest_module.decompiled_apk = self.decompiled_apk
        self.manifest = manifest_module.run()
        if "print" in self.manifest:
            return {"print": "Could not get the manifest"}

        self.manifest = self.manifest.popitem()[1]

        Log.info("Analysing application's manifest permissions")

        # setup vars
        permissions = [
            permission.strip() for permission in self.permissions.split(";")
        ]
        app_permissions = self.manifest.permissions()

        inadequate_permissions = []
        for permission in app_permissions:
            if permission in self.permissions:
                inadequate_permissions.append(permission)

        if inadequate_permissions:
            result.update({
                "report":
                True,
                "details":
                "* {}".format("\n* ".join(inadequate_permissions))
            })

            return {
                "{}_result".format(self.name()): result,
                "{}_permissions".format(self.manifest.package()):
                app_permissions
            }

        return {"{}_result".format(self.name()): result}
예제 #7
0
    def run(self):
        Log.info("Checking for apktool.yml file")

        # find apktool yml file
        filename = "{}/apktool.yml".format(self.decompiled_apk)

        if exists(filename):
            Log.info("Creating apktool yaml object")

            # get info
            yaml = ApktoolYaml(filename)

            # get identifier
            identifier = yaml.apk_filename().lower().rsplit(".", 1)[0]

            manifest_module = ManifestModule()
            manifest_module.decompiled_apk = self.decompiled_apk
            self.manifest = manifest_module.run()
            if "print" not in self.manifest:
                identifier = self.manifest.popitem()[1].package()

            return {"{}_yaml".format(identifier): yaml}

        return {"print": "Could not find apktool.yml"}
예제 #8
0
    def run(self):

        Log.info("Checking output folders")
        if not self.output:
            self.output = "/tmp/scrounger-tmp"

        # create the required dirs
        execute("mkdir -p {}".format(self.output))

        # get identifier
        manifest_module = ManifestModule()
        manifest_module.decompiled_apk = self.decompiled_apk
        manifest = manifest_module.run()
        if "print" not in manifest:
            manifest = manifest.popitem()[1]
            identifier = manifest.package()

        # set filenames
        debuggable_apk = "{}/{}-debuggable.apk".format(self.output, identifier)

        # read manifest content
        Log.info("Modifying AndroidManifest.xml")
        with open(manifest.file_path(), "r") as fd:
            manifest_content = fd.read()

        # look for <application> and modify debuggable
        new_manifest_content = ""
        for line in manifest_content.split("\n"):
            if "<application" in line:
                if "debuggable" in line:
                    line = line.replace("android:debuggable=\"false\"",
                                        "android:debuggable=\"true\"")
                else:
                    line_split = line.split(">", 1)
                    line = "{} android:debuggable=\"true\">{}".format(
                        line_split[0], line_split[1])

            new_manifest_content = "{}\n{}".format(new_manifest_content, line)

        new_manifest_content = "\n".join(new_manifest_content.split("\n")[1:])

        # overwrite the manifest with the new content
        with open(manifest.file_path(), "w") as fd:
            fd.write(new_manifest_content)

        # recompiled the application
        Log.info("Re-compiling application")
        output = recompile(self.decompiled_apk, debuggable_apk)

        if "Exception" in output:
            return {
                "print":
                "Failed to re-compile the application:\n{}".format(output)
            }

        # signing the application
        sign_module, signed_apk = SignApkModule(), None
        sign_module.recompiled_apk = debuggable_apk
        sign_module.output = self.output
        sign_result = sign_module.run()
        for key in sign_result:
            if key.endswith("_signed"):
                signed_apk = sign_result[key]

        if signed_apk:
            execute("mv {} {}".format(signed_apk, debuggable_apk))

        # install the application
        if self.install:
            Log.info("Uninstalling previously installed application")
            self.device.uninstall(identifier)

            Log.info("Installing new debuggable application")
            self.device.install(debuggable_apk)

        return {
            "{}_debuggable".format(identifier):
            debuggable_apk,
            "print":
            "Application re-compiled and signed to {}".format(debuggable_apk)
        }
예제 #9
0
    def run(self):
        result = {
            "title": "Application Does Not Use Obfuscation",
            "details": "",
            "severity": "Medium",
            "report": False
        }

        exceptions = []

        # preparing variable to run
        ignore = [filepath.strip() for filepath in self.ignore.split(";")]

        # get identifier
        Log.info("Checking identifier package only")
        if self.check_package_only:
            manifest_module = ManifestModule()
            manifest_module.decompiled_apk = self.decompiled_apk
            self.manifest = manifest_module.run()
            if "print" not in self.manifest:
                identifier = self.manifest.popitem()[1].package()
            else:
                identifier = None
                exceptions += [Exception(self.manifest["print"])]

        Log.info("Identifying class names")
        class_names_list = class_names(self.decompiled_apk, ignore, identifier)

        Log.info("Identifying method names")
        method_names_list = method_names(self.decompiled_apk, ignore,
                                         identifier)

        Log.info("Identifying strings")
        strings_list = app_strings(self.decompiled_apk, ignore, identifier)

        Log.info("Identifying resources")
        resources_list = app_used_resources(self.decompiled_apk, ignore,
                                            identifier)

        Log.info("Analysing identified strings")

        # start by analysing class names
        class_detect_lang = detect_langs(" ".join(class_names_list))[0]
        class_small_names = [
            class_name for class_name in class_names_list
            if len(class_name) < 4
        ]

        # check if lang != expected or probability lower than required for class
        # names
        if class_detect_lang.lang != self.language or \
        class_detect_lang.prob*100 < self.min_percentage:
            result.update({
                "title":
                "Application shows evidence of obfuscation",
                "details":
                "Detected language {} with probability {}% on \
class names.".format(class_detect_lang.lang, class_detect_lang.prob * 100),
                "report":
                True
            })

        # check small_classes/total_classes >= min_percent_class_names
        sclass_percent = len(class_small_names) * 1.0 / len(
            class_names_list) * 100
        if sclass_percent >= self.min_percentage_small_names:
            result.update({
                "title":
                "Application shows evidence of obfuscation",
                "details":
                "{}\n\nDetected small class names: {}%".format(
                    result["details"], sclass_percent),
                "report":
                True
            })

        Log.debug("Classes detected {} with probability of {}%".format(
            class_detect_lang.lang, class_detect_lang.prob * 100))
        Log.debug("Small len classes {}/{} = {}%".format(
            len(class_small_names), len(class_names_list), sclass_percent))
        Log.debug("Unique small len classes {}/{} = {}%".format(
            len(set(class_small_names)), len(set(class_names_list)),
            len(set(class_small_names)) * 1.0 / len(set(class_names_list)) *
            100))

        # analyse method names
        method_detect_lang = detect_langs(" ".join(method_names_list))[0]
        method_small_names = [
            method_name for method_name in method_names_list
            if len(method_name) < 4
        ]

        # check if lang != expected or probability lower than required for
        # method names
        if method_detect_lang.lang != self.language or \
        method_detect_lang.prob*100 < self.min_percentage:
            result.update({
                "title":
                "Application shows evidence of obfuscation",
                "details":
                "{}\n\nDetected language {} with probability {}% on \
method names.".format(result["details"], method_detect_lang.lang,
                      method_detect_lang.prob * 100),
                "report":
                True
            })

        # check small_methods/total_methods >= min_percent_mathod_names
        smthod_percent = len(method_small_names) * 1.0 / len(
            method_names_list) * 100
        if smthod_percent >= self.min_percentage_small_names:
            result.update({
                "title":
                "Application shows evidence of obfuscation",
                "details":
                "{}\n\nDetected small method names: {}%".format(
                    result["details"], smthod_percent),
                "report":
                True
            })

        Log.debug("Methods detected {} with probability of {}%".format(
            method_detect_lang.lang, method_detect_lang.prob * 100))
        Log.debug("Small len methods {}/{} = {}%".format(
            len(method_small_names), len(method_names_list), smthod_percent))
        Log.debug("Unique small len classes {}/{} = {}%".format(
            len(set(method_small_names)), len(set(method_names_list)),
            len(set(method_small_names)) * 1.0 / len(set(method_names_list)) *
            100))

        # analyse strings and resources
        strings_detect_lang = detect_langs(" ".join(strings_list +
                                                    resources_list))[0]

        # check if lang != expected or probability lower than required for
        # strings and resources
        if strings_detect_lang.lang != self.language or \
        strings_detect_lang.prob*100 < self.min_percentage:
            result.update({
                "title":
                "Application shows evidence of obfuscation",
                "details":
                "{}\n\nDetected language {} with probability {}% on \
strings and resources.".format(result["details"], strings_detect_lang.lang,
                               strings_detect_lang.prob * 100),
                "report":
                True
            })

        Log.debug("Strings detected {} with probability of {}%".format(
            strings_detect_lang.lang, strings_detect_lang.prob * 100))

        if not result["report"]:
            result.update({
                "details": "No evidence of obfuscation found.",
                "report": True
            })

        return {
            "{}_result".format(self.name()): result,
            "exceptions": exceptions
        }