mirror of
https://github.com/MobSF/Mobile-Security-Framework-MobSF.git
synced 2026-10-11 22:50:09 +00:00
* AAB to APK conversion * relative urls fix for recent scan
This commit is contained in:
+1
-1
@@ -10,7 +10,7 @@ from mobsf.install.windows.setup import windows_config_local
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
VERSION = '4.0.1'
|
||||
VERSION = '4.0.2'
|
||||
BANNER = """
|
||||
__ __ _ ____ _____ _ _ ___
|
||||
| \/ | ___ | |__/ ___|| ___|_ _| || | / _ \
|
||||
|
||||
@@ -104,6 +104,7 @@ def get_executable_hashes():
|
||||
settings.JTOOL_BINARY,
|
||||
settings.CLASSDUMP_BINARY,
|
||||
settings.CLASSDUMP_SWIFT_BINARY,
|
||||
getattr(settings, 'BUNDLE_TOOL', ''),
|
||||
]
|
||||
for ubin in user_defined_bins:
|
||||
if ubin:
|
||||
@@ -130,9 +131,9 @@ def store_exec_hashes_at_first_run():
|
||||
hashes['signature'] = signature
|
||||
EXECUTABLE_HASH_MAP = hashes
|
||||
except Exception:
|
||||
logger.warning('Cannot calculate executable hashes, '
|
||||
'disabling runtime executable '
|
||||
'tampering detection')
|
||||
logger.exception('Cannot calculate executable hashes, '
|
||||
'disabling runtime executable '
|
||||
'tampering detection')
|
||||
|
||||
|
||||
def subprocess_hook(oldfunc, *args, **kwargs):
|
||||
@@ -149,6 +150,7 @@ def subprocess_hook(oldfunc, *args, **kwargs):
|
||||
for arg in agmtz:
|
||||
if arg.endswith('.jar'):
|
||||
exec2 = Path(arg).as_posix()
|
||||
break
|
||||
if '/' in exec1 or '\\' in exec1:
|
||||
exec1 = Path(exec1).as_posix()
|
||||
else:
|
||||
|
||||
@@ -86,6 +86,7 @@ APK_MIME = [
|
||||
'application/x-zip-compressed',
|
||||
'binary/octet-stream',
|
||||
'application/java-archive',
|
||||
'application/x-authorware-bin',
|
||||
]
|
||||
IPA_MIME = [
|
||||
'application/iphone',
|
||||
@@ -107,7 +108,13 @@ APPX_MIME = [
|
||||
'application/vns.ms-appx',
|
||||
'application/x-zip-compressed',
|
||||
]
|
||||
|
||||
# Supported File Extensions
|
||||
ANDROID_EXTS = (
|
||||
'apk', 'xapk', 'apks', 'zip',
|
||||
'aab', 'so', 'jar', 'aar',
|
||||
)
|
||||
IOS_EXTS = ('ipa', 'dylib', 'a')
|
||||
WINDOWS_EXTS = ('appx',)
|
||||
# REST API only mode
|
||||
# Set MOBSF_API_ONLY to 1 to enable REST API only mode
|
||||
# In this mode, web UI related urls are disabled.
|
||||
@@ -420,6 +427,7 @@ else:
|
||||
"""
|
||||
|
||||
# Android 3P Tools
|
||||
BUNDLE_TOOL = os.getenv('MOBSF_BUNDLE_TOOL', '')
|
||||
JADX_BINARY = os.getenv('MOBSF_JADX_BINARY', '')
|
||||
BACKSMALI_BINARY = os.getenv('MOBSF_BACKSMALI_BINARY', '')
|
||||
VD2SVG_BINARY = os.getenv('MOBSF_VD2SVG_BINARY', '')
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
"""MobSF REST API V 1."""
|
||||
from django.http import HttpResponse
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from django.conf import settings
|
||||
|
||||
from mobsf.StaticAnalyzer.models import (
|
||||
RecentScansDB,
|
||||
@@ -66,7 +67,7 @@ def api_scan(request):
|
||||
{'error': 'The file is not uploaded/available'}, 500)
|
||||
scan_type = robj[0].SCAN_TYPE
|
||||
# APK, Source Code (Android/iOS) ZIP, SO, JAR, AAR
|
||||
if scan_type in {'xapk', 'apk', 'apks', 'zip', 'so', 'jar', 'aar'}:
|
||||
if scan_type in settings.ANDROID_EXTS:
|
||||
resp = static_analyzer(request, checksum, True)
|
||||
if 'type' in resp:
|
||||
resp = static_analyzer_ios(request, checksum, True)
|
||||
@@ -75,14 +76,14 @@ def api_scan(request):
|
||||
else:
|
||||
response = make_api_response(resp, 200)
|
||||
# IPA
|
||||
elif scan_type in {'ipa', 'dylib', 'a'}:
|
||||
elif scan_type in settings.IOS_EXTS:
|
||||
resp = static_analyzer_ios(request, checksum, True)
|
||||
if 'error' in resp:
|
||||
response = make_api_response(resp, 500)
|
||||
else:
|
||||
response = make_api_response(resp, 200)
|
||||
# APPX
|
||||
elif scan_type == 'appx':
|
||||
elif scan_type in settings.WINDOWS_EXTS:
|
||||
resp = windows.staticanalyzer_windows(request, checksum, True)
|
||||
if 'error' in resp:
|
||||
response = make_api_response(resp, 500)
|
||||
|
||||
@@ -44,6 +44,7 @@ class FileType(object):
|
||||
or self.is_ipa()
|
||||
or self.is_appx()
|
||||
or self.is_apks()
|
||||
or self.is_aab()
|
||||
or self.is_jar()
|
||||
or self.is_aar()):
|
||||
return True
|
||||
@@ -57,6 +58,10 @@ class FileType(object):
|
||||
return (self.file_type in settings.APK_MIME
|
||||
and self.file_name_lower.endswith('.xapk'))
|
||||
|
||||
def is_aab(self):
|
||||
return (self.file_type in settings.APK_MIME
|
||||
and self.file_name_lower.endswith('.aab'))
|
||||
|
||||
def is_apk(self):
|
||||
return (self.file_type in settings.APK_MIME
|
||||
and self.file_name_lower.endswith('.apk'))
|
||||
|
||||
@@ -61,9 +61,13 @@ def index(request):
|
||||
+ settings.IPA_MIME
|
||||
+ settings.ZIP_MIME
|
||||
+ settings.APPX_MIME)
|
||||
exts = (settings.ANDROID_EXTS
|
||||
+ settings.IOS_EXTS
|
||||
+ settings.WINDOWS_EXTS)
|
||||
context = {
|
||||
'version': settings.MOBSF_VER,
|
||||
'mimes': mimes,
|
||||
'exts': '|'.join(exts),
|
||||
}
|
||||
template = 'general/home.html'
|
||||
return render(request, template, context)
|
||||
@@ -154,6 +158,8 @@ class Upload(object):
|
||||
return scanning.scan_xapk()
|
||||
elif self.file_type.is_apks():
|
||||
return scanning.scan_apks()
|
||||
elif self.file_type.is_aab():
|
||||
return scanning.scan_aab()
|
||||
elif self.file_type.is_jar():
|
||||
return scanning.scan_jar()
|
||||
elif self.file_type.is_aar():
|
||||
|
||||
@@ -98,6 +98,15 @@ class Scanning(object):
|
||||
logger.info('Performing Static Analysis of Android Split APK')
|
||||
return self.data
|
||||
|
||||
def scan_aab(self):
|
||||
"""Android App Bundle."""
|
||||
md5 = handle_uploaded_file(self.file, '.aab')
|
||||
self.data['hash'] = md5
|
||||
self.data['scan_type'] = 'aab'
|
||||
add_to_recent_scan(self.data)
|
||||
logger.info('Performing Static Analysis of Android App Bundle')
|
||||
return self.data
|
||||
|
||||
def scan_jar(self):
|
||||
"""Java JAR file."""
|
||||
md5 = handle_uploaded_file(self.file, '.jar')
|
||||
|
||||
@@ -14,17 +14,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
RESCAN = False
|
||||
# Set RESCAN to True if Static Analyzer Code is modified
|
||||
EXTS = (
|
||||
'.xapk',
|
||||
'.apk',
|
||||
'.ipa',
|
||||
'.appx',
|
||||
'.zip',
|
||||
'.a',
|
||||
'.so',
|
||||
'.dylib',
|
||||
'.aar',
|
||||
'.jar')
|
||||
EXTS = settings.ANDROID_EXTS + settings.IOS_EXTS + settings.WINDOWS_EXTS
|
||||
|
||||
|
||||
def static_analysis_test():
|
||||
|
||||
Binary file not shown.
@@ -71,6 +71,7 @@ from mobsf.StaticAnalyzer.views.android.strings import (
|
||||
get_strings_metadata,
|
||||
)
|
||||
from mobsf.StaticAnalyzer.views.android.xapk import (
|
||||
handle_aab,
|
||||
handle_split_apk,
|
||||
handle_xapk,
|
||||
)
|
||||
@@ -131,12 +132,9 @@ def static_analyzer(request, checksum, api=False):
|
||||
api)
|
||||
typ = robj[0].SCAN_TYPE
|
||||
filename = robj[0].FILE_NAME
|
||||
allowed_exts = (
|
||||
'.apk', '.xapk', '.zip', '.apks',
|
||||
'.jar', '.aar', '.so')
|
||||
allowed_typ = [i.replace('.', '') for i in allowed_exts]
|
||||
allowed_exts = tuple(f'.{i}' for i in settings.ANDROID_EXTS)
|
||||
if (not filename.lower().endswith(allowed_exts)
|
||||
or typ not in allowed_typ):
|
||||
or typ not in settings.ANDROID_EXTS):
|
||||
return print_n_send_error_response(
|
||||
request,
|
||||
'Invalid file extension or file type',
|
||||
@@ -163,6 +161,11 @@ def static_analyzer(request, checksum, api=False):
|
||||
if not handle_split_apk(app_dic):
|
||||
raise Exception('Invalid Split APK File')
|
||||
typ = 'apk'
|
||||
elif typ == 'aab':
|
||||
# Convert AAB to APK
|
||||
if not handle_aab(app_dic):
|
||||
raise Exception('Invalid AAB File')
|
||||
typ = 'apk'
|
||||
if typ == 'apk':
|
||||
app_dic['app_file'] = app_dic['md5'] + '.apk' # NEW FILENAME
|
||||
app_dic['app_path'] = (
|
||||
@@ -504,6 +507,7 @@ def static_analyzer(request, checksum, api=False):
|
||||
err = ('Only APK, JAR, AAR, SO and Zipped '
|
||||
'Android/iOS Source code supported now!')
|
||||
logger.error(err)
|
||||
raise Exception(err)
|
||||
except Exception as excep:
|
||||
logger.exception('Error Performing Static Analysis')
|
||||
msg = str(excep)
|
||||
|
||||
@@ -1,11 +1,19 @@
|
||||
# -*- coding: utf_8 -*-
|
||||
"""Handle XAPK File."""
|
||||
import logging
|
||||
import subprocess
|
||||
from json import load
|
||||
from shutil import move
|
||||
from pathlib import Path
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
from mobsf.StaticAnalyzer.views.common.shared_func import unzip
|
||||
from mobsf.MobSF.utils import is_safe_path
|
||||
from mobsf.MobSF.utils import (
|
||||
find_java_binary,
|
||||
is_file_exists,
|
||||
is_safe_path,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -47,11 +55,62 @@ def handle_split_apk(app_dic):
|
||||
manifest = app_dic['app_dir'] / 'AndroidManifest.xml'
|
||||
if manifest.exists():
|
||||
return True
|
||||
files = unzip(apks.as_posix(), app_dic['app_dir'])
|
||||
for apk in files:
|
||||
if not apk.startswith('config.') and apk.endswith('.apk'):
|
||||
full_path = app_dic['app_dir'] / apk
|
||||
if is_safe_path(app_dic['app_dir'], full_path):
|
||||
move(full_path, apks)
|
||||
for apk in unzip(apks.as_posix(), app_dic['app_dir']):
|
||||
full_path = app_dic['app_dir'] / apk
|
||||
safe_path = is_safe_path(app_dic['app_dir'], full_path)
|
||||
if (not apk.startswith('config.')
|
||||
and apk.endswith('.apk')
|
||||
and safe_path):
|
||||
move(full_path, apks)
|
||||
return True
|
||||
return None
|
||||
|
||||
|
||||
def handle_aab(app_dic):
|
||||
"""Convert AAB to APK using bundletool."""
|
||||
try:
|
||||
checksum = app_dic['md5']
|
||||
aab_path = app_dic['app_dir'] / f'{checksum}.aab'
|
||||
apks = aab_path.with_suffix('.apks')
|
||||
apk = aab_path.with_suffix('.apk')
|
||||
tools_dir = app_dic['tools_dir']
|
||||
# Check if previously converted
|
||||
manifest = app_dic['app_dir'] / 'AndroidManifest.xml'
|
||||
if manifest.exists():
|
||||
return True
|
||||
logger.info('Converting AAB to APK')
|
||||
if (getattr(settings, 'BUNDLE_TOOL', '')
|
||||
and len(settings.BUNDLE_TOOL) > 0
|
||||
and is_file_exists(settings.BUNDLE_TOOL)):
|
||||
bundletool = settings.BUNDLE_TOOL
|
||||
else:
|
||||
bundletool = Path(tools_dir) / 'bundletool-all-1.16.0.jar'
|
||||
bundletool = bundletool.as_posix()
|
||||
args = [
|
||||
find_java_binary(),
|
||||
'-jar',
|
||||
bundletool,
|
||||
'build-apks',
|
||||
f'--bundle={aab_path.as_posix()}',
|
||||
f'--output={apks.as_posix()}',
|
||||
'--mode=universal',
|
||||
]
|
||||
if not apks.exists() and aab_path.exists():
|
||||
# Convert AAB to APKS
|
||||
subprocess.run(args, timeout=300)
|
||||
# Remove AAB
|
||||
aab_path.unlink()
|
||||
# Extract APK from APKS
|
||||
for apk_file in unzip(apks.as_posix(), app_dic['app_dir']):
|
||||
full_path = app_dic['app_dir'] / apk_file
|
||||
safe_path = is_safe_path(app_dic['app_dir'], full_path)
|
||||
if apk_file == 'universal.apk' and safe_path:
|
||||
move(full_path, apk)
|
||||
apks.unlink()
|
||||
return True
|
||||
raise Exception('Unable to convert AAB to APK')
|
||||
except subprocess.TimeoutExpired:
|
||||
logger.warning('Converting AAB to APK timed out')
|
||||
except Exception:
|
||||
logger.exception('Converting AAB to APK')
|
||||
return None
|
||||
|
||||
@@ -411,12 +411,12 @@ def scan_library(request, checksum):
|
||||
return print_n_send_error_response(request, msg)
|
||||
with open(sfile, 'rb') as f:
|
||||
libchecksum = handle_uploaded_file(f, ext)
|
||||
if ext in ('.ipa', '.dylib', '.a'):
|
||||
if ext in [f'.{i}' for i in settings.IOS_EXTS]:
|
||||
static_analyzer = 'static_analyzer_ios'
|
||||
elif ext == '.appx':
|
||||
# Not applicable, but still set it
|
||||
static_analyzer = 'windows_static_analyzer'
|
||||
elif ext in ('.zip', '.so', '.jar', '.aar', '.apk', '.xapk'):
|
||||
elif ext in [f'.{i}' for i in settings.ANDROID_EXTS]:
|
||||
static_analyzer = 'static_analyzer'
|
||||
else:
|
||||
msg = 'Extension not supported'
|
||||
|
||||
@@ -105,10 +105,11 @@ def static_analyzer_ios(request, checksum, api=False):
|
||||
if file_type == 'dylib' and not Path(filename).suffix:
|
||||
# Force dylib extension on Frameworks
|
||||
filename = f'{filename}.dylib'
|
||||
allowed_exts = ('ios', '.ipa', '.zip', '.dylib', '.a')
|
||||
allowed_typ = [i.replace('.', '') for i in allowed_exts]
|
||||
ios_exts = tuple(f'.{i}' for i in settings.IOS_EXTS)
|
||||
allowed_exts = ios_exts + ('.zip', 'ios')
|
||||
allowed_types = settings.IOS_EXTS + ('zip', 'ios')
|
||||
if (not filename.lower().endswith(allowed_exts)
|
||||
or file_type not in allowed_typ):
|
||||
or file_type not in allowed_types):
|
||||
return print_n_send_error_response(
|
||||
request,
|
||||
'Invalid file extension or file type',
|
||||
|
||||
@@ -91,7 +91,7 @@ def staticanalyzer_windows(request, checksum, api=False):
|
||||
api)
|
||||
typ = robj[0].SCAN_TYPE
|
||||
filename = robj[0].FILE_NAME
|
||||
if typ != 'appx':
|
||||
if typ not in settings.WINDOWS_EXTS:
|
||||
return print_n_send_error_response(
|
||||
request,
|
||||
'File type not supported',
|
||||
|
||||
@@ -215,10 +215,10 @@
|
||||
// Is valid file extensions
|
||||
function isValidExt(file_name){
|
||||
var val = file_name.toLowerCase();
|
||||
var regex = new RegExp("^(.{1,300}?)\.(ipa|apk|apks|xapk|jar|aar|so|dylib|a|zip|appx)$");
|
||||
var regex = new RegExp("^(.{1,300}?)\.({{exts}})$");
|
||||
val = val.replace(/^.*[\\\/]/, '');
|
||||
if (!(regex.test(val))) {
|
||||
_('status').innerText = "MobSF only supports APK, APKS, XAPK, JAR, AAR, SO, IPA, DYLIB, A, ZIP, and APPX files.";
|
||||
_('status').innerText = "MobSF only supports APK, APKS, XAPK, AAB, JAR, AAR, SO, IPA, DYLIB, A, ZIP, and APPX files.";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -67,8 +67,8 @@
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<p><a class="btn btn-primary btn-sm" href="../{{ e.ANALYZER }}/{{e.MD5}}/"><i class="fas fa-eye"></i> Static Report</a>
|
||||
{% if '.apk' == e.FILE_NAME|slice:"-4:" or '.xapk' == e.FILE_NAME|slice:"-5:" or '.apks' == e.FILE_NAME|slice:"-5:"%}
|
||||
<p><a class="btn btn-primary btn-sm" href="../../../{{ e.ANALYZER }}/{{e.MD5}}/"><i class="fas fa-eye"></i> Static Report</a>
|
||||
{% if '.apk' == e.FILE_NAME|slice:"-4:" or '.xapk' == e.FILE_NAME|slice:"-5:" or '.apks' == e.FILE_NAME|slice:"-5:" or '.aab' == e.FILE_NAME|slice:"-4:" %}
|
||||
<a class="btn btn-success btn-sm {% if not e.DYNAMIC_REPORT_EXISTS %}disabled{% endif %}" href="{% url "dynamic_report" checksum=e.MD5 %}"><i class="fa fa-mobile"></i> Dynamic Report</a>
|
||||
{% elif '.ipa' == e.FILE_NAME|slice:"-4:" %}
|
||||
{% if e.PACKAGE_NAME %}
|
||||
@@ -86,6 +86,7 @@
|
||||
{% if '.apk' == e.FILE_NAME|slice:"-4:"%}<i class="fab fa-android fa-3x"></i>
|
||||
{% elif '.xapk' == e.FILE_NAME|slice:"-5:"%}<i class="fab fa-android fa-3x"></i>
|
||||
{% elif '.apks' == e.FILE_NAME|slice:"-5:"%}<i class="fab fa-android fa-3x"></i>
|
||||
{% elif '.aab' == e.FILE_NAME|slice:"-4:"%}<i class="fab fa-android fa-3x"></i>
|
||||
{% elif '.jar' == e.FILE_NAME|slice:"-4:"%}<i class="fab fa-java fa-3x"></i>
|
||||
{% elif '.aar' == e.FILE_NAME|slice:"-4:"%}<i class="fas fa-table fa-3x"></i>
|
||||
{% elif '.so' == e.FILE_NAME|slice:"-3:"%}<i class="fa fa-th-large fa-3x"></i>
|
||||
@@ -100,9 +101,9 @@
|
||||
<td>{{ e.TIMESTAMP }}</td>
|
||||
<td><p>
|
||||
<a class="btn btn-outline-primary btn-sm" href="{% url "pdf" checksum=e.MD5%}"><i class="fas fa-file-pdf"></i></a>
|
||||
<a class="btn btn-outline-info btn-sm" href="../{{ e.ANALYZER }}/{{e.MD5}}/?rescan=1"><i class="fas fa-sync-alt"></i></a>
|
||||
<a class="btn btn-outline-info btn-sm" href="../../../{{ e.ANALYZER }}/{{e.MD5}}/?rescan=1"><i class="fas fa-sync-alt"></i></a>
|
||||
</p>
|
||||
{% if '.apk' == e.FILE_NAME|slice:"-4:" or '.xapk' == e.FILE_NAME|slice:"-5:" or '.apks' == e.FILE_NAME|slice:"-5:"%}
|
||||
{% if '.apk' == e.FILE_NAME|slice:"-4:" or '.xapk' == e.FILE_NAME|slice:"-5:" or '.apks' == e.FILE_NAME|slice:"-5:" or '.aab' == e.FILE_NAME|slice:"-4:"%}
|
||||
<p><a class="diffButton btn btn-warning btn-sm" id="{{ e.MD5 }}_{{ e.FILE_NAME }}"><i class="fas fa-not-equal"></i> Diff or Compare</a>
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "mobsf"
|
||||
version = "4.0.1"
|
||||
version = "4.0.2"
|
||||
description = "Mobile Security Framework (MobSF) is an automated, all-in-one mobile application (Android/iOS/Windows) pen-testing, malware analysis and security assessment framework capable of performing static and dynamic analysis."
|
||||
keywords = ["mobsf", "mobile security framework", "mobile security", "security tool", "static analysis", "dynamic analysis", "malware analysis"]
|
||||
authors = ["Ajin Abraham <[email protected]>"]
|
||||
|
||||
Reference in New Issue
Block a user