Exemplo n.º 1
0
    def run(self):
        Log.info("Dumping classes with otool")
        try:
            class_dump = otool_class_dump_to_dict(otool_class_dump(
                self.binary))
        except Exception as e:
            Log.error("An error ocurred when trying to use otool")
            Log.debug(e)
            Log.info("Trying jtool")
            class_dump = jtool_class_dump_to_dict(jtool_class_dump(
                self.binary))

        dump_name = self.binary.rsplit("/", 1)[-1].replace(" ", ".")
        result = {
            "{}_class_dump".format(dump_name.replace(".", "_")): class_dump
        }

        if hasattr(self, "output") and self.output:
            Log.info("Saving classes to file")
            dump_path = "{}/{}.class.dump".format(self.output, dump_name)

            # create output folder
            execute("mkdir -p {}".format(dump_path))
            save_class_dump(class_dump, dump_path)

            result.update({
                "{}_dump_path".format(dump_name.replace(".", "_")): dump_path,
                "print": "Dump saved in {}.".format(dump_path)
            })

        return result
Exemplo n.º 2
0
    def run(self):
        Log.info("Creating output directories")

        execute("mkdir -p {}".format(self.output))

        Log.info("Pulling keychain data")
        keychain_data = self.device.keychain_data()

        result = {"keychain_data": keychain_data}

        if hasattr(self, "output") and self.output:
            filename = "{}/keychain.json".format(self.output)

            Log.info("Saving keychain data")
            with open(filename, "w") as fp:
                fp.write(dumps(keychain_data, ensure_ascii=False))

            result.update({
                "keychain_file":
                filename,
                "print":
                "Keychain data saved in {}.".format(filename)
            })

        return result
Exemplo n.º 3
0
    def run(self):

        # get the jar first
        jar_module = JarModule()
        jar_module.output = self.output
        jar_module.apk = self.apk
        result = jar_module.run()

        self.jar = [result[jar] for jar in result if jar.endswith("_jar")][0]
        if not self.jar:
            return {"print": "Could not decompile the application"}

        Log.info("Creating source directory")
        # create source directory
        identifier = self.jar.rsplit("/", 1)[-1].lower().rsplit(".", 1)[0]

        output_path = "{}/{}.source".format(self.output, identifier)
        execute("mkdir -p {}".format(output_path))

        # unzip
        Log.info("Getting application's source")
        source(self.jar, output_path)

        return {
            "{}_source".format(identifier): output_path,
            "print": "Application source reversed to {}".format(output_path)
        }
Exemplo n.º 4
0
    def run(self):
        # create unzipped directory
        identifier = self.ipa.rsplit("/", 1)[-1].lower().rsplit(".", 1)[0]

        Log.info("Crating output directories")
        output_path = "{}/{}.unzipped".format(self.output, identifier)
        execute("mkdir -p {}".format(output_path))

        # unzip
        Log.info("Unzipping application")
        unzip(self.ipa, output_path)

        # get new identifier
        app_path = application_path(output_path)

        # get info
        info_module = InfoModule()
        info_module.unzipped_ipa = output_path
        info = info_module.run()
        for key in info:
            if key.endswith("_info"):
                info = info[key]
                break

        # move to new directory
        if "CFBundleIdentifier" in info:
            identifier = info["CFBundleIdentifier"]
            old_output_path = output_path
            output_path = "{}/{}.unzipped".format(self.output, identifier)
            execute("mv {} {}".format(old_output_path, output_path))

        return {
            "{}_unzipped".format(identifier): output_path,
            "print": "Application unzipped to {}".format(output_path)
        }
Exemplo n.º 5
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)
        }
