Exemple #1
0
    def module_run(self, verbose=False):

        logs = ""
        vulnerable = False
        providers = self.get_providers()

        z = self.apk.vm_analysis.tainted_packages.search_methods(
            ".", "addURI", ".")

        for p in z:
            method = self.apk.dalvik_vm_format.get_method_by_idx(
                p.get_src_idx())
            if method.get_code() is None:
                continue
            mx = self.apk.vm_analysis.get_method(method)
            ms = decompile.DvMethod(mx)
            try:
                ms.process()
            except AttributeError as e:
                self.warning(
                    "Error while processing disassembled Dalvik method: %s" %
                    e.message)
            source = ms.get_source()
            matches = re.findall(r'addURI\("([^"]*)", "([^"]*)"', source)
            for match in matches:
                for provider in providers:
                    if provider["authorities"] == match[0]:
                        provider["uris"].add("uri://%s/%s" %
                                             (match[0], match[1]))

        for provider in providers:
            provider["uris"] = list(provider["uris"])

            if provider["exported"] and provider["permission"] is None and provider["read_permission"] is None\
                    and provider["write_permission"] is None:
                #content was introduced in honeycomb
                if self.avd is not None and self.avd.target >= 16:
                    for uri in provider["uris"]:
                        logs += "$ adb shell content query --uri %s\n" % uri
                        logs += self.avd.shell("content query --uri %s" % uri)
                provider["vulnerable"] = True
                vulnerable = True
            else:
                provider["vulnerable"] = False

        if verbose:
            print logs
        return {
            "results":
            providers,
            "vulnerabilities": [
                framework.Vulnerability(
                    "Exported content provider.",
                    "The following application provider is exported, which means that any application can access it"
                    " without the need for any custom permission.",
                    framework.Vulnerability.MEDIUM,
                    resources=[p for p in providers if p["vulnerable"]],
                    logs=logs).__dict__
            ] if vulnerable else []
        }
Exemple #2
0
    def module_run(self, verbose=False):

        logs = ""
        receivers = self.get_receivers()
        vulnerable = False

        #for each action by categories, send intent and see what happen
        for receiver in receivers:
            receiver["vulnerable"] = False
            if receiver["exported"] and receiver["permission"] is None:
		receiver["vulnerable"] = True
                #1. Get exposed broadcast receivers from results
		if self.avd is not None:
	                for intent in receiver["intent_filters"]:
	                    if intent['category'] is not None:
	                        output = self.avd.shell("am broadcast -a %s -c %s -n %s/%s" %
	                                                (intent['action'], intent['category'],
	                                                 self.apk.get_package(), receiver["name"]))
	                    else:
	                        output = self.avd.shell("am broadcast -a %s -n %s/%s" %
	                                                (intent['action'], self.apk.get_package(), receiver["name"]))

	                    if "Broadcast completed" not in output:

        	                logs += "$ adb shell am broadcast -a %s -c %s -n %s/%s\n%s\n" % \
	                                (intent['action'], intent["category"], self.apk.get_package(), receiver["name"], output)
			    else:
				receiver["vulnerable"] = False

                if not len(receiver["intent_filters"]) and self.avd is not None:
                    for category in categories:
                        for action in actions:
                            #2. Fuzz receivers with a set of intents (Null intents, malformed, ...)
                            output = self.avd.shell("am broadcast -a %s -c %s -n %s/%s" %
                                                    (action, category, self.apk.get_package(), receiver["name"]))
                            if "Broadcast completed" not in output:
                                print output
                                logs += "$ adb shell am broadcast -a %s -c %s -n %s/%s\n%s\n" % \
                                        (action, category, self.apk.get_package(), receiver["name"], output)
                                receiver["vulnerable"] = True
	    if receiver["vulnerable"] is True:
		vulnerable = True

        return {
            "results": receivers,
            "vulnerabilities": [
                framework.Vulnerability(
                    "Unprotected broadcast receiver.",
                    "The following broadcast receivers were found to be vulnerable.",
                    framework.Vulnerability.LOW,
                    resources=[r for r in receivers if r["vulnerable"]],
                    logs=logs
            ).__dict__] if vulnerable else []
        }
