mirror of
https://github.com/MobSF/Mobile-Security-Framework-MobSF.git
synced 2026-10-12 02:06:59 +00:00
[HOTFIX] add apksigner.jar for reading signatures (#2254)
* Add `apksigner.jar` * Use apksigner to extract signature versions (v1, v2, v3, v4) * Fix: #2120
This commit is contained in:
@@ -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))]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'])
|
||||
|
||||
Generated
+7
-7
@@ -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]
|
||||
|
||||
Reference in New Issue
Block a user