Exemplo n.º 6
0
    def run(self):
        Log.info("Dumping classes with otool")
        class_dump = otool_class_dump_to_dict(
            self.device.otool("-ov", self.binary)[0]) # stdout

        dump_name = self.binary.rsplit("/", 1)[-1].replace(" ", ".")
        result = {
            "{}_class_dump".format(dump_name.replace(".", "_")): class_dump
        }

        if hasattr(self, "output") and self.output:
            Log.info("Saving classes to file")
            dump_path = "{}/{}.class.dump".format(self.output, dump_name)

            # create output folder
            execute("mkdir -p {}".format(dump_path))

            save_class_dump(class_dump, dump_path)

            result.update({
                "{}_dump_path".format(dump_name.replace(".", "_")): dump_path,
                "print": "Dump saved in {}.".format(dump_path)
            })

        return result
Exemplo n.º 7
0
    def run(self):
        # create decompiled directory
        identifier = self.apk.rsplit("/", 1)[-1].lower().rsplit(".", 1)[0]

        Log.info("Creating output directory")
        filename = "{}/{}.jar".format(self.output, identifier)
        execute("mkdir -p {}".format(self.output))

        # get jar
        Log.info("Getting application's jar")
        jar(self.apk, filename)

        return {
            "{}_jar".format(identifier): filename,
            "print": "Application JAR saved to {}".format(filename)
        }
Exemplo n.º 8
0
    def complete_set(self, text, line, start_index, end_index):
        from scrounger.utils.general import execute

        CMD_LEN = (3, 4)

        commands = line.split(" ")
        options = []

        if "global" in line:
            CMD_LEN = (4, 5)

        if len(commands) < CMD_LEN[0]:  # if setting var name or global
            if "global" not in line:
                options = ["global"]
            options += [option for option in self._session.global_options
                        ] + [option for option in self._session.options]

        elif len(commands) < CMD_LEN[1]:  # if setting value
            options = [""]  # add None as 1 of the options
            options += [
                "result:{}".format(name) for name in self._session.results
            ]

            # looks in the file system
            if text.startswith("./") or text.startswith("/") or \
            text.startswith("~/"):
                options += [
                    f.replace(_HOME, "~") for f in execute("ls -d {}*".format(
                        text.replace("~", _HOME))).split("\n")
                ]

        options = list(set(options))
        return [option for option in options if option.startswith(text)]
Exemplo n.º 9
0
    def run(self):
        results = []
        exceptions = []

        # run all modules
        Log.info("Running all Android analysis modules")
        for module in self._analysis_modules:

            instance = module.Module()
            for option in self.options:
                if hasattr(self, option["name"]):
                    setattr(instance, option["name"],
                            getattr(self, option["name"]))

            try:
                Log.debug("Validating and Running: {}".format(instance.name()))

                instance.validate_options()
                run_result = instance.run()

                for key in run_result:
                    if key.endswith("_result") and validate_analysis_result(
                            run_result[key]) and run_result[key]["report"]:
                        results += [run_result[key]]
            except Exception as e:
                exceptions += [{"module": instance.name(), "exception": e}]

        # setup output folders
        Log.info("Creating output folders")
        output_directory = "{}{}".format(self.output, self._output_directory)
        execute("mkdir -p {}".format(output_directory))
        output_file = "{}/results.json".format(output_directory)

        # write results to json file
        Log.info("Writing results to file")
        with open(output_file, "w") as fp:
            fp.write(dumps(results))

        return {
            "android_analysis":
            results,
            "exceptions":
            exceptions,
            "print":
            "The following issues were found:\n* {}".format("\n* ".join(
                [result["title"] for result in results]))
        }