Exemple #3
0
    def module_run(self, verbose=False):

        results = []

        z = self.apk.vm_analysis.tainted_packages.search_methods(
            ".", "rawQuery", ".")
        z += self.apk.vm_analysis.tainted_packages.search_methods(
            ".", "query", ".")

        for p in z:
            method = self.apk.dalvik_vm_format.get_method_by_idx(
                p.get_src_idx())
            if method.get_code() is None:
                continue
            mx = self.apk.vm_analysis.get_method(method)
            if self.apk.get_package() in method.get_class_name().replace(
                    "/", "."):
                ms = decompile.DvMethod(mx)
                try:
                    ms.process()
                except AttributeError as e:
                    self.warning(
                        "Error while processing disassembled Dalvik method: %s"
                        % e.message)
                if method.get_class_name()[1:-1] not in [
                        x["file"] for x in results
                ]:
                    results.append({
                        "file":
                        method.get_class_name()[1:-1],
                        "lines": [method.get_debug().get_line_start()]
                    })
                else:
                    for r in results:
                        if r["file"] == method.get_class_name()[1:-1]:
                            if method.get_debug().get_line_start(
                            ) not in r["lines"]:
                                r["lines"].append(
                                    method.get_debug().get_line_start())

        return {
            "results":
            results,
            "vulnerabilities": [
                framework.Vulnerability(
                    "Multiple SQL injection vectors.",
                    "The application do not make use of prepared statement which could lead to SQL injection vulnerabilities.\n"
                    "Review the results to see if these raw queries can be exploited.",
                    framework.Vulnerability.MEDIUM,
                    resources=results).__dict__
            ] if len(results) else []
        }
Exemple #4
0
    def module_run(self, verbose=False):

        logs = ""
        activities = self.get_activities()
        vulnerable = False

        for activity in activities:
            launcher = False
            activity["vulnerable"] = False
            for intfilter in activity["intent_filters"]:
                if intfilter["action"] == "android.intent.action.MAIN" and \
                        intfilter["category"] == "android.intent.category.LAUNCHER":
                    launcher = True

            if not launcher and (activity["exported"]
                                 and activity["permission"] is None):
                activity["vulnerable"] = True
                vulnerable = True
                if self.avd is not None:
                    output = self.avd.shell(
                        "am start -n %s/%s" %
                        (self.apk.get_package(), activity["name"]))
                    time.sleep(1)
                    self.avd.screenshot("%s/analysis/%s/screenshots/%s.png" %
                                        (self.root_dir, self.apk.get_package(),
                                         activity["name"]))
                    activity["screenshot"] = "screenshots/%s.png" % (
                        activity["name"])
                    logs += "$ adb shell am start -n %s/%s\n%s\n" % (
                        self.apk.get_package(), activity["name"], output)
                    if "Error" in output:
                        activity["vulnerable"] = False

        return {
            "results":
            activities,
            "vulnerabilities": [
                framework.Vulnerability(
                    "Potentially vulnerable activity components.",
                    "The following activities were found to be vulnerable.",
                    framework.Vulnerability.LOW,
                    resources=[a for a in activities if a["vulnerable"]],
                    logs=logs).__dict__
            ] if vulnerable else []
        }
