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." }
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
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 }
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}
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}
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}