Exemplo n.º 10
0
    def preloop(self):
        from os import popen, path
        from scrounger.utils.general import execute
        import scrounger.modules

        _Cmd.preloop(self)  ## sets up command completion

        self._rows, self._columns = popen('stty size', 'r').read().split()
        self._rows, self._columns = int(self._rows), int(self._columns)
        if self._columns < 128: self._columns = 128

        # need to add / to then replace it
        modules_path = "{}/".format(scrounger.modules.__path__[0])
        modules = execute("find {} -name '*.py'".format(modules_path))

        self._available_modules = [
            module.replace(modules_path, "").replace(".py", "")
            for module in modules.split("\n") if module and "__" not in module
        ]

        # add custom modules
        modules_path = "{}/modules/".format(_SCROUNGER_HOME)
        modules = execute("find {} -name \"*.py\"".format(modules_path))

        # add path to sys.path
        _path.append(modules_path)

        #self._custom_modules = [
        self._available_modules += [
            module.replace(modules_path, "").replace(".py", "")
            for module in modules.split("\n") if module and "__" not in module
        ]

        # fix for macos
        self._available_modules = [
            module[1:] if module.startswith("/") else module
            for module in sorted(self._available_modules)
        ]

        execute("mkdir -p {}".format(self._global_options["output"]))

        readline.set_completer_delims(' \t\n')
        if path.exists(_HISTORY_FILE):
            readline.read_history_file(_HISTORY_FILE)
Exemplo n.º 11
0
    def _set_var(self, options, variable):
        if not " " in variable:
            key = variable.strip()
            value = None
        else:
            key, value = variable.split(" ", 1)

        if value == "None" or value == None:
            value = ""

        if key == "output":
            from scrounger.utils.general import execute
            execute("mkdir -p {}".format(value))

        if key.lower() == "debug" and value.lower() == "true":
            import logging as _logging
            Log.setLevel(_logging.DEBUG)

        options[key] = value
Exemplo n.º 12
0
    def _stop_connection(self):
        """
        Stops the SSH connection with the remote device
        """
        from scrounger.utils.general import execute

        # cleanup
        if self._timer:
            self._timer.cancel()
            self._timer = None

        if self._ssh_session:
            self._ssh_session.disconnect()
            #self._iproxy_process.kill()
            self._iproxy_process = self._ssh_session = None
            execute('killall iproxy')  # make sure iproxy is killed

            # Log session stop
            _Log.debug("ssh session killed.")
