diff --git a/mobsf/StaticAnalyzer/views/android/cert_analysis.py b/mobsf/StaticAnalyzer/views/android/cert_analysis.py index 8d0f51eb..a49d45d7 100755 --- a/mobsf/StaticAnalyzer/views/android/cert_analysis.py +++ b/mobsf/StaticAnalyzer/views/android/cert_analysis.py @@ -6,6 +6,8 @@ import hashlib import logging import os import re +import subprocess +from pathlib import Path from androguard.util import get_certificate_name_string @@ -15,6 +17,10 @@ from oscrypto import asymmetric from django.utils.html import escape +from mobsf.MobSF.utils import ( + find_java_binary, +) + logger = logging.getLogger(__name__) ANDROID_8_1_LEVEL = 27 HIGH = 'high' @@ -85,14 +91,41 @@ def get_pub_key_details(data): return certlist -def apksigtool_cert(apk_path): +def get_signature_versions(app_path, tools_dir): + """Get signature versions using apksigner.""" + v1, v2, v3, v4 = False, False, False, False + try: + logger.info('Getting Signature Versions') + apksigner = Path(tools_dir) / 'apksigner.jar' + args = [find_java_binary(), '-Xmx1024M', + '-Djava.library.path=', '-jar', apksigner, + 'verify', '--verbose', app_path] + out = subprocess.check_output( + args, stderr=subprocess.STDOUT) + out = out.decode('utf-8', 'ignore') + if re.findall(r'v1 scheme \(JAR signing\): true', out): + v1 = True + if re.findall(r'\(APK Signature Scheme v2\): true', out): + v2 = True + if re.findall(r'\(APK Signature Scheme v3\): true', out): + v3 = True + if re.findall(r'\(APK Signature Scheme v4\): true', out): + v4 = True + except Exception: + logger.exception('Failed to get signature versions') + return v1, v2, v3, v4 + + +def apksigtool_cert(apk_path, tools_dir): """Get Human readable certificate with apksigtool.""" certlist = [] certs = [] pub_keys = [] - signed, v1, v2, v3, v4 = False, False, False, False, 'Unknown' + signed = False certs_no = 0 min_sdk = None + v1, v2, v3, v4 = get_signature_versions( + apk_path, tools_dir) try: from apksigtool import ( APKSignatureSchemeBlock, @@ -104,12 +137,6 @@ def apksigtool_cert(apk_path): b = pair.value if isinstance(b, APKSignatureSchemeBlock): signed = True - if b.version == 1: - v1 = True - if b.version == 2: - v2 = True - if b.version == 3: - v3 = True for signer in b.signers: if b.is_v3(): min_sdk = signer.min_sdk @@ -149,19 +176,18 @@ def apksigtool_cert(apk_path): } -def get_cert_data(a): +def get_cert_data(a, app_path, tools_dir): """Get Human readable certificate.""" certlist = [] - signed, v1, v2, v3, v4 = False, False, False, False, 'Unknown' + signed = False + v1, v2, v3, v4 = get_signature_versions( + app_path, tools_dir) if a.is_signed(): signed = True certlist.append('Binary is signed') else: certlist.append('Binary is not signed') certlist.append('Missing certificate') - v1 = a.is_signed_v1() - v2 = a.is_signed_v2() - v3 = a.is_signed_v3() certlist.append(f'v1 signature: {v1}') certlist.append(f'v2 signature: {v2}') certlist.append(f'v3 signature: {v3}') @@ -192,7 +218,7 @@ def get_cert_data(a): } -def cert_info(a, app_path, app_dir, man_dict): +def cert_info(a, app_dic, man_dict): """Return certificate information.""" try: logger.info('Reading Code Signing Certificate') @@ -202,13 +228,15 @@ def cert_info(a, app_path, app_dir, man_dict): summary = {HIGH: 0, WARNING: 0, INFO: 0} if a: - cert_data = get_cert_data(a) + cert_data = get_cert_data( + a, app_dic['app_path'], app_dic['tools_dir']) else: logger.warning('androguard certificate parsing failed,' ' switching to apksigtool') - cert_data = apksigtool_cert(app_path) + cert_data = apksigtool_cert( + app_dic['app_path'], app_dic['tools_dir']) - cert_path = os.path.join(app_dir, 'META-INF/') + cert_path = os.path.join(app_dic['app_dir'], 'META-INF/') if os.path.exists(cert_path): files = [f for f in os.listdir( cert_path) if os.path.isfile(os.path.join(cert_path, f))] diff --git a/mobsf/StaticAnalyzer/views/android/jar_aar.py b/mobsf/StaticAnalyzer/views/android/jar_aar.py index 6eeef93b..21db0d21 100644 --- a/mobsf/StaticAnalyzer/views/android/jar_aar.py +++ b/mobsf/StaticAnalyzer/views/android/jar_aar.py @@ -100,8 +100,7 @@ def common_analysis(request, app_dic, rescan, api, analysis_type): ) cert_dic = cert_info( apk, - app_dic['app_path'], - app_dic['app_dir'], + app_dic, man_data_dic) else: app_dic['manifest_file'] = None diff --git a/mobsf/StaticAnalyzer/views/android/static_analyzer.py b/mobsf/StaticAnalyzer/views/android/static_analyzer.py index 14eebb7b..093faf03 100755 --- a/mobsf/StaticAnalyzer/views/android/static_analyzer.py +++ b/mobsf/StaticAnalyzer/views/android/static_analyzer.py @@ -205,8 +205,7 @@ def static_analyzer(request, api=False): elf_dict = library_analysis(app_dic['app_dir'], 'elf') cert_dic = cert_info( apk, - app_dic['app_path'], - app_dic['app_dir'], + app_dic, man_data_dic) apkid_results = apkid_analysis(app_dic[ 'app_dir'], app_dic['app_path'], app_dic['app_name']) diff --git a/poetry.lock b/poetry.lock index 8ff28292..82346e18 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1301,13 +1301,13 @@ pyasn1 = ">=0.4.6" [[package]] name = "libsast" -version = "2.0.0" +version = "2.0.2" description = "A generic SAST library built on top of semgrep and regex" optional = false python-versions = ">=3.8,<4.0" files = [ - {file = "libsast-2.0.0-py3-none-any.whl", hash = "sha256:424c94abadb9801dc5bb1c9afa8f189a8a4097e0b04b7a857e15da35de76c60b"}, - {file = "libsast-2.0.0.tar.gz", hash = "sha256:476d43d5af63d99ea57c9ece2b5d8b7da113ece18c219a258fecc31b6fcc97bb"}, + {file = "libsast-2.0.2-py3-none-any.whl", hash = "sha256:f9c0a78cad457444af391be355419956abb7019f616437094153efb279ddf4c9"}, + {file = "libsast-2.0.2.tar.gz", hash = "sha256:6465fb44ae0a27a48b398445117f5a785b41b923965a1a3c653e253e2ec29339"}, ] [package.dependencies] @@ -2959,13 +2959,13 @@ testing = ["coverage (>=5.0)", "pytest", "pytest-cover"] [[package]] name = "wcmatch" -version = "8.4.1" +version = "8.5" description = "Wildcard/glob file name matcher." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "wcmatch-8.4.1-py3-none-any.whl", hash = "sha256:3476cd107aba7b25ba1d59406938a47dc7eec6cfd0ad09ff77193f21a964dee7"}, - {file = "wcmatch-8.4.1.tar.gz", hash = "sha256:b1f042a899ea4c458b7321da1b5e3331e3e0ec781583434de1301946ceadb943"}, + {file = "wcmatch-8.5-py3-none-any.whl", hash = "sha256:14554e409b142edeefab901dc68ad570b30a72a8ab9a79106c5d5e9a6d241bd5"}, + {file = "wcmatch-8.5.tar.gz", hash = "sha256:86c17572d0f75cbf3bcb1a18f3bf2f9e72b39a9c08c9b4a74e991e1882a8efb3"}, ] [package.dependencies]