def run(self):
        Log.info("Installing binaries")
        binaries_to_install = self.binaries.split(";")
        packages_to_install = self.packages.split(";")
        repositories_to_install = self.repositories.split(";")
        binaries_local_path = "{}/bin/ios".format(_SCROUNGER_HOME)

        for binary in binaries_to_install:
            installed = self.device.install_binary("{}/{}".format(
                binaries_local_path, binary))

            if not installed:
                Log.error("Could not install {}".format(binary))

        scrounger_apt_list = "/etc/apt/sources.list.d/scrounger.list"
        repositories_list = " ".join(self.device.repositories())

        Log.info("Adding repositories")
        for repository in repositories_to_install:
            if repository not in repositories_list:
                Log.info("Adding {} repository".format(repository))
                if not repository.endswith("/"):
                    repository = "{}/".format(repository)
                self.device.execute("echo deb {} ./ >> {}".format(
                    repository, scrounger_apt_list))
                self.device.execute("apt update")

        packages = " ".join(packages_to_install)
        Log.info("Trying to install {}".format(packages))
        self.device.execute(
            "apt -y --allow-unauthenticated install {}".format(packages))

        return {
            "print": "Binaries installed."
        }
Beispiel #2
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
Beispiel #3
0
    def run(self):
        Log.info("Installing binaries")
        binaries_to_install = self.binaries.split(";")
        binaries_local_path = "{}/bin/ios".format(_SCROUNGER_HOME)

        for binary in binaries_to_install:
            installed = self.device.install_binary("{}/{}".format(
                binaries_local_path, binary))

            if not installed:
                Log.error("Could not install {}".format(binary))

        return {"print": "Binaries installed."}
    def run(self):
        result = {
            "title": "Application Does Not Detect Debuggers",
            "details": "",
            "severity": "Medium",
            "report": False
        }

        Log.info("Getting binary strings")
        strs = strings(self.binary)

        Log.info("Analysing Strings")
        if not re.search(self._regex, strs):
            result.update({
                "report": True,
                "details": "No evidence of the application trying to detect \
debuggers being attached."
            })

        Log.info("Starting the application and identifying the process ID")
        openned, attempts = self.device.start(self.identifier), 0
        while "device locked" in openned[1] and attempts < 3:
            Log.error("Device is locked - cannot open the application")
            Log.info("Please unlock the device - waiting 10 seconds")
            sleep(10)
            openned, attempts = self.device.start(self.identifier), attempts + 1

        pid = self.device.pid(self.identifier)

        if pid:
            Log.info("Starting GDB on the remote device")
            gdb = GDB(self.device)

            Log.info("Attaching GDB to the application")
            gdb_result, attempt = gdb.execute("attach {}".format(pid)), 0

            # try to get stdout, might take time to flush
            while not gdb_result and attempt < 3:
                sleep(5)
                gdb_result, attempt = gdb.read(), attempt + 1

            if gdb_result and "unable to attach" in gdb_result.lower():
                result.update({
                    "title": "Application Detected Debugger Attached",
                    "report": True,
                    "details": "Scrounger was unable to attach a debugger to \
the application."
                })
            elif gdb_result:
                result.update({
                    "report": True,
                    "details": "{}\n\nScrounger was able to attach a debugger \
to the application:\n\n{}".format(result["details"], gdb_result)
                    }
                )

            gdb.exit()

        else:
            Log.error("The application is not running")

        return {
            "{}_result".format(self.name()): result
        }
Beispiel #5
0
    def run(self):
        from time import sleep
        result = {
            "title": "Application Does Not Implement SSL Pinning",
            "details": "",
            "severity": "High",
            "report": False
        }

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

        Log.info("Identifying smali directories")
        dirs = smali_dirs(self.decompiled_apk)

        Log.info("Analysing application's smali for SSL evidences")
        for directory in dirs:
            smali = "{}/{}".format(self.decompiled_apk, directory)
            ssl_keywords.update(pretty_grep(self.regex, smali))

        if not ssl_keywords:
            result.update({
                "report": True,
                "details": "Found no evidences of a `TrustManager`."
            })

        Log.info("Analysing SSL evidences")

        for filename in ssl_keywords:
            if any(filepath in filename for filepath in ignore):
                continue

            with open(filename, "r") as fp:
                smali = fp.read()

            if re.search(self.mock_check_server, smali):
                result.update({
                    "report":
                    True,
                    "details":
                    "{}\n* {}:{}\n".format(
                        result["details"],
                        filename.replace(self.decompiled_apk, ""),
                        extract_smali_method("checkServerTrusted", filename))
                })

            if re.search(self.mock_accepted_issuers, smali):
                result.update({
                    "report":
                    True,
                    "details":
                    "{}\n* {}:{}\n".format(
                        result["details"],
                        filename.replace(self.decompiled_apk, ""),
                        extract_smali_method("getAcceptedIssuers", filename))
                })

        if self.device and self.identifier and \
        self.proxy_host != None and self.proxy_port != None:
            Log.info("Testing SSL Pinning using a proxy")
            Log.info(
                "Make sure your device trusts the CA in: {}/ca.crt".format(
                    _CERT_PATH))
            Log.info("Waiting for {} seconds to allow time to setup the \
proxy on the remote device".format(self.wait_time))
            sleep(int(self.wait_time))

            Log.info("Killing the application")
            self.device.stop(self.identifier)

            Log.info("Starting the SSL proxy")
            proxy_server = create_server(self.proxy_host, self.proxy_port,
                                         _CERT_PATH)

            Log.info("Starting the Application")
            self.device.start(self.identifier)

            Log.info("Waiting for the Application to start and make requests")
            sleep(10)

            pinned = list(
                set(proxy_server.server.connected) -
                set(proxy_server.server.requested))

            if not proxy_server.server.connected:
                Log.error("No connections made by the application")

            if pinned:
                result.update({
                    "title":
                    "Application Implements SSL Pinning",
                    "report":
                    True,
                    "details":
                    "{}\n\nThe application started a connection but \
made no requests to the following domains:\n* {}".format(
                        result["details"], "\n* ".join(pinned))
                })

            proxy_server.stop()

        return {"{}_result".format(self.name()): result}