Exemplo n.º 13
0
    def plist(self, plist_file_path):
        """
        Returns the contents of a plist file on the remote device

        :param str plist_file_path: the plist file to be read
        :return: returns a dict with the plist contents
        """
        from scrounger.utils.ios import plist
        from scrounger.utils.general import execute

        # get local file
        local_file = "/tmp/Info.plist"
        self.get(plist_file_path, local_file)
        plist_content = plist(local_file)

        # clean up tmp file
        execute("rm -rf {}".format(local_file))

        return plist_content
        """
Exemplo n.º 14
0
    def __init__(self):
        import scrounger.modules.analysis.android as android_analysis
        all_modules = android_analysis.__all__

        # add custom modules
        modules_path = "{}/modules/".format(_SCROUNGER_HOME)
        modules = execute("find {} -name \"*.py\"".format(modules_path))

        # add path to sys.path
        _path.append(modules_path)

        modules = [
            module.replace(modules_path, "").replace(".py", "")
            for module in modules.split("\n") if module and "__" not in module
        ]

        all_modules += [
            module for module in modules
            if module.startswith("custom/analysis/android")
        ]

        for module in all_modules:
            if module.startswith("custom/"):
                module_class = __import__("{}".format(module.replace("/",
                                                                     ".")),
                                          fromlist=["Module"])
            else:
                module_class = __import__(
                    "scrounger.modules.analysis.android.{}".format(module),
                    fromlist=["Module"])

            # avoid running full_analysis again
            if self.__module__ == module_class.__name__:
                continue

            self._analysis_modules += [module_class]

            self.options += module_class.Module.options
            sanitized_options = {}
            for option in self.options:
                if option["name"] not in sanitized_options:
                    sanitized_options[option["name"]] = option
                elif option["name"] in sanitized_options and \
                not sanitized_options[option["name"]]["required"] and \
                option["required"]:
                    sanitized_options[option["name"]]["required"] = True

            self.options = sanitized_options.values()

        super(Module, self).__init__()
Exemplo n.º 15
0
    def get(self, remote_file_path, local_file_path):
        """
        Retrieves a file from the remote device

        :param str remote_file_path: the path on the remote device
        :param str local_file_path: the path on the local host to copy the file
        to (it needs to contain the file name too)
        :return: returns nothing
        """
        from scrounger.utils.general import execute

        # start a connection if there is none
        self._start_connection()

        # logging files
        _Log.debug("copying {} to {}.".format(remote_file_path,
                                              local_file_path))

        # create local file path if not exists
        execute("mkdir -p {}".format(local_file_path.split("/", 1)[0]))

        # get file
        self._ssh_session.get_file(remote_file_path, local_file_path)
Exemplo n.º 16
0
    def __init__(self, name):
        from os import popen, path

        # helper functions
        from scrounger.utils.general import execute

        # used to find the available modules
        import scrounger.modules

        self._name = name

        self._rows, self._columns  = popen('stty size', 'r').read().split()
        self._rows, self._columns  = int(self._rows), int(self._columns)
        if self._columns < 128: self._columns = 128

        # need to add / to then replace it
        modules_path = "{}/".format(scrounger.modules.__path__[0])
        modules = execute("find {} -name '*.py'".format(modules_path))

        self._available_modules = [
            module.replace(modules_path, "").replace(".py", "")
            for module in modules.split("\n")
            if module and "__" not in module
        ]

        # add custom modules
        modules_path = "{}/modules/".format(_SCROUNGER_HOME)
        modules = execute("find {} -name \"*.py\"".format(modules_path))

        # add path to sys.path
        _path.append(modules_path)

        self._available_modules += [
            module.replace(modules_path, "").replace(".py", "")
            for module in modules.split("\n")
            if module and "__" not in module
        ]

        # fix for macos
        self._available_modules = [
            module[1:] if module.startswith("/") else module
            for module in sorted(self._available_modules)
        ]

        # public vars to be used by calling modules
        self.options = {}
        self.global_options  = {
            "debug":   "False",
            "device":  "",
            "output":  "",
            "verbose": "False"
        }

        self.devices = {}
        self.results = {}
        self.exceptions = [] # unused
        self.prompt = None

        # initialize private vars
        self._module_instance = None
        self._current_module = None
        self._module_class = None
Exemplo n.º 17
0
def _create_custom_modules_paths():
    from scrounger.utils.general import execute
    from os import path

    current_path = path.realpath(__file__).rsplit('/', 1)[0]
    modules_path = "{}/scrounger/modules/".format(current_path)

    # create custom module paths
    module_types = execute("find {} -type d".format(modules_path))
    for module_type in module_types.split("\n"):
        custom_path = "{}/modules/custom/{}".format(_SCROUNGER_HOME,
            module_type.replace(modules_path, ""))
        execute("mkdir -p {}".format(custom_path))

        # add __init__.py to be able to import modules
        execute("touch {}/__init__.py".format(custom_path))

    # copy ios binaries
    ios_binaries_path = "{}/bin/ios/".format(current_path)
    installed_path = "{}/bin/ios".format(_SCROUNGER_HOME)
    execute("mkdir -p {}".format(installed_path))

    binaries = execute("find {} -type f".format(ios_binaries_path))
    for binary in binaries.split("\n"):
        execute("cp {} {}".format(binary, installed_path))

    # copy android binaries
    android_binaries_path = "{}/bin/android/".format(current_path)
    installed_path = "{}/bin/android".format(_SCROUNGER_HOME)
    execute("mkdir -p {}".format(installed_path))

    binaries = execute("find {} -type f".format(android_binaries_path))
    for binary in binaries.split("\n"):
        execute("cp {} {}".format(binary, installed_path))

    # change scrounger's private key perms
    execute("chmod 600 {}/bin/ios/scrounger.key".format(_SCROUNGER_HOME))

    # generate keys for SSL proxy
    execute("mkdir -p {}/certs".format(_CERT_PATH))
    execute("openssl genrsa -out {}/ca.key 2048".format(_CERT_PATH))
    execute("openssl req -new -x509 -days 3650 -key {}/ca.key \
-out {}/ca.crt -subj \"/CN=proxy2 CA\"".format(_CERT_PATH, _CERT_PATH))
    execute("openssl genrsa -out {}/cert.key 2048".format(_CERT_PATH))
Exemplo n.º 18
0
def _create_custom_modules_paths():
    from scrounger.utils.general import execute
    from os import path

    current_path = path.realpath(__file__).rsplit('/', 1)[0]
    modules_path = "{}/scrounger/modules/".format(current_path)

    # create custom module paths
    module_types = execute("find {} -type d".format(modules_path))
    for module_type in module_types.split("\n"):
        custom_path = "{}/modules/custom/{}".format(
            _SCROUNGER_HOME, module_type.replace(modules_path, ""))
        execute("mkdir -p {}".format(custom_path))

        # add __init__.py to be able to import modules
        execute("touch {}/__init__.py".format(custom_path))

    # copy ios binaries
    ios_binaries_path = "{}/bin/ios/".format(current_path)
    installed_path = "{}/bin/ios".format(_SCROUNGER_HOME)
    execute("mkdir -p {}".format(installed_path))

    binaries = execute("find {} -type f".format(ios_binaries_path))
    for binary in binaries.split("\n"):
        execute("cp {} {}".format(binary, installed_path))

    # copy android binaries
    ios_binaries_path = "{}/bin/android/".format(current_path)
    installed_path = "{}/bin/android".format(_SCROUNGER_HOME)
    execute("mkdir -p {}".format(installed_path))

    binaries = execute("find {} -type f".format(ios_binaries_path))
    for binary in binaries.split("\n"):
        execute("cp {} {}".format(binary, installed_path))
Exemplo n.º 19
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)
        }
Exemplo n.º 20
0
    def __init__(self):
        """
        Creates a module object and checks if the required variables and methods
        are defined
        """

        # check for meta and options variables first
        REQUIRED_VARIABLES = ["meta", "options"]
        for variable in REQUIRED_VARIABLES:
            if not hasattr(self, variable):
                raise MissingFieldException(
                    "Missing the `{}` variable when defining a module".format(
                        variable))

        # check for requires methods
        REQUIRED_METHODS = ["run", "validate_options"]
        for method in REQUIRED_METHODS:
            if not hasattr(self, method):
                raise MissingFieldException(
                    "Missing the `{}` method when defining a module".format(
                        method))

        # check if meta variable has all the necessary values and are valid
        REQUIRED_META_FIELDS = {
            "author": str,
            "description": str,
            "certainty": int
        }

        for field in REQUIRED_META_FIELDS:
            # check if any missing fields

            if field not in self.meta:
                raise MisconfiguredVariable(
                    "Field `{}` is missing from the `meta` variable".format(
                        field))

            # check if values are valid
            if not isinstance(self.meta[field], REQUIRED_META_FIELDS[field]):
                from scrounger.utils.general import execute
                execute("echo Meta: {} >> /tmp/debug.log".format(self.meta))
                raise MisconfiguredVariable(
                    "Metafield `{}` is not of type `{}`".format(
                        field, REQUIRED_META_FIELDS[field]))

        # check if certainty is > 0 and <= 100
        if self.meta["certainty"] < 0 or self.meta["certainty"] > 100:
            raise MisconfiguredVariable(
                "Certainty must be a value between 0 and 100")

        # check if additional options are valid
        required_fields = ["name", "description", "required", "default"]
        for option in self.options:
            for field in required_fields:
                if field not in option:
                    raise MisconfiguredVariable(
                        "Field `{}` not found in option `{}`".format(
                            field, option))

            # check if var name has spaces
            if " " in option["name"]:
                raise MisconfiguredVariable(
                    "Option `{}` contains spaces".format(option["name"]))

        self._init_called = True