Exemple #5
0
    def module_run(self, verbose=False):
        services = self.get_services()
        vulnerable = False
        logs = ""
        for service in services:
            service["vulnerable"] = False
            if service[
                    "exported"] and service["permission"] is None and not len(
                        service['intent_filters']):
                if self.avd is not None:
                    output = self.avd.shell(
                        "am startservice -n %s/%s" %
                        (self.apk.get_package(), service["name"]))
                    logs += "$ adb shell am startservice -n %s/%s\n%s\n" % (
                        self.apk.get_package(), service["name"], output)
                    if "Error: Not found; no service started." not in output:
                        service["vulnerable"] = True
                        vulnerable = True
                    #force-stop was only introduced in honeycomb
                    if self.avd.target > 12:
                        output = self.avd.shell("am force-stop %s" %
                                                (self.apk.get_package()))
                        logs += "$ adb shell am force-stop %s \n%s" % (
                            self.apk.get_package(), output)
        if verbose:
            print logs

        return {
            "results":
            services,
            "vulnerabilities": [
                framework.Vulnerability(
                    "Potentially vulnerable service component.",
                    "The following services were found to be vulnerable.",
                    framework.Vulnerability.LOW,
                    resources=[s for s in services if s["vulnerable"]],
                    logs=logs).__dict__
            ] if vulnerable else []
        }
Exemple #6
0
    def module_run(self, verbose=False):

        webviews = []
        vulnerable = False
        funcs = [{
            "name": "setJavaScriptEnabled",
            "default": False,
            "description": "Allows the WebView to execute Javascript."
        }, {
            "name":
            "setPluginsEnabled"
            if self.apk.get_min_sdk_version() < 8 else "setPluginState",
            "default":
            True,
            "description":
            "Allow the loading of plugins (ie. Flash)."
        }, {
            "name":
            "setAllowContentAccess",
            "default":
            True,
            "description":
            "WebView has access to content providers on the system."
        }, {
            "name":
            "setAllowFileAccess",
            "default":
            True,
            "description":
            "Allows a WebView to load content from the filesystem using file:// scheme."
        }, {
            "name":
            "setAllowFileAccessFromFileURLS",
            "default":
            True if self.apk.get_min_sdk_version() <= 15 else False,
            "description":
            "Allows the HTML file that was loaded using file:// scheme to access "
            "other files on the system"
        }, {
            "name":
            "setAllowUniversalAccessFromFilesURLS",
            "default":
            True if self.apk.get_min_sdk_version() <= 15 else False,
            "description":
            "Allows the HTML file that was loaded using file:// to "
            "access content from any origin (including other files)."
        }, {
            "name": "setSavePassword",
            "default": True,
            "description": "The WebView will save entered passwords."
        }]

        z = self.apk.vm_analysis.tainted_packages.search_packages("WebView")

        for p in z:
            method = self.apk.dalvik_vm_format.get_method_by_idx(
                p.get_src_idx())
            if method.get_code() is None:
                continue
            if method.get_class_name()[1:-1] not in [
                    x["file"] for x in webviews
            ]:
                webview = {
                    "file": method.get_class_name()[1:-1],
                    "line": method.get_debug().get_line_start()
                }
                mx = self.apk.vm_analysis.get_method(method)
                ms = decompile.DvMethod(mx)

                try:
                    ms.process()
                except AttributeError as e:
                    self.warning(
                        "Error while processing disassembled Dalvik method: %s"
                        % e.message)

                source = ms.get_source()
                for func in funcs:
                    matches = re.findall(r'%s\((.*?)\);' % func["name"],
                                         source)
                    if len(matches) == 1:
                        webview[func["name"]] = True if matches[
                            0] == "1" or matches[0] == "true" else False
                    else:
                        webview[func["name"]] = func["default"]
                webviews.append(webview)

        for webview in webviews:
            if webview["setJavaScriptEnabled"]:
                vulnerable = True

        return {
            "results":
            webviews,
            "logs":
            "",
            "vulnerabilities": [
                framework.Vulnerability(
                    "Explicitly enabled Javascript in WebViews",
                    "The application explicitly enable Javascript for multiple webviews.",
                    framework.Vulnerability.MEDIUM,
                    resources=webviews).__dict__
            ] if vulnerable else []
        }
