From cbc184f30154a824082d68729b7ea9452d68cd79 Mon Sep 17 00:00:00 2001 From: Joey Jiao Date: Mon, 8 Apr 2019 11:25:00 +0800 Subject: [PATCH 1/9] Add option --log-level to filt output report --- qark/qark.py | 6 ++++-- qark/report.py | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/qark/qark.py b/qark/qark.py index 88cf6a2a..35b87214 100644 --- a/qark/qark.py +++ b/qark/qark.py @@ -49,9 +49,11 @@ help="report output path.", show_default=True) @click.option("--keep-report/--no-keep-report", default=False, help="Append to final report file.", show_default=True) +@click.option("--log-level", default=0, + help="Minimum issue output log level, default INFO(0), can be [0(INFO), 1(WARNING), 2(ERROR), 3(VULNERABILITY)].") @click.version_option() @click.pass_context -def cli(ctx, sdk_path, build_path, debug, source, report_type, exploit_apk, report_path, keep_report): +def cli(ctx, sdk_path, build_path, debug, source, report_type, exploit_apk, report_path, keep_report, log_level): if not source: click.secho("Please pass a source for scanning through either --java or --apk") click.secho(ctx.get_help()) @@ -96,7 +98,7 @@ def cli(ctx, sdk_path, build_path, debug, source, report_type, exploit_apk, repo click.secho("Writing report...") report = Report(issues=set(scanner.issues), report_path=report_path, keep_report=keep_report) - report_path = report.generate(file_type=report_type) + report_path = report.generate(file_type=report_type, log_level=log_level) click.secho("Finish writing report to {report_path} ...".format(report_path=report_path)) if exploit_apk: diff --git a/qark/report.py b/qark/report.py index f9300283..8731d3de 100644 --- a/qark/report.py +++ b/qark/report.py @@ -47,7 +47,7 @@ def __init__(self, issues=None, report_path=None, keep_report=False): self.report_path = report_path or DEFAULT_REPORT_PATH self.keep_report = keep_report - def generate(self, file_type='html', template_file=None): + def generate(self, file_type='html', template_file=None, log_level=0): """This method uses Jinja2 to generate a standalone HTML version of the report. :param str file_type: The type of file for the report. Defaults to 'html'. @@ -67,7 +67,7 @@ def generate(self, file_type='html', template_file=None): template = jinja_env.get_template('{file_type}_report.jinja'.format(file_type=file_type)) else: template = Template(template_file) - report_file.write(template.render(issues=list(self.issues))) + report_file.write(template.render(issues=[x for x in list(self.issues) if x.severity._value_ >= log_level])) report_file.write('\n') return full_report_path From d05acc6f0fe893d6ea5aff63667ada365a947fe4 Mon Sep 17 00:00:00 2001 From: Joey Jiao Date: Mon, 8 Apr 2019 13:50:32 +0800 Subject: [PATCH 2/9] Add option --disable-plugins to disable plugin for check --- qark/qark.py | 6 ++++-- qark/scanner/scanner.py | 6 +++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/qark/qark.py b/qark/qark.py index 35b87214..1404b30a 100644 --- a/qark/qark.py +++ b/qark/qark.py @@ -51,9 +51,11 @@ help="Append to final report file.", show_default=True) @click.option("--log-level", default=0, help="Minimum issue output log level, default INFO(0), can be [0(INFO), 1(WARNING), 2(ERROR), 3(VULNERABILITY)].") +@click.option("--disable-plugins", default=None, + help="Disable plugins seperated by commas.") @click.version_option() @click.pass_context -def cli(ctx, sdk_path, build_path, debug, source, report_type, exploit_apk, report_path, keep_report, log_level): +def cli(ctx, sdk_path, build_path, debug, source, report_type, exploit_apk, report_path, keep_report, log_level, disable_plugins): if not source: click.secho("Please pass a source for scanning through either --java or --apk") click.secho(ctx.get_help()) @@ -92,7 +94,7 @@ def cli(ctx, sdk_path, build_path, debug, source, report_type, exploit_apk, repo click.secho("Running scans...") path_to_source = decompiler.path_to_source if decompiler.source_code else decompiler.build_directory - scanner = Scanner(manifest_path=decompiler.manifest_path, path_to_source=path_to_source) + scanner = Scanner(manifest_path=decompiler.manifest_path, path_to_source=path_to_source, disable_plugins=disable_plugins.split(",")) scanner.run() click.secho("Finish scans...") diff --git a/qark/scanner/scanner.py b/qark/scanner/scanner.py index dd7abf81..2ee5910a 100644 --- a/qark/scanner/scanner.py +++ b/qark/scanner/scanner.py @@ -20,7 +20,7 @@ class Scanner(object): - def __init__(self, manifest_path, path_to_source): + def __init__(self, manifest_path, path_to_source, disable_plugins=None): """ Creates the scanner. @@ -35,6 +35,8 @@ def __init__(self, manifest_path, path_to_source): self._gather_files() + self.disable_plugins = disable_plugins + def run(self): """ Runs all the plugin checks by category. @@ -59,6 +61,8 @@ def run(self): continue for plugin_name in get_plugins(category): + if plugin_name in self.disable_plugins: + continue plugins.append(plugin_source.load_plugin(plugin_name).plugin) self._run_checks(plugins) From 0ca08b60a7aa5d42fd69a4f5757f4d2d8b6bc65d Mon Sep 17 00:00:00 2001 From: Joey Jiao Date: Mon, 8 Apr 2019 13:51:54 +0800 Subject: [PATCH 3/9] Add section around each issue --- qark/templates/html_report.jinja | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/qark/templates/html_report.jinja b/qark/templates/html_report.jinja index 2c5acde9..3f09fc53 100644 --- a/qark/templates/html_report.jinja +++ b/qark/templates/html_report.jinja @@ -3,9 +3,11 @@

Issues

{% for issue in issues %} +

{{ issue.severity.name }} {{ issue.name }}

{{ issue.description }}

File: {{ issue.file_object }}{% if issue.line_number %}:{{ issue.line_number[0] }}:{{ issue.line_number[1] }} {% endif %}
+
{% endfor %} - \ No newline at end of file + From dc722ff3f25bdf7aca5afcc28e70d71c45cc5a78 Mon Sep 17 00:00:00 2001 From: Joey Jiao Date: Mon, 8 Apr 2019 15:18:55 +0800 Subject: [PATCH 4/9] Scan AndroidManifest.xml even only source code provided --- qark/scanner/scanner.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qark/scanner/scanner.py b/qark/scanner/scanner.py index 2ee5910a..7c3a72c5 100644 --- a/qark/scanner/scanner.py +++ b/qark/scanner/scanner.py @@ -29,7 +29,7 @@ def __init__(self, manifest_path, path_to_source, disable_plugins=None): """ self.files = set() self.issues = [] - self.manifest_path = manifest_path + self.manifest_path = manifest_path if manifest_path else path_to_source + '/AndroidManifest.xml' self.path_to_source = path_to_source From 33e442ce53d4998b16e07363358e8a8406d992d2 Mon Sep 17 00:00:00 2001 From: Joey Jiao Date: Wed, 10 Apr 2019 10:41:52 +0800 Subject: [PATCH 5/9] Fix disable-plugins --- qark/qark.py | 2 +- qark/scanner/scanner.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/qark/qark.py b/qark/qark.py index 1404b30a..06ad070d 100644 --- a/qark/qark.py +++ b/qark/qark.py @@ -51,7 +51,7 @@ help="Append to final report file.", show_default=True) @click.option("--log-level", default=0, help="Minimum issue output log level, default INFO(0), can be [0(INFO), 1(WARNING), 2(ERROR), 3(VULNERABILITY)].") -@click.option("--disable-plugins", default=None, +@click.option("--disable-plugins", default='', help="Disable plugins seperated by commas.") @click.version_option() @click.pass_context diff --git a/qark/scanner/scanner.py b/qark/scanner/scanner.py index 7c3a72c5..cb073492 100644 --- a/qark/scanner/scanner.py +++ b/qark/scanner/scanner.py @@ -20,7 +20,7 @@ class Scanner(object): - def __init__(self, manifest_path, path_to_source, disable_plugins=None): + def __init__(self, manifest_path, path_to_source, disable_plugins=['']): """ Creates the scanner. From 54a9380f366c99f502d9d2e8372ddc44e48e200c Mon Sep 17 00:00:00 2001 From: Joey Jiao Date: Thu, 11 Apr 2019 10:40:24 +0800 Subject: [PATCH 6/9] Add option --min-sdk incase no uses-sdk in manifest --- qark/plugins/helpers.py | 4 ++-- qark/plugins/manifest_helpers.py | 4 +++- qark/qark.py | 6 ++++-- qark/scanner/plugin.py | 4 ++-- qark/scanner/scanner.py | 6 ++++-- 5 files changed, 15 insertions(+), 9 deletions(-) diff --git a/qark/plugins/helpers.py b/qark/plugins/helpers.py index 8836db04..adfa090f 100644 --- a/qark/plugins/helpers.py +++ b/qark/plugins/helpers.py @@ -75,7 +75,7 @@ def valid_method_invocation(method_invocation, method_name, num_arguments): and len(method_invocation.arguments) == num_arguments) -def get_min_sdk_from_files(files, apk_constants=None): +def get_min_sdk_from_files(files, apk_constants=None, min_sdk=None): """ Get the min_sdk from either the `apk_constants` if it exists, or the manifest file in `files` if it exists. If neither exists, return 1 as the default minimum SDK @@ -91,7 +91,7 @@ def get_min_sdk_from_files(files, apk_constants=None): except (KeyError, TypeError): for decompiled_file in files: if decompiled_file.lower().endswith("{separator}androidmanifest.xml".format(separator=os.sep)): - return get_min_sdk(decompiled_file) + return get_min_sdk(decompiled_file, min_sdk=min_sdk) return 1 diff --git a/qark/plugins/manifest_helpers.py b/qark/plugins/manifest_helpers.py index e2737679..24ed9b2d 100644 --- a/qark/plugins/manifest_helpers.py +++ b/qark/plugins/manifest_helpers.py @@ -22,7 +22,7 @@ def get_package_from_manifest(manifest_path): return manifest_xml.getroot().attrib.get("package") -def get_min_sdk(manifest_xml, files=None): +def get_min_sdk(manifest_xml, files=None, min_sdk=None): """ Given the manifest as a `minidom.parse`'d object or path to manifest, try to get the minimum SDK the manifest specifies. @@ -31,6 +31,8 @@ def get_min_sdk(manifest_xml, files=None): :param Set[str] files: list of files received from Scanner :return: int of the version if it exists, else 1 (the default) """ + if min_sdk: + return int(min_sdk) if manifest_xml is None and files: manifest_xml = get_manifest_out_of_files(files) diff --git a/qark/qark.py b/qark/qark.py index 06ad070d..38804717 100644 --- a/qark/qark.py +++ b/qark/qark.py @@ -53,9 +53,11 @@ help="Minimum issue output log level, default INFO(0), can be [0(INFO), 1(WARNING), 2(ERROR), 3(VULNERABILITY)].") @click.option("--disable-plugins", default='', help="Disable plugins seperated by commas.") +@click.option("--min-sdk", default=None, + help="Override min-sdk which getting from AndroidManifest.xml.") @click.version_option() @click.pass_context -def cli(ctx, sdk_path, build_path, debug, source, report_type, exploit_apk, report_path, keep_report, log_level, disable_plugins): +def cli(ctx, sdk_path, build_path, debug, source, report_type, exploit_apk, report_path, keep_report, log_level, disable_plugins, min_sdk): if not source: click.secho("Please pass a source for scanning through either --java or --apk") click.secho(ctx.get_help()) @@ -94,7 +96,7 @@ def cli(ctx, sdk_path, build_path, debug, source, report_type, exploit_apk, repo click.secho("Running scans...") path_to_source = decompiler.path_to_source if decompiler.source_code else decompiler.build_directory - scanner = Scanner(manifest_path=decompiler.manifest_path, path_to_source=path_to_source, disable_plugins=disable_plugins.split(",")) + scanner = Scanner(manifest_path=decompiler.manifest_path, path_to_source=path_to_source, disable_plugins=disable_plugins.split(","), min_sdk=min_sdk) scanner.run() click.secho("Finish scans...") diff --git a/qark/scanner/plugin.py b/qark/scanner/plugin.py index 63ac0193..d1041281 100644 --- a/qark/scanner/plugin.py +++ b/qark/scanner/plugin.py @@ -249,7 +249,7 @@ class ManifestPlugin(BasePlugin): package_name = "PACKAGE_NOT_FOUND" @classmethod - def update_manifest(cls, path_to_manifest): + def update_manifest(cls, path_to_manifest, min_sdk): """Users of this class should call this method instead of changing class attributes directly""" cls.manifest_path = path_to_manifest try: @@ -261,7 +261,7 @@ def update_manifest(cls, path_to_manifest): return try: - cls.min_sdk = get_min_sdk(cls.manifest_path) + cls.min_sdk = get_min_sdk(cls.manifest_path, min_sdk=min_sdk) cls.target_sdk = get_target_sdk(cls.manifest_path) except AttributeError: # manifest path is not set, assume min_sdk and target_sdk diff --git a/qark/scanner/scanner.py b/qark/scanner/scanner.py index cb073492..839424d9 100644 --- a/qark/scanner/scanner.py +++ b/qark/scanner/scanner.py @@ -20,7 +20,7 @@ class Scanner(object): - def __init__(self, manifest_path, path_to_source, disable_plugins=['']): + def __init__(self, manifest_path, path_to_source, disable_plugins=[''], min_sdk=None): """ Creates the scanner. @@ -37,6 +37,8 @@ def __init__(self, manifest_path, path_to_source, disable_plugins=['']): self.disable_plugins = disable_plugins + self.min_sdk = min_sdk + def run(self): """ Runs all the plugin checks by category. @@ -48,7 +50,7 @@ def run(self): if category == "manifest": # Manifest plugins only need to run once, so we run them and continue manifest_plugins = get_plugins(category) - ManifestPlugin.update_manifest(self.manifest_path) + ManifestPlugin.update_manifest(self.manifest_path, self.min_sdk) if ManifestPlugin.manifest_xml is not None: for plugin in [plugin_source.load_plugin(plugin_name).plugin for plugin_name in manifest_plugins]: From 6397d2dab0f8419750a83db969d4a0355b907358 Mon Sep 17 00:00:00 2001 From: Joey Jiao Date: Thu, 11 Apr 2019 10:42:43 +0800 Subject: [PATCH 7/9] Able to build apk from source --- qark/decompiler/decompiler.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/qark/decompiler/decompiler.py b/qark/decompiler/decompiler.py index 216362c2..741669c1 100644 --- a/qark/decompiler/decompiler.py +++ b/qark/decompiler/decompiler.py @@ -68,7 +68,11 @@ def __init__(self, path_to_source, build_directory=None): if os.path.isdir(path_to_source) or is_java_file(path_to_source): self.source_code = True - self.manifest_path = None + if os.path.isdir(path_to_source): + self.manifest_path = os.path.join(path_to_source, 'AndroidManifest.xml') + else: + self.manifest_path = None + self.apk_name = os.path.splitext(os.path.basename(path_to_source))[0].split('/')[-1] log.debug("Decompiler got directory to run on, assuming Java source code") return From 2d646bf0e0ada9d3c56504208488d895b7952146 Mon Sep 17 00:00:00 2001 From: Joey Jiao Date: Fri, 12 Apr 2019 14:54:08 +0800 Subject: [PATCH 8/9] Fix no such file when decompile apk --- qark/decompiler/decompiler.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/qark/decompiler/decompiler.py b/qark/decompiler/decompiler.py index 741669c1..6a4822d0 100644 --- a/qark/decompiler/decompiler.py +++ b/qark/decompiler/decompiler.py @@ -163,13 +163,13 @@ def run_apktool(self): log.debug("APKTool finish executing, trying to move manifest into proper location") # copy valid XML file to correct location - shutil.move(os.path.join(self.build_directory, "apktool", "AndroidManifest.xml"), - os.path.join(self.build_directory, "AndroidManifest.xml")) +# shutil.move(os.path.join(self.build_directory, "apktool", "AndroidManifest.xml"), +# os.path.join(self.build_directory, "AndroidManifest.xml")) log.debug("Manifest moved successfully") log.debug("Removing apktool subdirectory of build") # remove the apktool generated files (only needed manifest file) - shutil.rmtree(os.path.join(self.build_directory, "apktool")) +# shutil.rmtree(os.path.join(self.build_directory, "apktool")) log.debug("Removed apktool directory") return os.path.join(self.build_directory, "AndroidManifest.xml") From 1f5abfe88405d0d99b9146870ab4d3719014b7cb Mon Sep 17 00:00:00 2001 From: Joey Jiao Date: Tue, 25 Jun 2019 09:20:51 +0800 Subject: [PATCH 9/9] Update API to 28 --- qark/apk_builder.py | 2 +- qark/exploit_apk/app/build.gradle | 8 ++++---- qark/plugins/file/api_keys.py | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/qark/apk_builder.py b/qark/apk_builder.py index b9e31f1d..db767fbf 100644 --- a/qark/apk_builder.py +++ b/qark/apk_builder.py @@ -110,7 +110,7 @@ def _build_apk(self): os.chdir(self.exploit_apk_path) write_key_value_to_xml('packageName', self.package_name, self.strings_xml_path) self._write_properties_file({"sdk.dir": self.sdk_path}) - command = "./gradlew assembleDebug" + command = "sh -x ./gradlew assembleDebug" try: subprocess.call(shlex.split(command)) except Exception: diff --git a/qark/exploit_apk/app/build.gradle b/qark/exploit_apk/app/build.gradle index ab7d8bba..e45255bc 100644 --- a/qark/exploit_apk/app/build.gradle +++ b/qark/exploit_apk/app/build.gradle @@ -1,12 +1,12 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 21 - buildToolsVersion "21.1.2" + compileSdkVersion 28 + buildToolsVersion "28.0.3" defaultConfig { applicationId 'com.secbro.qark' minSdkVersion 7 - targetSdkVersion 21 + targetSdkVersion 28 versionCode 1 versionName "1.0" } @@ -26,4 +26,4 @@ dependencies { compile 'com.android.support:appcompat-v7:22.2.1' compile 'com.android.support:design:22.2.1' compile 'com.android.support:recyclerview-v7:22.2.1' -} \ No newline at end of file +} diff --git a/qark/plugins/file/api_keys.py b/qark/plugins/file/api_keys.py index 3988742d..dd1a7228 100644 --- a/qark/plugins/file/api_keys.py +++ b/qark/plugins/file/api_keys.py @@ -11,7 +11,7 @@ API_KEY_REGEX = re.compile(r'(?=.{20,})(?=.+\d)(?=.+[a-z])(?=.+[A-Z])(?=.+[-_])') SPECIAL_CHARACTER_REGEX = re.compile(r'(?=.+[!$%^&*()_+|~=`{}\[\]:<>?,./])') -BLACKLISTED_EXTENSIONS = (".apk", ".dex", ".png", ".jar") +BLACKLISTED_EXTENSIONS = (".apk", ".dex", ".png", ".jar", ".jpg") API_KEY_DESCRIPTION = "Please confirm and investigate the API key to determine its severity."