Beispiel #6
0
    def run(self):
        result = {
            "title": "Application Does Not Implement SSL Pinning",
            "details": "",
            "severity": "Medium",
            "report": False
        }

        Log.info("Getting application's strings")
        strs = strings(self.binary)

        Log.info("Analysing strings and class dump")
        matches = re.findall(self._regex, strs)
        evidence = pretty_grep(self._regex, self.class_dump)

        if matches:
            result.update({
                "report":
                True,
                "details":
                "The following strings were found:\n* {}".format("\n* ".join(
                    sorted(set(matches))))
            })

        if evidence:
            result.update({
                "report":
                True,
                "details":
                "{}\nThe following was found in the class dump:\n\
{}".format(result["details"], pretty_grep_to_str(evidence, self.class_dump))
            })

        if self.device and self.identifier and \
        self.proxy_host != None and self.proxy_port != None:
            Log.info("Testing SSL Pinning using a proxy")
            Log.info(
                "Make sure your device trusts the CA in: {}/ca.crt".format(
                    _CERT_PATH))
            Log.info("Waiting for {} seconds to allow time to setup the \
proxy on the remote device".format(self.wait_time))
            sleep(int(self.wait_time))

            Log.info("Killing the application")
            self.device.stop(self.identifier)

            Log.info("Starting the SSL proxy")
            proxy_server = create_server(self.proxy_host, self.proxy_port,
                                         _CERT_PATH)

            Log.info("Starting the Application")
            self.device.start(self.identifier)

            Log.info("Waiting for the Application to start and make requests")
            sleep(10)

            pinned = list(
                set(proxy_server.server.connected) -
                set(proxy_server.server.requested))

            if not proxy_server.server.connected:
                Log.error("No connections made by the application")

            if pinned:
                result.update({
                    "report":
                    True,
                    "details":
                    "{}\n\nThe application started a connection but \
made no requests to the following domains:\n* {}".format(
                        result["details"], "\n* ".join(pinned))
                })

            proxy_server.stop()

        return {"{}_result".format(self.name()): result}
Beispiel #7
0
    def run(self):
        result = {
            "title": "Application Does Not Detect Debuggers",
            "details": "",
            "severity": "Medium",
            "report": False
        }

        ignore = [filepath.strip() for filepath in self.ignore.split(";")]

        Log.info("Identifying smali directories")
        dirs = smali_dirs(self.decompiled_apk)

        Log.info("Looking for evidence in smali code")
        debug_evidence = {}
        for directory in dirs:
            smali = "{}/{}".format(self.decompiled_apk, directory)
            debug_evidence.update(pretty_grep(self.debug_regex, smali))

        if debug_evidence:
            result.update({
                "title":
                "Application Destects Debuggers",
                "report":
                True,
                "details":
                "The following evidence was found in the smali \
code:\n{}".format(
                    pretty_grep_to_str(debug_evidence, self.decompiled_apk,
                                       ignore))
            })
        else:
            result.update({
                "details": "No evidence of debug detection was found in the \
smali code.",
                "report": True
            })

        if self.repackage:
            Log.info("Trying to modify the application to be debuggable")

            # make the application debuggable
            debug_module = DModule()
            debug_module.decompiled_apk = self.decompiled_apk
            debug_module.device = self.device
            debug_module.output = None  # will default to /tmp
            debug_module.install = True
            debug_module.run()

        Log.info("Starting the application and identifying the process ID")
        self.device.start(self.identifier)
        pid = self.device.pid(self.identifier)

        if pid:
            Log.info("Forwarding local ports")
            forward(54321, pid)

            Log.info("Starting JDB")
            jdb = JDB("127.0.0.1", 54321)

            if not jdb.running():
                result.update({
                    "report":
                    True,
                    "details":
                    "{}\n\nScrounger was unable to attach a debugger\
 to the application.".format(result["details"])
                })
            else:
                result.update({
                    "report":
                    True,
                    "details":
                    "{}\n\nScrounger was able to attach a debugger \
to the application:\n\n{}".format(result["details"], jdb.read())
                })

            Log.info("Removing forwarded ports and exiting jdb")
            remove_forward()
            jdb.exit()

        else:
            Log.error("The application is not running")

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