Exemple #7
0
    def module_run(self, verbose=False):

        result = None

        file_list = self.apk.zip.namelist()
        p_find_cert = re.compile('^(META-INF/.*?(RSA$|DSA$))$')
        cert_found = ''

        for i in file_list:
            if p_find_cert.match(i):
                cert_found = p_find_cert.match(i).groups()[0]

        if cert_found:
            with open("/tmp/%s.cert" % self.apk.get_package(), "wb") as f:
                rsa = self.apk.get_file(cert_found)
                f.write(rsa)

            p = os.popen(
                "openssl pkcs7 -inform DER -in /tmp/%s.cert -out /tmp/%s.pem -outform PEM -print_certs"
                % (self.apk.get_package(), self.apk.get_package()))
            output = p.read()
            p.close()
            if not output:
                x509 = X509.load_cert('/tmp/%s.pem' % self.apk.get_package())
                dates = re.findall(
                    r'Not Before: ([^\n]*)\n            Not After : ([^\n]*)\n',
                    x509.as_text())
                result = {
                    "fingerprint":
                    x509.get_fingerprint("sha1"),
                    "issuer":
                    x509.get_issuer().as_text(),
                    'not_before':
                    dates[0][0],
                    'not_after':
                    dates[0][1],
                    "pubkey":
                    x509.get_pubkey().get_rsa().as_pem()
                    if cert_found.endswith("RSA") else None,
                    "serial_number":
                    x509.get_serial_number(),
                    "subject":
                    x509.get_subject().as_text(),
                    "version":
                    x509.get_version(),
                    "text":
                    x509.as_text(),
                    "verified":
                    x509.verify()
                }
            else:
                self.error("An error occured while parsing the file.")
        else:
            self.error("Certificate not found.")

        if verbose:
            print "\n%s" % result["text"]

        vulnerabilities = []

        d = datetime.datetime.strptime(result["not_after"],
                                       "%b %d %H:%M:%S %Y %Z")
        t = calendar.timegm(d.timetuple())
        if t < int(time.time()):
            vulnerabilities.append(
                framework.Vulnerability(
                    "Certificate has expired.",
                    "The application certificate has expired.",
                    framework.Vulnerability.INFO).__dict__)

        if result is not None and result["verified"] is False:
            vulnerabilities.append(
                framework.Vulnerability(
                    "Certificate is not verified.",
                    "The application certificate could not be verified.",
                    framework.Vulnerability.INFO).__dict__)

        if result is not None and "Android Debug" in result["issuer"]:
            vulnerabilities.append(
                framework.Vulnerability(
                    "Debug certificate.",
                    "The application has been packaged with a debug certificate.",
                    framework.Vulnerability.INFO,
                    logs=result["issuer"]).__dict__)

        if result is not None and result["verified"] is True:
            vulnerabilities.append(
                framework.Vulnerability(
                    "Certificate is verified.",
                    "The application certificate has been verified.",
                    framework.Vulnerability.INFO).__dict__)

        return {"results": result, "vulnerabilities": vulnerabilities}
Exemple #8
0
    def module_run(self, verbose=False):
        logs = ""
        xml = self.apk.get_android_manifest_xml()
        debuggable = False
        for a in xml.getElementsByTagName("application"):
            if a.getAttribute('android:debuggable') is not None:
                debuggable = True if a.getAttribute(
                    'android:debuggable') == 'true' else False

        if debuggable and self.avd is not None:

            #launch the app
            activities = self.get_activities()
            for activity in activities:
                for intfilter in activity["intent_filters"]:
                    if intfilter["action"] == "android.intent.action.MAIN" and \
                            intfilter["category"] == "android.intent.category.LAUNCHER":
                        output = self.avd.shell(
                            "am start -n %s/%s" %
                            (self.apk.get_package(), activity["name"]))
                        logs += "$ adb shell am start -n %s/%s\n%s\n" % (
                            self.apk.get_package(), activity["name"], output)
                        break

            #find application's process
            pid = 0
            output = self.avd.shell("ps | grep %s" % self.apk.get_package())
            logs += "$ adb shell ps | grep %s\n %s\n" % (
                self.apk.get_package(), output)
            for line in output.split("\n"):
                s = line.split()
                if len(s) == 9 and s[8] == self.apk.get_package():
                    pid = int(s[1])
            if pid is None:
                self.error(
                    "The application is not running. Can't confirm debuggable status."
                )
            else:
                #forward jdwp and connect remotely with jdb
                logs += "$ adb forward tcp:54321 jdwp:%d\n" % pid
                p = os.popen(
                    "adb -s emulator-%d forward tcp:54321 jdwp:%d" %
                    (self.avd._id, pid), )
                output = p.read()
                p.close()
                if "Error:" in output:
                    raise Exception(output)
                else:
                    p = os.popen(
                        "echo 'classes' | jdb -attach localhost:54321 | grep %s"
                        % self.apk.get_package(), )
                    output = p.read()
                    p.close()
                    logs += "$ jdb -attach localhost:54321\n> classes\n%s" % output
                    if "Error" in output:
                        debuggable = False
                    else:
                        if "Unable to attach to target VM." in output:
                            debuggable = False

        if verbose:
            print logs

        return {
            "results":
            debuggable,
            "vulnerabilities": [
                framework.Vulnerability(
                    "The application is debuggable",
                    "The application is set to debuggable. A malicious application can subvert the integrity and "
                    "confidentiality of the vulnerable application by connecting to its debug port.\n"
                    "The application can be easily reversed engineered.",
                    framework.Vulnerability.HIGH,
                    logs=logs).__dict__
            ] if debuggable else []
        }
Exemple #9
0
    def module_run(self, verbose=False):
        xml = self.apk.get_android_manifest_xml()
        results = {"allow_backup": False, "backup_agent": None}
        for a in xml.getElementsByTagName("application"):
            if a.getAttribute('android:allowBackup') is not None:
                results["allow_backup"] = True if a.getAttribute(
                    'android:allowBackup') == 'true' else False
                if results["allow_backup"]:
                    results["backup_agent"] = a.getAttribute(
                        'android:backupAgent')

        if results["allow_backup"] and self.avd is not None:
            try:
                self.output("Application allow backup. Backing up data ...")
                if not self.avd.headless:
                    backup_location = "%s/analysis/%s/storage/backup" % (
                        self.root_dir, self.apk.get_package())
                    if not os.path.exists(backup_location):
                        os.mkdir(backup_location)
                    if self.avd.backup(self.apk.get_package(),
                                       location="%s/backup.ab" %
                                       backup_location):
                        self.output(
                            str("Package backed up to %s, decompressing ..." %
                                backup_location))

                        #zlib decompression
                        ab_file = open("%s/backup.ab" % backup_location, "rb")
                        tar_file = open("%s/backup.tar" % backup_location,
                                        "wb")
                        ab_file.read(24)
                        tar_file.write(zlib.decompress(ab_file.read()))
                        tar_file.close()
                        ab_file.close()

                        #tar decompression
                        tar = tarfile.open("%s/backup.tar" % backup_location)
                        tar.extractall(path=backup_location)
                        tar.close()
                    else:
                        self.warning(
                            "An error occured when trying to backup %s" %
                            self.apk.get_package())
                else:
                    self.warning(
                        "Emulator running in headless mode. Can't run automated backup script."
                    )
            except Exception as e:
                self.warning(str(e.message))

        return {
            "results":
            results,
            "vulnerabilities": [
                framework.Vulnerability(
                    "The application allow backups.",
                    "The allowBackup attribute determines if an application's data can be backed up and restored.\n"
                    "By default, this flag is set to true. When this flag is set to true, application data can be backed up"
                    " and restored by the user using adb backup and adb restore. This may have security consequences for an"
                    " application. adb backup allows users who have enabled USB debugging to copy application data off of"
                    " the device. Once backed up, all application data can be read by the user. adb restore allows creation"
                    " of application data from a source specified by the user. Following a restore, applications should not"
                    " assume that the data, file permissions, and directory permissions were created by the application"
                    " itself. Setting allowBackup=\"false\" opts an application out of both backup and restore.",
                    framework.Vulnerability.LOW).__dict__
            ] if results["allow_backup"] is True else []
        }
Exemple #10
0
    def module_run(self, verbose=False):

        #proguard detection
        proguard = False
        for root, dirs, files in os.walk(
                "%s/analysis/%s/code/decompiled/%s" %
            (self.root_dir, self.apk.get_package(), "/".join(
                self.apk.get_package().split(".")))):
            for f in files:
                if f in ["%s.java" % x for x in string.ascii_lowercase]:
                    proguard = True

        #dexguard detection

        #1. use of unicode/chinese characters
        chinese_filenames = 0
        for root, dirs, files in os.walk(
                "%s/analysis/%s/smali" %
            (self.root_dir, self.apk.get_package())):
            for f in files:
                for c in f:
                    if u'\u4e00' <= c <= u'\u9fff':
                        chinese_filenames += 1

        chinese_chars = 0
        for root, dirs, files in os.walk(
                "%s/analysis/%s/smali" %
            (self.root_dir, self.apk.get_package())):
            for filename in files:
                with codecs.open(os.path.join(root, filename), "rb",
                                 "utf-8") as f:
                    for c in f.read():
                        if u'\u4e00' <= c <= u'\u9fff':
                            chinese_chars += 1

        #2. Usage of huge arrays (> 1900 bytes)
        huge_arrays = 0
        for root, dirs, files in os.walk(
                "%s/analysis/%s/smali" %
            (self.root_dir, self.apk.get_package())):
            for filename in files:
                with open(os.path.join(root, filename), 'rb') as f:
                    matches = re.findall(
                        r'new-array ([^,]*),([^,]*),([^\n]*)\n', f.read())
                    if len(matches):
                        if matches[0][1] > 1900:
                            huge_arrays += 1

        #3. Heavy use of reflection
        reflection = 0
        for root, dirs, files in os.walk(
                "%s/analysis/%s/smali" %
            (self.root_dir, self.apk.get_package())):
            for filename in files:
                with open(os.path.join(root, filename), 'rb') as f:
                    matches = re.findall('r(Ljava/lang/reflect/[^;];)',
                                         f.read())
                    reflection += len(matches)

        #4. Dynamic Code Loading and Executing
        dexclassloader = 0
        for root, dirs, files in os.walk(
                "%s/analysis/%s/smali" %
            (self.root_dir, self.apk.get_package())):
            for filename in files:
                with open(os.path.join(root, filename), 'rb') as f:
                    matches = re.findall('r(Ldalvik/system/DexClassLoader;)',
                                         f.read())
                    dexclassloader += len(matches)

        #5. Heavy use of Java’s encryption classes

        #APKProtect detection
        # The string "APKProtected" is present in the dex
        apkprotect = False
        for root, dirs, files in os.walk(
                "%s/analysis/%s/smali" %
            (self.root_dir, self.apk.get_package())):
            for filename in files:
                with open(os.path.join(root, filename), 'rb') as f:
                    if "APKProtected" in f.read():
                        apkprotect = True

        dexguard = (dexclassloader > 0 and chinese_chars > 0
                    and chinese_filenames > 0)

        obfuscator = None
        if dexguard:
            obfuscator = "Dexguard"
        if proguard:
            obfuscator = "Proguard"
        if apkprotect:
            obfuscator = "APKProtect"

        if verbose and obfuscator is not None:
            print "Obfuscator : %s" % obfuscator

        return {
            "results":
            obfuscator,
            "logs":
            "",
            "vulnerabilities": [
                framework.Vulnerability(
                    "Lack of Code Obfuscation",
                    "Obfuscation raise the bar for third parties that would want to determine how your application is "
                    "working and protect your application against piracy or unwanted clones on the market."
                    "Multiple solutions exists but we recommend you to use Proguard as it is well integrated into the "
                    "Android Studio IDE.",
                    framework.Vulnerability.LOW).__dict__
            ] if obfuscator is None else []
        }