mirror of
https://github.com/MobSF/Mobile-Security-Framework-MobSF.git
synced 2026-10-12 02:06:59 +00:00
[HOTFIX][EFR-08] Dylib + Symbols + Other Features (#2239)
* Dylib analysis support + PDF for iOS Binary * Dylib string extraction * Improved iOS Plist secret extraction * iOS/Android Form Validation QA * Independent Dylib scan * Symbols view for dylib and so * Trackers support for so
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
sonar.sources=.
|
||||
sonar.exclusions=mobsf/static/**/*,mobsf/templates/**/*
|
||||
sonar.sourceEncoding=UTF-8
|
||||
sonar.python.version=3.7, 3.8, 3.9, 3.10, 3.11
|
||||
+1
-1
@@ -10,7 +10,7 @@ from mobsf.install.windows.setup import windows_config_local
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
VERSION = '3.7.3'
|
||||
VERSION = '3.7.4'
|
||||
BANNER = """
|
||||
__ __ _ ____ _____ _____ _____
|
||||
| \/ | ___ | |__/ ___|| ___|_ _|___ /|___ |
|
||||
|
||||
@@ -72,6 +72,7 @@ ALLOWED_EXTENSIONS = {
|
||||
'.jar': 'application/java-archive',
|
||||
'.aar': 'application/octet-stream',
|
||||
'.so': 'application/octet-stream',
|
||||
'.dylib': 'application/octet-stream',
|
||||
}
|
||||
# =============ALLOWED MIMETYPES=================
|
||||
APK_MIME = [
|
||||
@@ -330,6 +331,8 @@ else:
|
||||
DEX2SMALI_ENABLED = os.getenv('MOBSF_DEX2SMALI_ENABLED', '1')
|
||||
# Android Shared Object Binary Analysis
|
||||
SO_ANALYSIS_ENABLED = os.getenv('MOBSF_SO_ANALYSIS_ENABLED', '1')
|
||||
# iOS Dynamic Library Binary Analysis
|
||||
DYLIB_ANALYSIS_ENABLED = os.getenv('MOBSF_DYLIB_ANALYSIS_ENABLED', '1')
|
||||
# =================================================
|
||||
# --------------------------
|
||||
# MALWARE ANALYZER SETTINGS
|
||||
|
||||
+17
-5
@@ -634,6 +634,19 @@ def is_elf_so_magic(file_obj):
|
||||
return bool(magic == b'\x7F\x45\x4C\x46')
|
||||
|
||||
|
||||
def is_dylib_magic(file_obj):
|
||||
magic = file_obj.read(4)
|
||||
file_obj.seek(0, 0)
|
||||
# DYLIB Magic
|
||||
magics = (
|
||||
b'\xFE\xED\xFA\xCE', # 32 bit
|
||||
b'\xCE\xFA\xED\xFE', # 32 bit
|
||||
b'\xFE\xED\xFA\xCF', # 64 bit
|
||||
b'\xCF\xFA\xED\xFE', # 64 bit
|
||||
b'\xCA\xFE\xBA\xBE')
|
||||
return bool(magic in magics)
|
||||
|
||||
|
||||
def disable_print():
|
||||
sys.stdout = open(os.devnull, 'w')
|
||||
|
||||
@@ -706,9 +719,8 @@ def get_android_src_dir(app_dir, typ):
|
||||
|
||||
def settings_enabled(attr):
|
||||
"""Get settings state if present."""
|
||||
disabled = ('', ' ', '""', '" "', '0', '"0"')
|
||||
if not getattr(settings, attr, True):
|
||||
disabled = ('', ' ', '""', '" "', '0', '"0"', False)
|
||||
try:
|
||||
return getattr(settings, attr) not in disabled
|
||||
except Exception:
|
||||
return False
|
||||
if getattr(settings, attr) in disabled:
|
||||
return False
|
||||
return True
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import functools
|
||||
|
||||
from mobsf.MobSF.utils import (
|
||||
is_dylib_magic,
|
||||
is_elf_so_magic,
|
||||
is_zip_magic,
|
||||
)
|
||||
@@ -20,6 +21,7 @@ class FileType(object):
|
||||
self.file_name_lower = file_obj.name.lower()
|
||||
self.zip = is_zip_magic(file_obj)
|
||||
self.so = is_elf_so_magic(file_obj)
|
||||
self.dylib = is_dylib_magic(file_obj)
|
||||
|
||||
def is_allow_file(self):
|
||||
"""
|
||||
@@ -29,6 +31,8 @@ class FileType(object):
|
||||
"""
|
||||
if self.so and self.is_so():
|
||||
return True
|
||||
if self.dylib and self.is_dylib():
|
||||
return True
|
||||
if self.zip and (
|
||||
self.is_apk()
|
||||
or self.is_xapk()
|
||||
@@ -65,6 +69,10 @@ class FileType(object):
|
||||
return (self.file_type in settings.APK_MIME
|
||||
and self.file_name_lower.endswith('.so'))
|
||||
|
||||
def is_dylib(self):
|
||||
return (self.file_type in settings.IPA_MIME
|
||||
and self.file_name_lower.endswith('.dylib'))
|
||||
|
||||
def is_zip(self):
|
||||
return (self.file_type in settings.ZIP_MIME
|
||||
and self.file_name_lower.endswith('.zip'))
|
||||
|
||||
@@ -12,6 +12,7 @@ from wsgiref.util import FileWrapper
|
||||
from django.conf import settings
|
||||
from django.core.paginator import Paginator
|
||||
from django.http import HttpResponse, HttpResponseRedirect
|
||||
from django.utils import timezone
|
||||
from django.shortcuts import (
|
||||
redirect,
|
||||
render,
|
||||
@@ -150,6 +151,8 @@ class Upload(object):
|
||||
return scanning.scan_zip()
|
||||
elif self.file_type.is_ipa():
|
||||
return scanning.scan_ipa()
|
||||
elif self.file_type.is_dylib():
|
||||
return scanning.scan_dylib()
|
||||
elif self.file_type.is_appx():
|
||||
return scanning.scan_appx()
|
||||
|
||||
@@ -302,13 +305,14 @@ def download(request):
|
||||
def generate_download(request):
|
||||
"""Generate downloads for uploaded binaries/source."""
|
||||
try:
|
||||
allowed = ('apk', 'ipa', 'smali', 'java', 'jar', 'aar', 'so')
|
||||
binary = ('apk', 'ipa', 'jar', 'aar', 'so', 'dylib')
|
||||
source = ('smali', 'java')
|
||||
logger.info('Generating Downloads')
|
||||
md5 = request.GET['hash']
|
||||
file_type = request.GET['file_type']
|
||||
match = re.match('^[0-9a-f]{32}$', md5)
|
||||
if (not match
|
||||
or file_type not in allowed):
|
||||
or file_type not in binary + source):
|
||||
msg = 'Invalid download type or hash'
|
||||
logger.exception(msg)
|
||||
return print_n_send_error_response(request, msg)
|
||||
@@ -329,7 +333,7 @@ def generate_download(request):
|
||||
shutil.make_archive(
|
||||
dwd_file.as_posix(), 'zip', directory.as_posix())
|
||||
file_name = f'{md5}-smali.zip'
|
||||
elif file_type in ('apk', 'ipa', 'jar', 'aar', 'so'):
|
||||
elif file_type in binary:
|
||||
# Binaries
|
||||
file_name = f'{md5}.{file_type}'
|
||||
src = app_dir / file_name
|
||||
@@ -409,3 +413,9 @@ class RecentScans(object):
|
||||
except Exception as exp:
|
||||
data = {'error': str(exp)}
|
||||
return data
|
||||
|
||||
|
||||
def update_scan_timestamp(scan_hash):
|
||||
# Update the last scan time.
|
||||
tms = timezone.now()
|
||||
RecentScansDB.objects.filter(MD5=scan_hash).update(TIMESTAMP=tms)
|
||||
|
||||
@@ -144,6 +144,16 @@ class Scanning(object):
|
||||
logger.info('Performing Static Analysis of iOS IPA')
|
||||
return self.data
|
||||
|
||||
def scan_dylib(self):
|
||||
"""IOS Dylib."""
|
||||
md5 = handle_uploaded_file(self.file, '.dylib')
|
||||
self.data['hash'] = md5
|
||||
self.data['scan_type'] = 'dylib'
|
||||
self.data['analyzer'] = 'static_analyzer_ios'
|
||||
add_to_recent_scan(self.data)
|
||||
logger.info('Performing Static Analysis of iOS IPA')
|
||||
return self.data
|
||||
|
||||
def scan_appx(self):
|
||||
"""Windows appx."""
|
||||
md5 = handle_uploaded_file(self.file, '.appx')
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
from django import forms
|
||||
|
||||
@@ -12,26 +13,22 @@ class AttackDetect(forms.Form):
|
||||
if (('../' in file) or ('%2e%2e' in file)
|
||||
or ('..' in file) or ('%252e' in file)):
|
||||
raise forms.ValidationError('Attack Detected')
|
||||
|
||||
# Allowed File extensions
|
||||
supported_ext = (r'^\.(java|smali|xml|'
|
||||
r'plist|m|swift|'
|
||||
r'db|sqlitedb|sqlite|txt|json)$')
|
||||
if not re.search(supported_ext, Path(file).suffix):
|
||||
raise forms.ValidationError('File Extension not supported')
|
||||
return file
|
||||
|
||||
|
||||
class IOSChecks(forms.Form):
|
||||
file = forms.CharField()
|
||||
type = forms.ChoiceField( # noqa A003
|
||||
choices=(
|
||||
('ipa', 'ipa'),
|
||||
('dylib', 'dylib'),
|
||||
('ios', 'ios')))
|
||||
|
||||
def clean_file(self):
|
||||
"""Safe Extension."""
|
||||
file = self.cleaned_data['file']
|
||||
ext = file.split('.')[-1]
|
||||
ext_type = re.search('plist|db|sqlitedb|sqlite|txt|m', ext)
|
||||
if not ext_type:
|
||||
raise forms.ValidationError('File Extension not supported')
|
||||
return file
|
||||
|
||||
|
||||
class APIChecks(forms.Form):
|
||||
hash = forms.CharField(min_length=32, max_length=32) # noqa A003
|
||||
@@ -69,15 +66,6 @@ class AndroidChecks(forms.Form):
|
||||
('aar', 'aar'),
|
||||
('so', 'so')))
|
||||
|
||||
def clean_file(self):
|
||||
"""Safe Extension."""
|
||||
file = self.cleaned_data['file']
|
||||
ext = file.split('.')[-1]
|
||||
ext_type = re.search('java|smali', ext)
|
||||
if not ext_type:
|
||||
raise forms.ValidationError('File Extension not supported')
|
||||
return file
|
||||
|
||||
|
||||
class ViewSourceIOSApiForm(AttackDetect, IOSChecks, APIChecks):
|
||||
pass
|
||||
|
||||
@@ -85,6 +85,7 @@ class StaticAnalyzerIOS(models.Model):
|
||||
ATS_ANALYSIS = models.TextField(default=[])
|
||||
BINARY_ANALYSIS = models.TextField(default=[])
|
||||
MACHO_ANALYSIS = models.TextField(default={})
|
||||
DYLIB_ANALYSIS = models.TextField(default=[])
|
||||
IOS_API = models.TextField(default={})
|
||||
CODE_ANALYSIS = models.TextField(default={})
|
||||
FILE_ANALYSIS = models.TextField(default=[])
|
||||
|
||||
@@ -189,10 +189,20 @@ class Checksec:
|
||||
def strings(self):
|
||||
return self.elf.strings
|
||||
|
||||
def get_symbols(self):
|
||||
symbols = []
|
||||
for i in self.elf.symbols:
|
||||
symbols.append(i.name)
|
||||
return symbols
|
||||
|
||||
|
||||
def elf_analysis(app_dir: str) -> dict:
|
||||
"""Perform elf analysis on shared object."""
|
||||
elf = {'elf_analysis': [], 'elf_strings': []}
|
||||
elf = {
|
||||
'elf_analysis': [],
|
||||
'elf_strings': [],
|
||||
'elf_symbols': [],
|
||||
}
|
||||
try:
|
||||
if not settings_enabled('SO_ANALYSIS_ENABLED'):
|
||||
return elf
|
||||
@@ -215,11 +225,18 @@ def elf_analysis(app_dir: str) -> dict:
|
||||
logger.info('Analyzing %s', so_rel)
|
||||
chk = Checksec(sofile, so_rel)
|
||||
elf_find = chk.checksec()
|
||||
elf_str = chk.strings()
|
||||
elf_sym = chk.get_symbols()
|
||||
if elf_find:
|
||||
elf['elf_analysis'].append(
|
||||
elf_find)
|
||||
if elf_str:
|
||||
elf['elf_strings'].append(
|
||||
{so_rel: chk.strings()})
|
||||
{so_rel: elf_str})
|
||||
if elf_sym:
|
||||
elf['elf_symbols'].append(
|
||||
{so_rel: elf_sym})
|
||||
|
||||
except Exception:
|
||||
logger.exception('Performing Binary Analysis')
|
||||
return elf
|
||||
|
||||
@@ -5,6 +5,7 @@ from django.conf import settings
|
||||
from django.db.models import QuerySet
|
||||
|
||||
from mobsf.MobSF.utils import python_dict, python_list
|
||||
from mobsf.MobSF.views.home import update_scan_timestamp
|
||||
from mobsf.StaticAnalyzer.models import StaticAnalyzerAndroid
|
||||
from mobsf.StaticAnalyzer.models import RecentScansDB
|
||||
from mobsf.StaticAnalyzer.views.common.suppression import (
|
||||
@@ -233,3 +234,47 @@ def save_or_update(update_type,
|
||||
MD5=app_dic['md5']).update(**values)
|
||||
except Exception:
|
||||
logger.exception('Updating RecentScansDB')
|
||||
|
||||
|
||||
def save_get_ctx(app, man, m_anal, code, cert, elf, apkid, quark, trk, rscn):
|
||||
# SAVE TO DB
|
||||
if rscn:
|
||||
logger.info('Updating Database...')
|
||||
save_or_update(
|
||||
'update',
|
||||
app,
|
||||
man,
|
||||
m_anal,
|
||||
code,
|
||||
cert,
|
||||
elf,
|
||||
apkid,
|
||||
quark,
|
||||
trk,
|
||||
)
|
||||
update_scan_timestamp(app['md5'])
|
||||
else:
|
||||
logger.info('Saving to Database')
|
||||
save_or_update(
|
||||
'save',
|
||||
app,
|
||||
man,
|
||||
m_anal,
|
||||
code,
|
||||
cert,
|
||||
elf,
|
||||
apkid,
|
||||
quark,
|
||||
trk,
|
||||
)
|
||||
return get_context_from_analysis(
|
||||
app,
|
||||
man,
|
||||
m_anal,
|
||||
code,
|
||||
cert,
|
||||
elf,
|
||||
apkid,
|
||||
quark,
|
||||
trk,
|
||||
)
|
||||
|
||||
@@ -15,7 +15,6 @@ from mobsf.StaticAnalyzer.views.common.shared_func import (
|
||||
get_avg_cvss,
|
||||
hash_gen,
|
||||
unzip,
|
||||
update_scan_timestamp,
|
||||
)
|
||||
from mobsf.StaticAnalyzer.views.common.appsec import (
|
||||
get_android_dashboard,
|
||||
@@ -41,9 +40,8 @@ from mobsf.StaticAnalyzer.views.android.converter import (
|
||||
apk_2_java,
|
||||
)
|
||||
from mobsf.StaticAnalyzer.views.android.db_interaction import (
|
||||
get_context_from_analysis,
|
||||
get_context_from_db_entry,
|
||||
save_or_update,
|
||||
save_get_ctx,
|
||||
)
|
||||
from mobsf.MalwareAnalyzer.views.MalwareDomainCheck import MalwareDomainCheck
|
||||
|
||||
@@ -174,41 +172,7 @@ def common_analysis(request, app_dic, rescan, api, analysis_type):
|
||||
app_dic['zipped'] = analysis_type
|
||||
app_dic['icon_hidden'] = True
|
||||
app_dic['icon_found'] = False
|
||||
logger.info('Connecting to Database')
|
||||
try:
|
||||
# SAVE TO DB
|
||||
if rescan:
|
||||
logger.info('Updating Database...')
|
||||
save_or_update(
|
||||
'update',
|
||||
app_dic,
|
||||
man_data_dic,
|
||||
man_an_dic,
|
||||
code_an_dic,
|
||||
cert_dic,
|
||||
elf_dict['elf_analysis'],
|
||||
apkid_results,
|
||||
quark_results,
|
||||
tracker_res,
|
||||
)
|
||||
update_scan_timestamp(app_dic['md5'])
|
||||
else:
|
||||
logger.info('Saving to Database')
|
||||
save_or_update(
|
||||
'save',
|
||||
app_dic,
|
||||
man_data_dic,
|
||||
man_an_dic,
|
||||
code_an_dic,
|
||||
cert_dic,
|
||||
elf_dict['elf_analysis'],
|
||||
apkid_results,
|
||||
quark_results,
|
||||
tracker_res,
|
||||
)
|
||||
except Exception:
|
||||
logger.exception('Saving to Database Failed')
|
||||
context = get_context_from_analysis(
|
||||
context = save_get_ctx(
|
||||
app_dic,
|
||||
man_data_dic,
|
||||
man_an_dic,
|
||||
@@ -218,6 +182,7 @@ def common_analysis(request, app_dic, rescan, api, analysis_type):
|
||||
apkid_results,
|
||||
quark_results,
|
||||
tracker_res,
|
||||
rescan,
|
||||
)
|
||||
context['appsec'] = get_android_dashboard(context, True)
|
||||
context['average_cvss'] = get_avg_cvss(
|
||||
|
||||
@@ -4,14 +4,15 @@ import logging
|
||||
from django.conf import settings
|
||||
from django.shortcuts import render
|
||||
|
||||
import mobsf.MalwareAnalyzer.views.Trackers as Trackers
|
||||
import mobsf.MalwareAnalyzer.views.VirusTotal as VirusTotal
|
||||
from mobsf.MobSF.utils import (
|
||||
file_size,
|
||||
)
|
||||
from mobsf.StaticAnalyzer.views.common.shared_func import (
|
||||
firebase_analysis,
|
||||
get_symbols,
|
||||
hash_gen,
|
||||
update_scan_timestamp,
|
||||
)
|
||||
from mobsf.StaticAnalyzer.views.android.binary_analysis import (
|
||||
elf_analysis,
|
||||
@@ -23,9 +24,8 @@ from mobsf.StaticAnalyzer.models import (
|
||||
StaticAnalyzerAndroid,
|
||||
)
|
||||
from mobsf.StaticAnalyzer.views.android.db_interaction import (
|
||||
get_context_from_analysis,
|
||||
get_context_from_db_entry,
|
||||
save_or_update,
|
||||
save_get_ctx,
|
||||
)
|
||||
from mobsf.MalwareAnalyzer.views.MalwareDomainCheck import MalwareDomainCheck
|
||||
|
||||
@@ -90,8 +90,10 @@ def so_analysis(request, app_dic, rescan, api):
|
||||
}
|
||||
app_dic['real_name'] = ''
|
||||
elf_dict = elf_analysis(app_dic['app_dir'])
|
||||
# File Analysis is used to store symbols from so
|
||||
app_dic['certz'] = get_symbols(
|
||||
elf_dict['elf_symbols'])
|
||||
apkid_results = {}
|
||||
tracker_res = {}
|
||||
code_an_dic = {
|
||||
'api': {},
|
||||
'findings': {},
|
||||
@@ -114,50 +116,23 @@ def so_analysis(request, app_dic, rescan, api):
|
||||
# Firebase DB Check
|
||||
code_an_dic['firebase'] = firebase_analysis(
|
||||
code_an_dic['urls_list'])
|
||||
|
||||
# Domain Extraction and Malware Check
|
||||
logger.info(
|
||||
'Performing Malware Check on extracted Domains')
|
||||
code_an_dic['domains'] = MalwareDomainCheck().scan(
|
||||
code_an_dic['urls_list'])
|
||||
|
||||
# Extract Trackers from Domains
|
||||
trk = Trackers.Trackers(
|
||||
None, app_dic['tools_dir'])
|
||||
trackers = trk.get_trackers_domains_or_deps(
|
||||
code_an_dic['domains'], [])
|
||||
|
||||
app_dic['zipped'] = 'so'
|
||||
app_dic['icon_hidden'] = True
|
||||
app_dic['icon_found'] = False
|
||||
logger.info('Connecting to Database')
|
||||
try:
|
||||
# SAVE TO DB
|
||||
if rescan:
|
||||
logger.info('Updating Database...')
|
||||
save_or_update(
|
||||
'update',
|
||||
app_dic,
|
||||
man_data_dic,
|
||||
man_an_dic,
|
||||
code_an_dic,
|
||||
cert_dic,
|
||||
elf_dict['elf_analysis'],
|
||||
apkid_results,
|
||||
quark_results,
|
||||
tracker_res,
|
||||
)
|
||||
update_scan_timestamp(app_dic['md5'])
|
||||
else:
|
||||
logger.info('Saving to Database')
|
||||
save_or_update(
|
||||
'save',
|
||||
app_dic,
|
||||
man_data_dic,
|
||||
man_an_dic,
|
||||
code_an_dic,
|
||||
cert_dic,
|
||||
elf_dict['elf_analysis'],
|
||||
apkid_results,
|
||||
quark_results,
|
||||
tracker_res,
|
||||
)
|
||||
except Exception:
|
||||
logger.exception('Saving to Database Failed')
|
||||
context = get_context_from_analysis(
|
||||
context = save_get_ctx(
|
||||
app_dic,
|
||||
man_data_dic,
|
||||
man_an_dic,
|
||||
@@ -166,7 +141,8 @@ def so_analysis(request, app_dic, rescan, api):
|
||||
elf_dict['elf_analysis'],
|
||||
apkid_results,
|
||||
quark_results,
|
||||
tracker_res,
|
||||
trackers,
|
||||
rescan,
|
||||
)
|
||||
context['appsec'] = {}
|
||||
context['average_cvss'] = None
|
||||
|
||||
@@ -41,9 +41,8 @@ from mobsf.StaticAnalyzer.views.android.converter import (
|
||||
dex_2_smali,
|
||||
)
|
||||
from mobsf.StaticAnalyzer.views.android.db_interaction import (
|
||||
get_context_from_analysis,
|
||||
get_context_from_db_entry,
|
||||
save_or_update,
|
||||
save_get_ctx,
|
||||
)
|
||||
from mobsf.StaticAnalyzer.views.android.icon_analysis import (
|
||||
find_icon_path_zip,
|
||||
@@ -74,7 +73,6 @@ from mobsf.StaticAnalyzer.views.common.shared_func import (
|
||||
get_avg_cvss,
|
||||
hash_gen,
|
||||
unzip,
|
||||
update_scan_timestamp,
|
||||
)
|
||||
from mobsf.StaticAnalyzer.views.common.appsec import (
|
||||
get_android_dashboard,
|
||||
@@ -237,42 +235,7 @@ def static_analyzer(request, api=False):
|
||||
code_an_dic['urls_list'])
|
||||
|
||||
app_dic['zipped'] = 'apk'
|
||||
|
||||
logger.info('Connecting to Database')
|
||||
try:
|
||||
# SAVE TO DB
|
||||
if rescan:
|
||||
logger.info('Updating Database...')
|
||||
save_or_update(
|
||||
'update',
|
||||
app_dic,
|
||||
man_data_dic,
|
||||
man_an_dic,
|
||||
code_an_dic,
|
||||
cert_dic,
|
||||
elf_dict['elf_analysis'],
|
||||
apkid_results,
|
||||
quark_results,
|
||||
tracker_res,
|
||||
)
|
||||
update_scan_timestamp(app_dic['md5'])
|
||||
else:
|
||||
logger.info('Saving to Database')
|
||||
save_or_update(
|
||||
'save',
|
||||
app_dic,
|
||||
man_data_dic,
|
||||
man_an_dic,
|
||||
code_an_dic,
|
||||
cert_dic,
|
||||
elf_dict['elf_analysis'],
|
||||
apkid_results,
|
||||
quark_results,
|
||||
tracker_res,
|
||||
)
|
||||
except Exception:
|
||||
logger.exception('Saving to Database Failed')
|
||||
context = get_context_from_analysis(
|
||||
context = save_get_ctx(
|
||||
app_dic,
|
||||
man_data_dic,
|
||||
man_an_dic,
|
||||
@@ -282,6 +245,7 @@ def static_analyzer(request, api=False):
|
||||
apkid_results,
|
||||
quark_results,
|
||||
tracker_res,
|
||||
rescan,
|
||||
)
|
||||
context['appsec'] = get_android_dashboard(context, True)
|
||||
context['average_cvss'] = get_avg_cvss(
|
||||
@@ -450,41 +414,7 @@ def static_analyzer(request, api=False):
|
||||
None, app_dic['tools_dir'])
|
||||
trackers = trk.get_trackers_domains_or_deps(
|
||||
code_an_dic['domains'], [])
|
||||
logger.info('Connecting to Database')
|
||||
try:
|
||||
# SAVE TO DB
|
||||
if rescan:
|
||||
logger.info('Updating Database...')
|
||||
save_or_update(
|
||||
'update',
|
||||
app_dic,
|
||||
man_data_dic,
|
||||
man_an_dic,
|
||||
code_an_dic,
|
||||
cert_dic,
|
||||
[],
|
||||
{},
|
||||
[],
|
||||
trackers,
|
||||
)
|
||||
update_scan_timestamp(app_dic['md5'])
|
||||
else:
|
||||
logger.info('Saving to Database')
|
||||
save_or_update(
|
||||
'save',
|
||||
app_dic,
|
||||
man_data_dic,
|
||||
man_an_dic,
|
||||
code_an_dic,
|
||||
cert_dic,
|
||||
[],
|
||||
{},
|
||||
[],
|
||||
trackers,
|
||||
)
|
||||
except Exception:
|
||||
logger.exception('Saving to Database Failed')
|
||||
context = get_context_from_analysis(
|
||||
context = save_get_ctx(
|
||||
app_dic,
|
||||
man_data_dic,
|
||||
man_an_dic,
|
||||
@@ -494,6 +424,7 @@ def static_analyzer(request, api=False):
|
||||
{},
|
||||
[],
|
||||
trackers,
|
||||
rescan,
|
||||
)
|
||||
else:
|
||||
msg = 'This ZIP Format is not supported'
|
||||
|
||||
@@ -68,6 +68,9 @@ def common_fields(findings, data):
|
||||
cert_files = None
|
||||
cfp = []
|
||||
for fa in data['file_analysis']:
|
||||
if isinstance(fa, str):
|
||||
# FA is being used by so/dylib
|
||||
continue
|
||||
if 'Cert' in fa.get('finding', ''):
|
||||
cfp = fa['files']
|
||||
break
|
||||
|
||||
@@ -17,7 +17,6 @@ from pathlib import Path
|
||||
|
||||
import requests
|
||||
|
||||
from django.utils import timezone
|
||||
from django.utils.html import escape
|
||||
|
||||
from mobsf.MobSF import settings
|
||||
@@ -26,7 +25,6 @@ from mobsf.MobSF.utils import (
|
||||
print_n_send_error_response,
|
||||
upstream_proxy,
|
||||
)
|
||||
from mobsf.StaticAnalyzer.models import RecentScansDB
|
||||
from mobsf.StaticAnalyzer.views.comparer import (
|
||||
generic_compare,
|
||||
)
|
||||
@@ -164,12 +162,6 @@ def get_avg_cvss(findings):
|
||||
return avg_cvss
|
||||
|
||||
|
||||
def update_scan_timestamp(scan_hash):
|
||||
# Update the last scan time.
|
||||
tms = timezone.now()
|
||||
RecentScansDB.objects.filter(MD5=scan_hash).update(TIMESTAMP=tms)
|
||||
|
||||
|
||||
def open_firebase(url):
|
||||
# Detect Open Firebase Database
|
||||
try:
|
||||
@@ -253,7 +245,7 @@ def strings_and_entropies(src, exts):
|
||||
if not src.exists():
|
||||
return data
|
||||
excludes = ('\\u0', 'com.google.')
|
||||
eslash = ('(L', '[L', '[F', 'Ljava', 'Lkotlin', 'kotlin', 'android')
|
||||
eslash = ('Ljava', 'Lkotlin', 'kotlin', 'android')
|
||||
for p in src.rglob('*'):
|
||||
if p.suffix not in exts or not p.exists():
|
||||
continue
|
||||
@@ -268,9 +260,18 @@ def strings_and_entropies(src, exts):
|
||||
continue
|
||||
if any(i in string and '/' in string for i in eslash):
|
||||
continue
|
||||
if not string[0].isalnum():
|
||||
continue
|
||||
data['strings'].add(string)
|
||||
if data['strings']:
|
||||
data['secrets'] = get_entropies(data['strings'])
|
||||
except Exception:
|
||||
logger.exception('Extracting Data from Code')
|
||||
return data
|
||||
|
||||
|
||||
def get_symbols(symbols):
|
||||
for i in symbols:
|
||||
for _, val in i.items():
|
||||
return val
|
||||
return []
|
||||
|
||||
@@ -14,12 +14,19 @@ from mobsf.StaticAnalyzer.views.ios.classdump import (
|
||||
from mobsf.StaticAnalyzer.views.ios.macho_analysis import (
|
||||
macho_analysis,
|
||||
)
|
||||
from mobsf.StaticAnalyzer.views.ios.strings import (
|
||||
strings_on_binary,
|
||||
)
|
||||
from mobsf.StaticAnalyzer.views.ios.binary_rule_matcher import (
|
||||
binary_rule_matcher,
|
||||
)
|
||||
from mobsf.StaticAnalyzer.tools.strings import (
|
||||
strings_util,
|
||||
from mobsf.MobSF.utils import (
|
||||
settings_enabled,
|
||||
)
|
||||
from mobsf.StaticAnalyzer.views.ios.macho_analysis import (
|
||||
Checksec,
|
||||
)
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -32,17 +39,6 @@ def detect_bin_type(libs):
|
||||
return 'Objective C'
|
||||
|
||||
|
||||
def strings_on_ipa(bin_path):
|
||||
"""Extract Strings from IPA."""
|
||||
try:
|
||||
logger.info('Running strings against the Binary')
|
||||
unique_str = []
|
||||
unique_str = list(set(strings_util(bin_path.as_posix())))
|
||||
return unique_str
|
||||
except Exception:
|
||||
logger.exception('Running strings against the Binary')
|
||||
|
||||
|
||||
def get_bin_info(bin_file):
|
||||
"""Get Binary Information."""
|
||||
logger.info('Getting Binary Information')
|
||||
@@ -107,13 +103,47 @@ def binary_analysis(src, tools_dir, app_dir, executable_name):
|
||||
binary_rule_matcher(
|
||||
binary_findings,
|
||||
macho['symbols'], classdump)
|
||||
strings_in_ipa = strings_on_ipa(bin_path)
|
||||
bin_dict['checksec'] = macho['checksec']
|
||||
bin_dict['libraries'] = macho['libraries']
|
||||
bin_dict['bin_code_analysis'] = binary_findings
|
||||
bin_dict['strings'] = strings_in_ipa
|
||||
bin_dict['bin_info'] = bin_info
|
||||
bin_dict['bin_type'] = bin_type
|
||||
logger.info('Running strings against the Binary')
|
||||
bin_dict['strings'] = strings_on_binary(bin_path)
|
||||
except Exception:
|
||||
logger.exception('IPA Binary Analysis')
|
||||
return bin_dict
|
||||
|
||||
|
||||
def dylibs_analysis(app_dir: str) -> dict:
|
||||
"""Perform analysis on dynamic libraries."""
|
||||
dylibs = {
|
||||
'dylib_analysis': [],
|
||||
'dylib_strings': [],
|
||||
'dylib_symbols': [],
|
||||
}
|
||||
try:
|
||||
if not settings_enabled('DYLIB_ANALYSIS_ENABLED'):
|
||||
return dylibs
|
||||
logger.info('Dynamic Library Analysis Started')
|
||||
sdir = Path(app_dir)
|
||||
# Supports IPA, DYLIB
|
||||
for dylib in sdir.rglob('*.dylib'):
|
||||
dy = (
|
||||
f'{dylib.parents[1].name}/'
|
||||
f'{dylib.parents[0].name}/'
|
||||
f'{dylib.name}')
|
||||
logger.info('Analyzing %s', dy)
|
||||
chk = Checksec(dylib, dy)
|
||||
dy_find = chk.checksec()
|
||||
symbols = chk.get_symbols()
|
||||
if dy_find:
|
||||
dylibs['dylib_analysis'].append(dy_find)
|
||||
dylibs['dylib_strings'].append(
|
||||
{dy: strings_on_binary(dylib)})
|
||||
if symbols:
|
||||
dylibs['dylib_symbols'].append(
|
||||
{dy: symbols})
|
||||
except Exception:
|
||||
logger.exception('Performing Dynamic Library Analysis')
|
||||
return dylibs
|
||||
|
||||
@@ -4,9 +4,12 @@ import logging
|
||||
from django.conf import settings
|
||||
|
||||
from mobsf.MobSF.utils import python_dict, python_list
|
||||
from mobsf.MobSF.views.home import update_scan_timestamp
|
||||
from mobsf.StaticAnalyzer.models import StaticAnalyzerIOS
|
||||
from mobsf.StaticAnalyzer.models import RecentScansDB
|
||||
from mobsf.StaticAnalyzer.views.common.suppression import process_suppression
|
||||
from mobsf.StaticAnalyzer.views.common.suppression import (
|
||||
process_suppression,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -48,6 +51,7 @@ def get_context_from_db_entry(db_entry):
|
||||
'ats_analysis': python_dict(db_entry[0].ATS_ANALYSIS),
|
||||
'binary_analysis': binary,
|
||||
'macho_analysis': python_dict(db_entry[0].MACHO_ANALYSIS),
|
||||
'dylib_analysis': python_list(db_entry[0].DYLIB_ANALYSIS),
|
||||
'ios_api': python_dict(db_entry[0].IOS_API),
|
||||
'code_analysis': code,
|
||||
'file_analysis': python_list(db_entry[0].FILE_ANALYSIS),
|
||||
@@ -108,6 +112,7 @@ def get_context_from_analysis(app_dict,
|
||||
'ats_analysis': info_dict['inseccon'],
|
||||
'binary_analysis': binary,
|
||||
'macho_analysis': bin_dict['checksec'],
|
||||
'dylib_analysis': bin_dict['dylib_analysis'],
|
||||
'ios_api': code_dict['api'],
|
||||
'code_analysis': code,
|
||||
'file_analysis': all_files['special_files'],
|
||||
@@ -159,6 +164,7 @@ def save_or_update(update_type,
|
||||
'ATS_ANALYSIS': info_dict['inseccon'],
|
||||
'BINARY_ANALYSIS': bin_dict['bin_code_analysis'],
|
||||
'MACHO_ANALYSIS': bin_dict['checksec'],
|
||||
'DYLIB_ANALYSIS': bin_dict['dylib_analysis'],
|
||||
'IOS_API': code_dict['api'],
|
||||
'CODE_ANALYSIS': code_dict['code_anal'],
|
||||
'FILE_ANALYSIS': all_files['special_files'],
|
||||
@@ -193,3 +199,33 @@ def save_or_update(update_type,
|
||||
MD5=app_dict['md5_hash']).update(**values)
|
||||
except Exception:
|
||||
logger.exception('Updating RecentScansDB')
|
||||
|
||||
|
||||
def save_get_ctx(app_dict, pdict, code_dict, bin_dict, all_files, rescan):
|
||||
# Saving to DB
|
||||
logger.info('Connecting to DB')
|
||||
if rescan:
|
||||
logger.info('Updating Database...')
|
||||
save_or_update(
|
||||
'update',
|
||||
app_dict,
|
||||
pdict,
|
||||
code_dict,
|
||||
bin_dict,
|
||||
all_files)
|
||||
update_scan_timestamp(app_dict['md5_hash'])
|
||||
else:
|
||||
logger.info('Saving to Database')
|
||||
save_or_update(
|
||||
'save',
|
||||
app_dict,
|
||||
pdict,
|
||||
code_dict,
|
||||
bin_dict,
|
||||
all_files)
|
||||
return get_context_from_analysis(
|
||||
app_dict,
|
||||
pdict,
|
||||
code_dict,
|
||||
bin_dict,
|
||||
all_files)
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
# -*- coding: utf_8 -*-
|
||||
"""Handle (.dylib) Dynamic library file."""
|
||||
import logging
|
||||
from pathlib import Path
|
||||
|
||||
import mobsf.MalwareAnalyzer.views.Trackers as Trackers
|
||||
import mobsf.MalwareAnalyzer.views.VirusTotal as VirusTotal
|
||||
|
||||
from django.conf import settings
|
||||
from django.shortcuts import render
|
||||
|
||||
from mobsf.MobSF.utils import (
|
||||
file_size,
|
||||
)
|
||||
from mobsf.StaticAnalyzer.models import StaticAnalyzerIOS
|
||||
from mobsf.StaticAnalyzer.views.ios.binary_analysis import (
|
||||
dylibs_analysis,
|
||||
get_bin_info,
|
||||
)
|
||||
from mobsf.StaticAnalyzer.views.ios.binary_rule_matcher import (
|
||||
binary_rule_matcher,
|
||||
)
|
||||
from mobsf.StaticAnalyzer.views.ios.db_interaction import (
|
||||
get_context_from_db_entry,
|
||||
save_get_ctx,
|
||||
)
|
||||
from mobsf.StaticAnalyzer.views.ios.strings import (
|
||||
get_strings_metadata,
|
||||
)
|
||||
from mobsf.StaticAnalyzer.views.common.shared_func import (
|
||||
firebase_analysis,
|
||||
get_symbols,
|
||||
hash_gen,
|
||||
)
|
||||
from mobsf.MalwareAnalyzer.views.MalwareDomainCheck import (
|
||||
MalwareDomainCheck,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def dylib_analysis(request, app_dict, rescan, api):
|
||||
"""Independent Dylib (.dylib) analysis."""
|
||||
app_dir = Path(app_dict['app_dir'])
|
||||
app_dict['app_file'] = app_dict[
|
||||
'md5_hash'] + '.dylib' # NEW FILENAME
|
||||
app_dict['app_path'] = app_dir / app_dict['app_file']
|
||||
app_dict['app_path'] = app_dict['app_path'].as_posix()
|
||||
# DB
|
||||
ipa_db = StaticAnalyzerIOS.objects.filter(
|
||||
MD5=app_dict['md5_hash'])
|
||||
if ipa_db.exists() and not rescan:
|
||||
context = get_context_from_db_entry(ipa_db)
|
||||
else:
|
||||
logger.info('iOS DYLIB Analysis Started')
|
||||
app_dict['size'] = str(
|
||||
file_size(app_dict['app_path'])) + 'MB' # FILE SIZE
|
||||
app_dict['sha1'], app_dict['sha256'] = hash_gen(
|
||||
app_dict['app_path']) # SHA1 & SHA256 HASHES
|
||||
app_dict['bin_dir'] = app_dict['app_dir']
|
||||
# Get Files
|
||||
all_files = {
|
||||
'files_short': [],
|
||||
'files_long': [],
|
||||
'special_files': [],
|
||||
}
|
||||
infoplist_dict = {
|
||||
'bin_name': '',
|
||||
'bin': '',
|
||||
'id': app_dict['file_name'],
|
||||
'version': '',
|
||||
'build': '',
|
||||
'sdk': '',
|
||||
'pltfm': '',
|
||||
'min': '',
|
||||
'plist_xml': '',
|
||||
'permissions': {},
|
||||
'inseccon': {},
|
||||
'bundle_name': '',
|
||||
'build_version_name': '',
|
||||
'bundle_url_types': [],
|
||||
'bundle_supported_platforms': [],
|
||||
'bundle_version_name': '',
|
||||
}
|
||||
app_dict['appstore'] = ''
|
||||
app_dict['secrets'] = []
|
||||
bin_dict = {
|
||||
'checksec': {},
|
||||
'libraries': [],
|
||||
'bin_code_analysis': {},
|
||||
'strings': [],
|
||||
'bin_info': get_bin_info(
|
||||
Path(app_dict['app_path'])),
|
||||
'bin_type': 'Dylib',
|
||||
}
|
||||
# Analyze dylib
|
||||
dy = dylibs_analysis(app_dict['bin_dir'])
|
||||
bin_dict['dylib_analysis'] = dy['dylib_analysis']
|
||||
# Store Symbols in File Analysis
|
||||
all_files['special_files'] = get_symbols(
|
||||
dy['dylib_symbols'])
|
||||
# Binary code analysis on dylib symbols
|
||||
binary_rule_matcher(
|
||||
bin_dict['bin_code_analysis'],
|
||||
all_files['special_files'],
|
||||
b'')
|
||||
# Get Icon
|
||||
app_dict['icon_found'] = False
|
||||
# Extract String metadata
|
||||
code_dict = get_strings_metadata(
|
||||
app_dict,
|
||||
bin_dict,
|
||||
all_files,
|
||||
dy['dylib_strings'])
|
||||
|
||||
# Domain Extraction and Malware Check
|
||||
logger.info('Performing Malware Check on '
|
||||
'extracted Domains')
|
||||
code_dict['domains'] = MalwareDomainCheck().scan(
|
||||
code_dict['urls_list'])
|
||||
logger.info('Finished URL and Email Extraction')
|
||||
|
||||
# Extract Trackers from Domains
|
||||
trk = Trackers.Trackers(
|
||||
None, app_dict['tools_dir'])
|
||||
trackers = trk.get_trackers_domains_or_deps(
|
||||
code_dict['domains'], [])
|
||||
|
||||
code_dict['api'] = {}
|
||||
code_dict['code_anal'] = {}
|
||||
code_dict['firebase'] = firebase_analysis(
|
||||
code_dict['urls_list'])
|
||||
code_dict['trackers'] = trackers
|
||||
context = save_get_ctx(
|
||||
app_dict,
|
||||
infoplist_dict,
|
||||
code_dict,
|
||||
bin_dict,
|
||||
all_files,
|
||||
rescan)
|
||||
context['virus_total'] = None
|
||||
if settings.VT_ENABLED:
|
||||
vt = VirusTotal.VirusTotal()
|
||||
context['virus_total'] = vt.get_result(
|
||||
app_dict['app_path'],
|
||||
app_dict['md5_hash'])
|
||||
context['appsec'] = {}
|
||||
context['average_cvss'] = None
|
||||
template = 'static_analysis/ios_binary_analysis.html'
|
||||
if api:
|
||||
return context
|
||||
else:
|
||||
return render(request, template, context)
|
||||
@@ -1,63 +0,0 @@
|
||||
import io
|
||||
import logging
|
||||
|
||||
from mobsf.MalwareAnalyzer.views.MalwareDomainCheck import (
|
||||
MalwareDomainCheck,
|
||||
)
|
||||
from mobsf.StaticAnalyzer.views.common.shared_func import (
|
||||
url_n_email_extract,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def extract_urls_n_email(src, all_files, strings):
|
||||
"""IPA URL and Email Extraction."""
|
||||
try:
|
||||
logger.info('Starting IPA URL and Email Extraction')
|
||||
email_n_file = []
|
||||
url_n_file = []
|
||||
url_list = []
|
||||
domains = {}
|
||||
all_files.append({'data': strings, 'name': 'IPA Strings Dump'})
|
||||
for file in all_files:
|
||||
if isinstance(file, dict):
|
||||
relative_src_path = file['name']
|
||||
dat = '\n'.join(file['data'])
|
||||
# Skip CodeResources and contents under Frameworks
|
||||
elif 'CodeResources' in file or '/Frameworks/' in file:
|
||||
continue
|
||||
elif file.endswith(('.nib', '.ttf', '.svg', '.woff2',
|
||||
'.png', '.dylib', '.mobileprovision',
|
||||
'Assets.car')):
|
||||
continue
|
||||
else:
|
||||
dat = ''
|
||||
relative_src_path = file.replace(src, '')
|
||||
with io.open(file,
|
||||
mode='r',
|
||||
encoding='utf8',
|
||||
errors='ignore') as flip:
|
||||
dat = flip.read()
|
||||
# Extract URLs and Emails from Plists
|
||||
urls, urls_nf, emails_nf = url_n_email_extract(
|
||||
dat, relative_src_path)
|
||||
url_list.extend(urls)
|
||||
url_n_file.extend(urls_nf)
|
||||
email_n_file.extend(emails_nf)
|
||||
# Unique URLs
|
||||
urls_list = list(set(url_list))
|
||||
# Domain Extraction and Malware Check
|
||||
logger.info('Performing Malware Check on extracted Domains')
|
||||
domains = MalwareDomainCheck().scan(urls_list)
|
||||
logger.info('Finished URL and Email Extraction')
|
||||
binary_recon = {
|
||||
'urls_list': urls_list,
|
||||
'urlnfile': url_n_file,
|
||||
'domains': domains,
|
||||
'emailnfile': email_n_file,
|
||||
}
|
||||
return binary_recon
|
||||
|
||||
except Exception:
|
||||
logger.exception('IPA URL and Email Extraction')
|
||||
@@ -8,13 +8,17 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Checksec:
|
||||
def __init__(self, macho):
|
||||
def __init__(self, macho, rel_path=None):
|
||||
self.macho_path = macho.as_posix()
|
||||
if rel_path:
|
||||
self.macho_name = rel_path
|
||||
else:
|
||||
self.macho_name = macho.name
|
||||
self.macho = lief.parse(self.macho_path)
|
||||
|
||||
def checksec(self):
|
||||
macho_dict = {}
|
||||
macho_dict['name'] = self.macho.name
|
||||
macho_dict['name'] = self.macho_name
|
||||
|
||||
if not self.is_macho(self.macho_path):
|
||||
return {}
|
||||
@@ -58,6 +62,8 @@ class Checksec:
|
||||
'to execute reliably.')
|
||||
else:
|
||||
severity = 'high'
|
||||
if self.macho_name.endswith('.dylib'):
|
||||
severity = 'info'
|
||||
desc = (
|
||||
'The binary is built without Position '
|
||||
'Independent Code flag. In order to prevent '
|
||||
@@ -67,7 +73,8 @@ class Checksec:
|
||||
'the address space positions of key data areas of a '
|
||||
'process, including the base of the executable and the '
|
||||
'positions of the stack,heap and libraries. Use compiler '
|
||||
'option -fPIC to enable Position Independent Code.')
|
||||
'option -fPIC to enable Position Independent Code.'
|
||||
'Not applicable for dylibs.')
|
||||
macho_dict['pie'] = {
|
||||
'has_pie': has_pie,
|
||||
'severity': severity,
|
||||
@@ -88,12 +95,16 @@ class Checksec:
|
||||
'whether stack canary is enabled or not.')
|
||||
else:
|
||||
severity = 'high'
|
||||
sw_msg = ''
|
||||
if 'libswift' in self.macho_name:
|
||||
severity = 'warning'
|
||||
sw_msg = ' This might be okey for pure Swift dylibs.'
|
||||
desc = (
|
||||
'This binary does not have a stack '
|
||||
'canary value added to the stack. Stack canaries '
|
||||
'are used to detect and prevent exploits from '
|
||||
'overwriting return address. Use the option '
|
||||
'-fstack-protector-all to enable stack canaries.')
|
||||
f'-fstack-protector-all to enable stack canaries.{sw_msg}')
|
||||
macho_dict['stack_canary'] = {
|
||||
'has_canary': has_canary,
|
||||
'severity': severity,
|
||||
@@ -123,7 +134,9 @@ class Checksec:
|
||||
'management of Objective-C objects and '
|
||||
'protects from memory corruption '
|
||||
'vulnerabilities. Use compiler option '
|
||||
'-fobjc-arc to enable ARC.')
|
||||
'-fobjc-arc to enable ARC or set '
|
||||
'Objective-C Automatic Reference Counting '
|
||||
'to YES in project configuration.')
|
||||
macho_dict['arc'] = {
|
||||
'has_arc': has_arc,
|
||||
'severity': severity,
|
||||
@@ -221,7 +234,10 @@ class Checksec:
|
||||
return False
|
||||
|
||||
def is_encrypted(self):
|
||||
return bool(self.macho.encryption_info.crypt_id)
|
||||
try:
|
||||
return bool(self.macho.encryption_info.crypt_id)
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def is_symbols_stripped(self):
|
||||
filter_symbols = ['radr://5614542', '__mh_execute_header']
|
||||
@@ -252,7 +268,7 @@ class Checksec:
|
||||
|
||||
def macho_analysis(binary):
|
||||
try:
|
||||
logger.info('Running MachO Analysis on %s', binary.name)
|
||||
logger.info('Running MachO Analysis on: %s', binary.name)
|
||||
cs = Checksec(binary)
|
||||
chksec = cs.checksec()
|
||||
symbols = cs.get_symbols()
|
||||
|
||||
@@ -223,22 +223,24 @@ def get_summary(ats):
|
||||
return summary
|
||||
|
||||
|
||||
def get_plist_secrets(xml_string):
|
||||
"""Get possible hardcoded secrets from plist."""
|
||||
result_list = []
|
||||
def get_plist_secrets(app_dir):
|
||||
"""Get possible hardcoded secrets from plist files."""
|
||||
result_list = set()
|
||||
|
||||
def _remove_tags(data):
|
||||
"""Remove tags from input."""
|
||||
return sub('<[^<]+>', '', data).strip()
|
||||
|
||||
xml_list = xml_string.split('\n')
|
||||
|
||||
for index, line in enumerate(xml_list):
|
||||
if '<key>' in line and is_secret_key(_remove_tags(line)):
|
||||
nxt = index + 1
|
||||
value = (
|
||||
_remove_tags(xml_list[nxt])if nxt < len(xml_list) else False)
|
||||
if value and ' ' not in value:
|
||||
result_list.append(
|
||||
f'{_remove_tags(line)} : {_remove_tags(value)}')
|
||||
return result_list
|
||||
for i in Path(app_dir).rglob('*.plist'):
|
||||
xml_string = i.read_text('utf-8', 'ignore')
|
||||
xml_list = xml_string.split('\n')
|
||||
for index, line in enumerate(xml_list):
|
||||
if '<key>' in line and is_secret_key(_remove_tags(line)):
|
||||
nxt = index + 1
|
||||
value = (
|
||||
_remove_tags(
|
||||
xml_list[nxt])if nxt < len(xml_list) else False)
|
||||
if value and ' ' not in value:
|
||||
result_list.add(
|
||||
f'{_remove_tags(line)} : {_remove_tags(value)}')
|
||||
return list(result_list)
|
||||
|
||||
@@ -16,14 +16,17 @@ from mobsf.MobSF.utils import (
|
||||
)
|
||||
from mobsf.StaticAnalyzer.models import StaticAnalyzerIOS
|
||||
from mobsf.StaticAnalyzer.views.ios.appstore import app_search
|
||||
from mobsf.StaticAnalyzer.views.ios.binary_analysis import binary_analysis
|
||||
from mobsf.StaticAnalyzer.views.ios.binary_analysis import (
|
||||
binary_analysis,
|
||||
dylibs_analysis,
|
||||
)
|
||||
from mobsf.StaticAnalyzer.views.ios.code_analysis import ios_source_analysis
|
||||
from mobsf.StaticAnalyzer.views.ios.db_interaction import (
|
||||
get_context_from_analysis,
|
||||
get_context_from_db_entry,
|
||||
save_or_update)
|
||||
save_get_ctx,
|
||||
)
|
||||
from mobsf.StaticAnalyzer.views.ios.dylib import dylib_analysis
|
||||
from mobsf.StaticAnalyzer.views.ios.file_analysis import ios_list_files
|
||||
from mobsf.StaticAnalyzer.views.ios.file_recon import extract_urls_n_email
|
||||
from mobsf.StaticAnalyzer.views.ios.icon_analysis import (
|
||||
get_icon,
|
||||
get_icon_source,
|
||||
@@ -32,17 +35,22 @@ from mobsf.StaticAnalyzer.views.ios.plist_analysis import (
|
||||
get_plist_secrets,
|
||||
plist_analysis,
|
||||
)
|
||||
from mobsf.StaticAnalyzer.views.ios.strings import (
|
||||
get_strings_metadata,
|
||||
)
|
||||
from mobsf.StaticAnalyzer.views.common.shared_func import (
|
||||
firebase_analysis,
|
||||
get_avg_cvss,
|
||||
hash_gen,
|
||||
strings_and_entropies,
|
||||
unzip,
|
||||
update_scan_timestamp,
|
||||
)
|
||||
from mobsf.StaticAnalyzer.views.common.appsec import (
|
||||
get_ios_dashboard,
|
||||
)
|
||||
from mobsf.MalwareAnalyzer.views.MalwareDomainCheck import (
|
||||
MalwareDomainCheck,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -69,18 +77,19 @@ def static_analyzer_ios(request, api=False):
|
||||
if re_scan == '1':
|
||||
rescan = True
|
||||
md5_match = re.match('^[0-9a-f]{32}$', checksum)
|
||||
if ((md5_match)
|
||||
and (filename.lower().endswith('.ipa')
|
||||
or filename.lower().endswith('.zip'))
|
||||
and (file_type in ['ipa', 'ios'])):
|
||||
app_dict = {}
|
||||
app_dict = {}
|
||||
if (md5_match
|
||||
and filename.lower().endswith(
|
||||
('.ipa', '.zip', '.dylib'))
|
||||
and file_type in ['ipa', 'ios', 'dylib']):
|
||||
app_dict['directory'] = Path(settings.BASE_DIR) # BASE DIR
|
||||
app_dict['file_name'] = filename # APP ORIGINAL NAME
|
||||
app_dict['md5_hash'] = checksum # MD5
|
||||
app_dir = Path(settings.UPLD_DIR) / checksum
|
||||
app_dict['app_dir'] = app_dir.as_posix() + '/'
|
||||
tools_dir = app_dict[
|
||||
'directory'] / 'StaticAnalyzer' / 'tools' / 'ios'
|
||||
tools_dir = tools_dir.as_posix()
|
||||
app_dict['tools_dir'] = tools_dir.as_posix()
|
||||
if file_type == 'ipa':
|
||||
app_dict['app_file'] = app_dict[
|
||||
'md5_hash'] + '.ipa' # NEW FILENAME
|
||||
@@ -93,7 +102,6 @@ def static_analyzer_ios(request, api=False):
|
||||
context = get_context_from_db_entry(ipa_db)
|
||||
else:
|
||||
logger.info('iOS Binary (IPA) Analysis Started')
|
||||
app_dict['app_dir'] = app_dir.as_posix() + '/'
|
||||
app_dict['size'] = str(
|
||||
file_size(app_dict['app_path'])) + 'MB' # FILE SIZE
|
||||
app_dict['sha1'], app_dict['sha256'] = hash_gen(
|
||||
@@ -110,79 +118,64 @@ def static_analyzer_ios(request, api=False):
|
||||
else:
|
||||
msg = ('IPA is malformed! '
|
||||
'MobSF cannot find Payload directory')
|
||||
if api:
|
||||
return print_n_send_error_response(
|
||||
request,
|
||||
msg,
|
||||
True)
|
||||
else:
|
||||
return print_n_send_error_response(
|
||||
request,
|
||||
msg,
|
||||
False)
|
||||
return print_n_send_error_response(
|
||||
request,
|
||||
msg,
|
||||
api)
|
||||
app_dict['bin_dir'] = app_dict['bin_dir'].as_posix() + '/'
|
||||
# Get Files
|
||||
all_files = ios_list_files(
|
||||
app_dict['bin_dir'], app_dict['md5_hash'], True, 'ipa')
|
||||
# Plist files are converted to xml/readable
|
||||
infoplist_dict = plist_analysis(app_dict['bin_dir'], False)
|
||||
app_dict['appstore'] = app_search(infoplist_dict.get('id'))
|
||||
app_dict['secrets'] = get_plist_secrets(
|
||||
infoplist_dict['plist_xml'])
|
||||
bin_analysis_dict = binary_analysis(
|
||||
app_dict['bin_dir'])
|
||||
bin_dict = binary_analysis(
|
||||
app_dict['bin_dir'],
|
||||
tools_dir,
|
||||
app_dict['tools_dir'],
|
||||
app_dict['app_dir'],
|
||||
infoplist_dict.get('bin'))
|
||||
# Analyze dylibs
|
||||
dy = dylibs_analysis(app_dict['bin_dir'])
|
||||
bin_dict['dylib_analysis'] = dy['dylib_analysis']
|
||||
# Get Icon
|
||||
app_dict['icon_found'] = get_icon(
|
||||
app_dict['md5_hash'],
|
||||
app_dict['bin_dir'],
|
||||
infoplist_dict.get('bin'))
|
||||
# IPA URL and Email Extract
|
||||
recon = extract_urls_n_email(app_dict['bin_dir'],
|
||||
all_files['files_long'],
|
||||
bin_analysis_dict['strings'])
|
||||
# Extract String metadata
|
||||
code_dict = get_strings_metadata(
|
||||
app_dict,
|
||||
bin_dict,
|
||||
all_files,
|
||||
dy['dylib_strings'])
|
||||
|
||||
# Domain Extraction and Malware Check
|
||||
logger.info('Performing Malware Check on '
|
||||
'extracted Domains')
|
||||
code_dict['domains'] = MalwareDomainCheck().scan(
|
||||
code_dict['urls_list'])
|
||||
logger.info('Finished URL and Email Extraction')
|
||||
|
||||
# Extract Trackers from Domains
|
||||
trk = Trackers.Trackers(
|
||||
None, tools_dir)
|
||||
None, app_dict['tools_dir'])
|
||||
trackers = trk.get_trackers_domains_or_deps(
|
||||
recon['domains'], [])
|
||||
code_dict = {
|
||||
'api': {},
|
||||
'code_anal': {},
|
||||
'urlnfile': recon['urlnfile'],
|
||||
'domains': recon['domains'],
|
||||
'emailnfile': recon['emailnfile'],
|
||||
'firebase': firebase_analysis(recon['urls_list']),
|
||||
'trackers': trackers,
|
||||
}
|
||||
# Saving to DB
|
||||
logger.info('Connecting to DB')
|
||||
if rescan:
|
||||
logger.info('Updating Database...')
|
||||
save_or_update(
|
||||
'update',
|
||||
app_dict,
|
||||
infoplist_dict,
|
||||
code_dict,
|
||||
bin_analysis_dict,
|
||||
all_files)
|
||||
update_scan_timestamp(app_dict['md5_hash'])
|
||||
else:
|
||||
logger.info('Saving to Database')
|
||||
save_or_update(
|
||||
'save',
|
||||
app_dict,
|
||||
infoplist_dict,
|
||||
code_dict,
|
||||
bin_analysis_dict,
|
||||
all_files)
|
||||
context = get_context_from_analysis(
|
||||
code_dict['domains'], [])
|
||||
|
||||
code_dict['api'] = {}
|
||||
code_dict['code_anal'] = {}
|
||||
code_dict['firebase'] = firebase_analysis(
|
||||
code_dict['urls_list'])
|
||||
code_dict['trackers'] = trackers
|
||||
context = save_get_ctx(
|
||||
app_dict,
|
||||
infoplist_dict,
|
||||
code_dict,
|
||||
bin_analysis_dict,
|
||||
all_files)
|
||||
bin_dict,
|
||||
all_files,
|
||||
rescan)
|
||||
context['virus_total'] = None
|
||||
if settings.VT_ENABLED:
|
||||
vt = VirusTotal.VirusTotal()
|
||||
@@ -197,6 +190,8 @@ def static_analyzer_ios(request, api=False):
|
||||
return context
|
||||
else:
|
||||
return render(request, template, context)
|
||||
elif file_type == 'dylib':
|
||||
return dylib_analysis(request, app_dict, rescan, api)
|
||||
elif file_type == 'ios':
|
||||
ios_zip_db = StaticAnalyzerIOS.objects.filter(
|
||||
MD5=app_dict['md5_hash'])
|
||||
@@ -208,7 +203,6 @@ def static_analyzer_ios(request, api=False):
|
||||
'md5_hash'] + '.zip' # NEW FILENAME
|
||||
app_dict['app_path'] = app_dir / app_dict['app_file']
|
||||
app_dict['app_path'] = app_dict['app_path'].as_posix()
|
||||
app_dict['app_dir'] = app_dir.as_posix() + '/'
|
||||
# ANALYSIS BEGINS - Already Unzipped
|
||||
app_dict['size'] = str(
|
||||
file_size(app_dict['app_path'])) + 'MB' # FILE SIZE
|
||||
@@ -222,7 +216,7 @@ def static_analyzer_ios(request, api=False):
|
||||
infoplist_dict = plist_analysis(app_dict['app_dir'], True)
|
||||
app_dict['appstore'] = app_search(infoplist_dict.get('id'))
|
||||
app_dict['secrets'] = get_plist_secrets(
|
||||
infoplist_dict['plist_xml'])
|
||||
app_dict['app_dir'])
|
||||
code_analysis_dic = ios_source_analysis(
|
||||
app_dict['app_dir'])
|
||||
ios_strs = strings_and_entropies(
|
||||
@@ -239,7 +233,7 @@ def static_analyzer_ios(request, api=False):
|
||||
list(set(code_analysis_dic['urls_list'])))
|
||||
# Extract Trackers from Domains
|
||||
trk = Trackers.Trackers(
|
||||
None, tools_dir)
|
||||
None, app_dict['tools_dir'])
|
||||
trackers = trk.get_trackers_domains_or_deps(
|
||||
code_analysis_dic['domains'], [])
|
||||
code_analysis_dic['trackers'] = trackers
|
||||
@@ -250,34 +244,15 @@ def static_analyzer_ios(request, api=False):
|
||||
'strings': list(ios_strs['strings']),
|
||||
'bin_info': {},
|
||||
'bin_type': code_analysis_dic['source_type'],
|
||||
'dylib_analysis': {},
|
||||
}
|
||||
# Saving to DB
|
||||
logger.info('Connecting to DB')
|
||||
if rescan:
|
||||
logger.info('Updating Database...')
|
||||
save_or_update(
|
||||
'update',
|
||||
app_dict,
|
||||
infoplist_dict,
|
||||
code_analysis_dic,
|
||||
fake_bin_dict,
|
||||
all_files)
|
||||
update_scan_timestamp(app_dict['md5_hash'])
|
||||
else:
|
||||
logger.info('Saving to Database')
|
||||
save_or_update(
|
||||
'save',
|
||||
app_dict,
|
||||
infoplist_dict,
|
||||
code_analysis_dic,
|
||||
fake_bin_dict,
|
||||
all_files)
|
||||
context = get_context_from_analysis(
|
||||
context = save_get_ctx(
|
||||
app_dict,
|
||||
infoplist_dict,
|
||||
code_analysis_dic,
|
||||
fake_bin_dict,
|
||||
all_files)
|
||||
all_files,
|
||||
rescan)
|
||||
context['appsec'] = get_ios_dashboard(context, True)
|
||||
context['average_cvss'] = get_avg_cvss(
|
||||
context['code_analysis'])
|
||||
@@ -287,22 +262,14 @@ def static_analyzer_ios(request, api=False):
|
||||
else:
|
||||
return render(request, template, context)
|
||||
else:
|
||||
msg = 'File Type not supported!'
|
||||
if api:
|
||||
return print_n_send_error_response(request, msg, True)
|
||||
else:
|
||||
return print_n_send_error_response(request, msg, False)
|
||||
msg = ('File Type not supported, '
|
||||
'Only IPA and DYLIB files are supported')
|
||||
return print_n_send_error_response(request, msg, api)
|
||||
else:
|
||||
msg = 'Hash match failed or Invalid file extension or file type'
|
||||
if api:
|
||||
return print_n_send_error_response(request, msg, True)
|
||||
else:
|
||||
return print_n_send_error_response(request, msg, False)
|
||||
return print_n_send_error_response(request, msg, api)
|
||||
except Exception as exp:
|
||||
logger.exception('Error Performing Static Analysis')
|
||||
msg = str(exp)
|
||||
exp_doc = exp.__doc__
|
||||
if api:
|
||||
return print_n_send_error_response(request, msg, True, exp_doc)
|
||||
else:
|
||||
return print_n_send_error_response(request, msg, False, exp_doc)
|
||||
return print_n_send_error_response(request, msg, api, exp_doc)
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
# -*- coding: utf_8 -*-
|
||||
"""Module for iOS String Analysis."""
|
||||
import io
|
||||
import logging
|
||||
|
||||
from mobsf.StaticAnalyzer.tools.strings import (
|
||||
strings_util,
|
||||
)
|
||||
from mobsf.StaticAnalyzer.views.common.entropy import (
|
||||
get_entropies,
|
||||
)
|
||||
from mobsf.StaticAnalyzer.views.common.shared_func import (
|
||||
url_n_email_extract,
|
||||
)
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def extract_urls_n_email(src, all_files, strings):
|
||||
"""IPA URL and Email Extraction."""
|
||||
email_n_file = []
|
||||
url_n_file = []
|
||||
url_list = []
|
||||
try:
|
||||
logger.info('Starting IPA URL and Email Extraction')
|
||||
all_files.append({'data': strings, 'name': 'IPA Strings Dump'})
|
||||
for file in all_files:
|
||||
if isinstance(file, dict):
|
||||
relative_src_path = file['name']
|
||||
dat = '\n'.join(file['data'])
|
||||
# Skip CodeResources and contents under Frameworks
|
||||
elif 'CodeResources' in file or '/Frameworks/' in file:
|
||||
continue
|
||||
elif file.endswith(('.nib', '.ttf', '.svg', '.woff2',
|
||||
'.png', '.dylib', '.mobileprovision',
|
||||
'Assets.car')):
|
||||
continue
|
||||
else:
|
||||
dat = ''
|
||||
relative_src_path = file.replace(src, '')
|
||||
with io.open(file,
|
||||
mode='r',
|
||||
encoding='utf8',
|
||||
errors='ignore') as flip:
|
||||
dat = flip.read()
|
||||
# Extract URLs and Emails from Plists
|
||||
urls, urls_nf, emails_nf = url_n_email_extract(
|
||||
dat, relative_src_path)
|
||||
url_list.extend(urls)
|
||||
url_n_file.extend(urls_nf)
|
||||
email_n_file.extend(emails_nf)
|
||||
except Exception:
|
||||
logger.exception('IPA URL and Email Extraction')
|
||||
return {
|
||||
'urls_list': list(set(url_list)),
|
||||
'urlnfile': url_n_file,
|
||||
'emailnfile': email_n_file,
|
||||
}
|
||||
|
||||
|
||||
def strings_on_binary(bin_path):
|
||||
"""Extract strings from binary."""
|
||||
try:
|
||||
return list(set(strings_util(bin_path.as_posix())))
|
||||
except Exception:
|
||||
logger.exception('Extracting strings from binary')
|
||||
|
||||
|
||||
def get_strings_metadata(app_dict, bin_dict, all_files, dy_list):
|
||||
"""Merge strings metadata."""
|
||||
# app_dict has secrets from plist secret analysis
|
||||
# bin_dict has strings from binary analysis
|
||||
urls_list = []
|
||||
urls_n_files = []
|
||||
emails_n_files = []
|
||||
secrets = []
|
||||
|
||||
# IPA URL and Email Extract
|
||||
str_meta = extract_urls_n_email(
|
||||
app_dict['bin_dir'],
|
||||
all_files['files_long'],
|
||||
bin_dict['strings'])
|
||||
urls_list = str_meta['urls_list']
|
||||
urls_n_files = str_meta['urlnfile']
|
||||
emails_n_files = str_meta['emailnfile']
|
||||
|
||||
if dy_list:
|
||||
# DYLIB (.dylib) by file
|
||||
dy_strings = []
|
||||
for dy in dy_list:
|
||||
for dy_file, s in dy.items():
|
||||
dy_strings.extend(s)
|
||||
dy_str = ' '.join(s)
|
||||
urls, urls_nf, emails_nf = url_n_email_extract(
|
||||
dy_str, dy_file)
|
||||
urls_list.extend(urls)
|
||||
urls_n_files.extend(urls_nf)
|
||||
emails_n_files.extend(emails_nf)
|
||||
secrets.extend(get_entropies(dy_str))
|
||||
|
||||
bin_dict['strings'].extend(dy_strings)
|
||||
bin_dict['strings'] = list(
|
||||
set(bin_dict['strings']))
|
||||
app_dict['secrets'].extend(secrets)
|
||||
app_dict['secrets'] = list(
|
||||
set(app_dict['secrets']))
|
||||
return {
|
||||
'urls_list': list(set(urls_list)),
|
||||
'urlnfile': urls_n_files,
|
||||
'emailnfile': emails_n_files,
|
||||
}
|
||||
@@ -27,13 +27,13 @@ from mobsf.MobSF.utils import (
|
||||
get_config_loc,
|
||||
print_n_send_error_response,
|
||||
)
|
||||
from mobsf.MobSF.views.home import update_scan_timestamp
|
||||
import mobsf.MalwareAnalyzer.views.VirusTotal as VirusTotal
|
||||
from mobsf.StaticAnalyzer.models import StaticAnalyzerWindows
|
||||
from mobsf.StaticAnalyzer.tools.strings import strings_util
|
||||
from mobsf.StaticAnalyzer.views.common.shared_func import (
|
||||
hash_gen,
|
||||
unzip,
|
||||
update_scan_timestamp,
|
||||
)
|
||||
from mobsf.StaticAnalyzer.views.windows.db_interaction import (
|
||||
get_context_from_analysis,
|
||||
|
||||
@@ -225,10 +225,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|zip|appx)$");
|
||||
var regex = new RegExp("^(.{1,300}?)\.(ipa|apk|apks|xapk|jar|aar|so|dylib|zip|appx)$");
|
||||
val = val.replace(/^.*[\\\/]/, '');
|
||||
if (!(regex.test(val))) {
|
||||
_('status').innerText = "Please upload APK/APKS/XAPK/JAR/AAR/SO/IPA/ZIP/APPX only!";
|
||||
_('status').innerText = "Please upload APK/APKS/XAPK/JAR/AAR/SO/IPA/DYLIB/ZIP/APPX only!";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -58,8 +58,10 @@
|
||||
{% if e.PACKAGE_NAME or e.APP_NAME %}
|
||||
<br/><strong>{{ e.APP_NAME }} {% if e.VERSION_NAME %} - {{ e.VERSION_NAME }} {% endif %}</strong>
|
||||
</br>{{ e.PACKAGE_NAME }}
|
||||
{% if '.so' != e.FILE_NAME|slice:"-3:"%}
|
||||
<p> <a href="../appsec_dashboard/{{ e.MD5 }}/" class="btn btn-sm btn-outline-primary" role="button"><i class="fas fa-user-shield"></i> MobSF Scorecard</a></p>
|
||||
{% if e.FILE_NAME|slice:"-6:" != '.dylib' %}
|
||||
{% if e.FILE_NAME|slice:"-3:" != '.so' %}
|
||||
<p> <a href="../appsec_dashboard/{{ e.MD5 }}/" class="btn btn-sm btn-outline-primary" role="button"><i class="fas fa-user-shield"></i> MobSF Scorecard</a></p>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<p><a class="btn btn-primary btn-sm" href="../{{ e.ANALYZER }}/?name={{e.FILE_NAME | urlencode}}&checksum={{e.MD5}}&type={{e.SCAN_TYPE}}"><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:"%}
|
||||
@@ -80,6 +82,7 @@
|
||||
{% 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>
|
||||
{% elif '.ipa' == e.FILE_NAME|slice:"-4:"%}<i class="fab fa-apple fa-3x"></i>
|
||||
{% elif '.dylib' == e.FILE_NAME|slice:"-6:"%}<i class="fa fa-th-large fa-3x"></i>
|
||||
{% elif '.zip' == e.FILE_NAME|slice:"-4:"%}<i class="fas fa-file-archive fa-3x"></i>
|
||||
{% elif '.appx' == e.FILE_NAME|slice:"-5:"%}<i class="fab fa-windows fa-3x"></i>
|
||||
{% endif %}
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
<td class="no_border"><h3>Scan Date:</h3></td>
|
||||
<td class="no_border"><h3>{{ timestamp }}</h3></td>
|
||||
</tr>
|
||||
{% if app_type not in 'so' %}
|
||||
{% if app_type not in 'so' %}
|
||||
{% if average_cvss %}
|
||||
<tr>
|
||||
<td class="no_border"><h3>Average CVSS Score:</h3></td>
|
||||
@@ -117,13 +117,13 @@
|
||||
</h3>
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% if trackers|key:"trackers" %}
|
||||
<tr>
|
||||
<td class="no_border"><h3>Trackers Detection:</h3></td>
|
||||
<td class="no_border"><h3><span {% if trackers.detected_trackers > 0 %} class="warning" {% endif %}>{{ trackers.detected_trackers }}/{{ trackers.total_trackers }}</span></h3></td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if virus_total and virus_total.items|length > 9 and 'request successfully queued' not in virus_total.verbose_msg %}
|
||||
<tr>
|
||||
<td class="no_border"><h3>VirusTotal Detection:</h3></td>
|
||||
@@ -944,6 +944,22 @@
|
||||
</table>
|
||||
{% endif %}
|
||||
|
||||
<!--Only for so-->
|
||||
{% if app_type in 'so' %}
|
||||
<h2><i class="fa fa-th"></i> SYMBOLS</h2>
|
||||
<table class="basic">
|
||||
<tbody>
|
||||
{% for val in file_analysis %}
|
||||
<tr>
|
||||
<td style="word-break:break-all;">
|
||||
{{ val }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
|
||||
{% if not playstore_details.error %}
|
||||
<h2><i class="fab fa-google-play"></i> PLAYSTORE INFORMATION </h2>
|
||||
|
||||
|
||||
@@ -43,9 +43,14 @@
|
||||
<div class="htext">IOS STATIC ANALYSIS REPORT </div>
|
||||
</div>
|
||||
</br></br></br>
|
||||
|
||||
<div class="app">
|
||||
{% if app_type not in 'Dylib' %}
|
||||
<img class="center" id="icon" src="{% if not appstore_details.error %}{{appstore_details.icon}}{% elif icon_found %}{{dwd_dir}}{{ md5 }}-icon.png{% else %}{{base_url}}{% static 'img/no_icon.png' %}{% endif %}" alt="app_icon"/>
|
||||
</div>
|
||||
{% else %}
|
||||
<img class="center" id="icon" src="{{base_url}}{% static 'img/no_icon.png' %}" alt="app_icon"/>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="center">
|
||||
<h1><i class="fab fa-apple"></i> {{ app_name }} {% if app_version %} ({{app_version}}) {% endif %}</h1>
|
||||
</div>
|
||||
@@ -64,6 +69,7 @@
|
||||
<td class="no_border"><h3>Scan Date:</h3></td>
|
||||
<td class="no_border"><h3>{{ timestamp }}</h3></td>
|
||||
</tr>
|
||||
{% if app_type not in 'Dylib' %}
|
||||
{% if average_cvss %}
|
||||
<tr>
|
||||
<td class="no_border"><h3>Average CVSS Score:</h3></td>
|
||||
@@ -116,6 +122,7 @@
|
||||
</h3>
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% if trackers|key:"trackers" %}
|
||||
<tr>
|
||||
<td class="no_border"><h3>Trackers Detection:</h3></td>
|
||||
@@ -129,6 +136,7 @@
|
||||
</tr>
|
||||
{% endif %}
|
||||
</table>
|
||||
{% if app_type not in 'Dylib' %}
|
||||
</br></br>
|
||||
</br></br>
|
||||
</br></br>
|
||||
@@ -157,7 +165,7 @@
|
||||
</table>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
{% endif %}
|
||||
<article id="contents">
|
||||
|
||||
|
||||
@@ -169,7 +177,7 @@
|
||||
<h5>SHA1:</h5> {{ sha1 }}</br>
|
||||
<h5>SHA256:</h5> {{ sha256 }}</br>
|
||||
|
||||
|
||||
{% if app_type not in 'Dylib' %}
|
||||
<h2><i class="fas fa-info"></i> APP INFORMATION</h2>
|
||||
|
||||
<h5>App Name:</h5> {{ app_name }}</br>
|
||||
@@ -181,7 +189,7 @@
|
||||
<h5>Platform Version:</h5> {{ platform }}</br>
|
||||
<h5>Min OS Version:</h5> {{ min_os_version }}</br>
|
||||
<h5>Supported Platforms:</h5> {% for pl in bundle_supported_platforms %} {{pl}}, {% endfor %}</br>
|
||||
|
||||
{% endif %}
|
||||
{% if binary_info %}
|
||||
<h2><i class="fas fa-ad"></i> BINARY INFORMATION </h2>
|
||||
|
||||
@@ -308,7 +316,7 @@
|
||||
|
||||
{% endif %}
|
||||
|
||||
|
||||
{% if app_type not in 'Dylib' %}
|
||||
<!--
|
||||
{% if file_analysis %}
|
||||
<h2><i class="fa fa-file"></i> FILE ANALYSIS</h2>
|
||||
@@ -338,6 +346,7 @@
|
||||
</table>
|
||||
{% endif %}
|
||||
-->
|
||||
{% endif %}
|
||||
{% if virus_total and virus_total.items|length > 9 and 'request successfully queued' not in virus_total.verbose_msg %}
|
||||
<h2><i class="fas fa-spider"></i> VIRUSTOTAL SCAN</h2>
|
||||
<p> <h5>{{ virus_total.positives }} / {{ virus_total.total }}</h5> AVs found this file Malicious! </p>
|
||||
@@ -441,7 +450,7 @@
|
||||
|
||||
{% endif %}
|
||||
{% if macho_analysis %}
|
||||
<h2><i class="fas fa-flag"></i> IPA BINARY ANALYSIS</h2>
|
||||
<h2><i class="fa fa-braille"></i> IPA BINARY ANALYSIS</h2>
|
||||
<table class="basic">
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -507,6 +516,62 @@
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
|
||||
{% if dylib_analysis %}
|
||||
<h2><i class="fa fa-flag"></i> DYNAMIC LIBRARY BINARY ANALYSIS</h2>
|
||||
<table class="basic">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>NO</th>
|
||||
<th>DYLIB</th>
|
||||
<th>NX</th>
|
||||
<th>STACK CANARY</th>
|
||||
<th>ARC</th>
|
||||
<th>RPATH</th>
|
||||
<th>CODE SIGNATURE</th>
|
||||
<th>ENCRYPTED</th>
|
||||
<th>SYMBOLS STRIPPED</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for dy in dylib_analysis %}
|
||||
<tr>
|
||||
<td>{{ forloop.counter }}</td>
|
||||
<td>{{dy.name}}</td>
|
||||
<td><b>{{dy.nx.has_nx}}</b>
|
||||
<br/>
|
||||
<span class="{% if dy.nx.severity == 'high' %}danger{% elif dy.nx.severity == 'warning' %}warning{% else %}info{% endif %}">{{dy.nx.severity}}</span><br/>
|
||||
<br/>{{dy.nx.description}}</td>
|
||||
<td><b>{{dy.stack_canary.has_canary}}</b>
|
||||
<br/>
|
||||
<span class="{% if dy.stack_canary.severity == 'high' %}danger{% elif dy.stack_canary.severity == 'warning' %}warning{% else %}info{% endif %}">{{dy.stack_canary.severity}}</span><br/>
|
||||
<br/>{{dy.stack_canary.description}}</td>
|
||||
<td><b>{{dy.arc.has_arc}}</b>
|
||||
<br/>
|
||||
<span class="{% if dy.arc.severity == 'high' %}danger{% elif dy.arc.severity == 'warning' %}warning{% else %}info{% endif %}">{{dy.arc.severity}}</span><br/>
|
||||
<br/>{{dy.arc.description}}</td>
|
||||
<td><b>{{dy.rpath.has_rpath}}</b>
|
||||
<br/>
|
||||
<span class="{% if dy.rpath.severity == 'high' %}danger{% elif dy.rpath.severity == 'warning' %}warning{% else %}info{% endif %}">{{dy.rpath.severity}}</span><br/>
|
||||
<br/>{{dy.rpath.description}}</td>
|
||||
<td><b>{{dy.code_signature.has_code_signature}}</b>
|
||||
<br/>
|
||||
<span class="{% if dy.code_signature.severity == 'high' %}danger{% elif dy.code_signature.severity == 'warning' %}warning{% else %}info{% endif %}">{{dy.code_signature.severity}}</span><br/>
|
||||
<br/>{{dy.code_signature.description}}</td>
|
||||
<td><b>{{dy.encrypted.is_encrypted}}</b>
|
||||
<br/>
|
||||
<span class="{% if dy.encrypted.severity == 'high' %}danger{% elif dy.encrypted.severity == 'warning' %}warning{% else %}info{% endif %}">{{dy.encrypted.severity}}</span><br/>
|
||||
<br/>{{dy.encrypted.description}}</td>
|
||||
<td><b>{{dy.symbol.is_stripped}}</b>
|
||||
<br/>
|
||||
<span class="{% if dy.symbol.severity == 'high' %}danger{% elif dy.symbol.severity == 'warning' %}warning{% else %}info{% endif %}">{{dy.symbol.severity}}</span><br/>
|
||||
<br/>{{dy.symbol.description}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
{% if app_type not in 'Dylib' %}
|
||||
{% if code_analysis %}
|
||||
<h2><i class="fas fa-code"></i> CODE ANALYSIS</h2>
|
||||
{% if code_analysis and 'summary' in code_analysis and code_analysis.summary|length > 0 %}
|
||||
@@ -585,6 +650,7 @@
|
||||
</table>
|
||||
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if domains %}
|
||||
|
||||
<h2><i class="fa fa-exclamation"></i> OFAC SANCTIONED COUNTRIES</h2>
|
||||
@@ -655,7 +721,9 @@
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
<!--
|
||||
|
||||
<!-- Display urls only for dylib files-->
|
||||
{% if app_type in 'Dylib' %}
|
||||
{% if urls %}
|
||||
<h2><i class="fas fa-globe"></i> URLS </h2>
|
||||
<table class="basic">
|
||||
@@ -681,7 +749,7 @@
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
-->
|
||||
{% endif %}
|
||||
{% if firebase_urls %}
|
||||
<h2><i class="fas fa-database"></i> FIREBASE DATABASES</h2>
|
||||
<table class="basic">
|
||||
@@ -789,6 +857,23 @@
|
||||
</table>
|
||||
{% endif %}
|
||||
|
||||
<!--Only for dylib-->
|
||||
{% if app_type in 'Dylib' %}
|
||||
<h2><i class="fa fa-th"></i> SYMBOLS</h2>
|
||||
<table class="basic">
|
||||
<tbody>
|
||||
{% for val in file_analysis %}
|
||||
<tr>
|
||||
<td style="word-break:break-all;">
|
||||
{{ val }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
|
||||
{% if app_type not in 'Dylib' %}
|
||||
{% if not appstore_details.error %}
|
||||
<h2><i class="fab fa-app-store-ios"></i> APP STORE INFORMATION </h2>
|
||||
|
||||
@@ -837,6 +922,7 @@
|
||||
<p>{{ appstore_details.description }}</p>
|
||||
</p>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</article>
|
||||
<article id="columns">
|
||||
</br>
|
||||
|
||||
@@ -264,6 +264,14 @@
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
{% if app_type in 'so' %}
|
||||
<li class="nav-item">
|
||||
<a href="#symbols" class="nav-link">
|
||||
<i class="fa fa-th nav-icon"></i>
|
||||
<p>Symbols</p>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if app_type not in 'so' %}
|
||||
<li class="nav-item has-treeview">
|
||||
<a href="#" class="nav-link">
|
||||
@@ -383,9 +391,10 @@
|
||||
<div class="card-body">
|
||||
<h5 class="card-title"></h5>
|
||||
<div class="row">
|
||||
{% if app_type not in 'so' %}
|
||||
|
||||
<div class="col-2">
|
||||
<p><strong><i class="fas fa-check-double"></i> APP SCORES</strong></p>
|
||||
{% if app_type not in 'so' %}
|
||||
<img id="app_icon" src="{% if icon_found %}/download/{{ md5 }}-icon.png{% else %}{% static 'img/no_icon.png' %}{% endif %}"/>
|
||||
{% if icon_hidden %}
|
||||
<strong>Hidden Icon!</strong>
|
||||
@@ -395,6 +404,7 @@
|
||||
<span class="badge bg-info">Average CVSS</span> <strong>{{ average_cvss }} </strong><br/>
|
||||
{% endif %}
|
||||
<span class="badge bg-{% if appsec.security_score < 40 %}danger{% elif appsec.security_score == 100 %}success{% else %}warning{% endif %}">Security Score</span> <strong>{{ appsec.security_score }}/100 </strong><br/>
|
||||
{% endif %}
|
||||
{% if trackers.detected_trackers > 0 %}
|
||||
<span class="badge bg-warning">Trackers Detection</span> <strong>{{ trackers.detected_trackers }}/{{ trackers.total_trackers }}</strong><br/>
|
||||
{% else %}
|
||||
@@ -408,10 +418,11 @@
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</p>
|
||||
{% if app_type not in 'so' %}
|
||||
<p> <a href="../appsec_dashboard/{{ md5 }}/" class="btn btn-outline-primary btn-sm" role="button"><i class="fas fa-user-shield"></i> MobSF Scorecard</a></p>
|
||||
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="col-6">
|
||||
<p><strong><i class="fas fa-box-open"></i> FILE INFORMATION </strong></p>
|
||||
<span class="badge bg-primary">File Name</span>
|
||||
@@ -2065,6 +2076,33 @@
|
||||
</div>
|
||||
</section>
|
||||
<!-- ===========================end strings ================================== -->
|
||||
{% if app_type in 'so' %}
|
||||
<a id="symbols" class="anchor"></a>
|
||||
<section class="content">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<p>
|
||||
<strong><i class="fa fa-th"></i> SYMBOLS</strong>
|
||||
</p>
|
||||
<div class="list-group">
|
||||
<p>
|
||||
{% for val in file_analysis %}
|
||||
{{ val }}<br/>
|
||||
{% endfor %}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- /.card -->
|
||||
</div>
|
||||
<!-- end row -->
|
||||
</div>
|
||||
</section>
|
||||
<!-- ===========================end symbols (only for shared object) ================================== -->
|
||||
{% endif %}
|
||||
{% if app_type not in 'so' %}
|
||||
<a id="activities" class="anchor"></a>
|
||||
<section class="content">
|
||||
|
||||
@@ -60,6 +60,7 @@
|
||||
</p>
|
||||
</a>
|
||||
</li>
|
||||
{% if app_type not in 'Dylib' %}
|
||||
<li class="nav-item">
|
||||
<a href="#url_schemes" class="nav-link">
|
||||
<i class="nav-icon fas fa-hashtag"></i>
|
||||
@@ -76,6 +77,7 @@
|
||||
</p>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
<li class="nav-item has-treeview">
|
||||
<a href="#" class="nav-link">
|
||||
<i class="nav-icon fas fa-shield-alt"></i>
|
||||
@@ -85,6 +87,7 @@
|
||||
</p>
|
||||
</a>
|
||||
<ul class="nav nav-treeview">
|
||||
{% if app_type not in 'Dylib' %}
|
||||
<li class="nav-item">
|
||||
<a href="#transport_security" class="nav-link">
|
||||
<i class="nav-icon fas fa-lock"></i>
|
||||
@@ -93,6 +96,7 @@
|
||||
</p>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
<li class="nav-item">
|
||||
<a href="#binary_code_analysis" class="nav-link">
|
||||
<i class="fa fa-code nav-icon"></i>
|
||||
@@ -101,14 +105,23 @@
|
||||
</p>
|
||||
</a>
|
||||
</li>
|
||||
{% if app_type not in 'Dylib' %}
|
||||
<li class="nav-item">
|
||||
<a href="#binary_analysis" class="nav-link">
|
||||
<i class="nav-icon fas fa-flag"></i>
|
||||
<i class="nav-icon fa fa-braille"></i>
|
||||
<p>
|
||||
Binary Analysis
|
||||
</p>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
<li class="nav-item">
|
||||
<a href="#dylib_analysis" class="nav-link">
|
||||
<i class="nav-icon fas fa-flag"></i>
|
||||
<p>Dylib Analysis</p>
|
||||
</a>
|
||||
</li>
|
||||
{% if app_type not in 'Dylib' %}
|
||||
<li class="nav-item">
|
||||
<a href="#file_analysis" class="nav-link">
|
||||
<i class="nav-icon fa fa-file"></i>
|
||||
@@ -117,6 +130,7 @@
|
||||
</p>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</li>
|
||||
<li class="nav-item has-treeview">
|
||||
@@ -193,6 +207,15 @@
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
{% if app_type in 'Dylib' %}
|
||||
<li class="nav-item">
|
||||
<a href="#symbols" class="nav-link">
|
||||
<i class="fa fa-th nav-icon"></i>
|
||||
<p>Symbols</p>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if app_type not in 'Dylib' %}
|
||||
<li class="nav-item">
|
||||
<a href="#libraries" class="nav-link">
|
||||
<i class="fab fa-buffer nav-icon"></i>
|
||||
@@ -207,6 +230,7 @@
|
||||
</p>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
<li class="nav-item">
|
||||
<a href="../pdf/?md5={{ md5 }}" class="nav-link">
|
||||
<i class="nav-icon fas fa-file-pdf"></i>
|
||||
@@ -258,12 +282,14 @@
|
||||
<div class="row">
|
||||
<div class="col-2">
|
||||
<p><strong><i class="fas fa-check-double"></i> APP SCORES</strong></p>
|
||||
{% if app_type not in 'Dylib' %}
|
||||
<img id="app_icon" src="{% if not appstore_details.error %}{{appstore_details.icon}}{% elif icon_found %}/download/{{ md5 }}-icon.png{% else %}{% static 'img/no_icon.png' %}{% endif %}"/>
|
||||
<p>
|
||||
{% if average_cvss %}
|
||||
<span class="badge bg-info">Average CVSS</span> <strong>{{ average_cvss }} </strong><br/>
|
||||
{% endif %}
|
||||
<span class="badge bg-{% if appsec.security_score < 40 %}danger{% elif appsec.security_score == 100 %}success{% else %}warning{% endif %}">Security Score</span> <strong>{{ appsec.security_score }}/100 </strong><br/>
|
||||
{% endif %}
|
||||
{% if trackers.detected_trackers > 0 %}
|
||||
<span class="badge bg-warning">Trackers Detection</span> <strong>{{ trackers.detected_trackers }}/{{ trackers.total_trackers }}</strong><br/>
|
||||
{% else %}
|
||||
@@ -277,7 +303,9 @@
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</p>
|
||||
{% if app_type not in 'Dylib' %}
|
||||
<p> <a href="../appsec_dashboard/{{ md5 }}/" class="btn btn-outline-primary btn-sm" role="button"><i class="fas fa-user-shield"></i> MobSF Scorecard</a></p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<p><strong><i class="fas fa-box-open"></i> FILE INFORMATION </strong></p>
|
||||
@@ -292,6 +320,7 @@
|
||||
<span class="badge bg-primary">SHA256</span>
|
||||
{{ sha256 }}<br/>
|
||||
</div>
|
||||
{% if app_type not in 'Dylib' %}
|
||||
<div class="col-3">
|
||||
<p><strong><i class="fas fa-info"></i> APP INFORMATION </strong></p>
|
||||
<span class="badge bg-primary">App Name</span>
|
||||
@@ -314,7 +343,8 @@
|
||||
{% for pl in bundle_supported_platforms %}
|
||||
{{pl}},
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="col-3">
|
||||
<p><strong><i class="fas fa-ad"></i> BINARY INFORMATION </strong></p>
|
||||
<span class="badge bg-primary">Arch</span>
|
||||
@@ -332,6 +362,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% if app_type not in 'Dylib' %}
|
||||
<!-- App Store -->
|
||||
{% if not appstore_details.error %}
|
||||
<div class="col-lg-12">
|
||||
@@ -376,6 +407,7 @@
|
||||
</div><!-- /.card -->
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<!-- /.col -->
|
||||
<!-- row and container-->
|
||||
</div>
|
||||
@@ -396,7 +428,7 @@
|
||||
<p>
|
||||
<strong><i class="fas fa-cog"></i> SCAN OPTIONS</strong>
|
||||
</p>
|
||||
<a role="button" class="btn btn-info" href="../static_analyzer_ios/?checksum={{ md5 }}&name={{ file_name }}&type=ipa&rescan=1" ><i class="fa fa-sync"></i> Rescan</a>
|
||||
<a role="button" class="btn btn-info" href="../static_analyzer_ios/?checksum={{ md5 }}&name={{ file_name }}&type={% if app_type in 'Dylib' %}dylib{% else %}ipa{% endif %}&rescan=1" ><i class="fa fa-sync"></i> Rescan</a>
|
||||
<a id="supbtn" onclick="list_suppressions()" role="button" class="btn btn-primary" data-target="#sup_list" data-toggle="modal" href="#"><i class="fa fa-list"></i> Manage Suppressions</a>
|
||||
</p>
|
||||
</div>
|
||||
@@ -409,10 +441,14 @@
|
||||
<strong><i class="fas fa-file-code"></i> DECOMPILED ASSETS</strong>
|
||||
</p>
|
||||
<p>
|
||||
{% if app_type not in 'Dylib' %}
|
||||
<a role="button" class="btn btn-primary" data-target="#mplist" data-toggle="modal" href="#"><i class="fa fa-list"></i> View Info.plist</a>
|
||||
{% endif %}
|
||||
<a role="button" class="btn btn-primary" data-target="#mstrings" data-toggle="modal" href="#"><i class="fas fa-font"></i> View Strings</a>
|
||||
{% if app_type not in 'Dylib' %}
|
||||
<a target="_blank" href="../view_file_ios/?file=classdump.txt&md5={{ md5 }}&type=ios" class="btn btn-warning" role="button"><i class="fa fa-code"></i> View Class Dump</a>
|
||||
<a href="../generate_downloads/?hash={{ md5 }}&file_type=ipa" class="btn btn-warning"> <i class="fa fa-download"></i> Download IPA</a>
|
||||
{% endif %}
|
||||
<a href="../generate_downloads/?hash={{ md5 }}&file_type={% if app_type in 'Dylib' %}dylib{% else %}ipa{% endif %}" class="btn btn-warning"> <i class="fa fa-download"></i> Download {% if app_type in 'Dylib' %}DYLIB{% else %}IPA{% endif %}</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -423,6 +459,7 @@
|
||||
</div>
|
||||
</section>
|
||||
<!-- ===========================end scan options================================== -->
|
||||
{% if app_type not in 'Dylib' %}
|
||||
<a id="url_schemes" class="anchor"></a>
|
||||
<section class="content">
|
||||
<div class="container-fluid">
|
||||
@@ -633,7 +670,7 @@
|
||||
</section>
|
||||
|
||||
<!-- ===========================end transport security ================================== -->
|
||||
|
||||
{% endif %}
|
||||
<a id="binary_code_analysis" class="anchor"></a>
|
||||
<section class="content">
|
||||
<div class="container-fluid">
|
||||
@@ -770,6 +807,7 @@
|
||||
</div>
|
||||
</section>
|
||||
<!-- ===========================end binary code analysis ================================== -->
|
||||
{% if app_type not in 'Dylib' %}
|
||||
<a id="binary_analysis" class="anchor"></a>
|
||||
<section class="content">
|
||||
<div class="container-fluid">
|
||||
@@ -778,7 +816,7 @@
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<p>
|
||||
<strong><i class="fa fa-flag"></i> IPA BINARY ANALYSIS</strong>
|
||||
<strong><i class="fa fa-braille"></i> IPA BINARY ANALYSIS</strong>
|
||||
</p>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered table-hover table-striped">
|
||||
@@ -856,6 +894,83 @@
|
||||
</section>
|
||||
|
||||
<!-- ===========================end binary analysis ================================== -->
|
||||
{% endif %}
|
||||
<a id="dylib_analysis" class="anchor"></a>
|
||||
<section class="content">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<p>
|
||||
<strong><i class="fa fa-flag"></i> DYNAMIC LIBRARY BINARY ANALYSIS</strong>
|
||||
</p>
|
||||
<div class="table-responsive">
|
||||
<table id="table_dylib" class="table table-bordered table-hover table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>NO</th>
|
||||
<th>DYLIB</th>
|
||||
<th>NX</th>
|
||||
<th>STACK CANARY</th>
|
||||
<th>ARC</th>
|
||||
<th>RPATH</th>
|
||||
<th>CODE SIGNATURE</th>
|
||||
<th>ENCRYPTED</th>
|
||||
<th>SYMBOLS STRIPPED</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% if not dylib_analysis %}
|
||||
No Dylibs found.
|
||||
{% endif %}
|
||||
{% for dy in dylib_analysis %}
|
||||
<tr>
|
||||
<td>{{ forloop.counter }}</td>
|
||||
<td>{{dy.name}}</td>
|
||||
<td><b>{{dy.nx.has_nx}}</b>
|
||||
<br/>
|
||||
<span class="badge bg-{% if dy.nx.severity == 'high' %}danger{% elif dy.nx.severity == 'warning' %}warning{% else %}info{% endif %}">{{dy.nx.severity}}</span>
|
||||
<br/>{{dy.nx.description}}</td>
|
||||
<td><b>{{dy.stack_canary.has_canary}}</b>
|
||||
<br/>
|
||||
<span class="badge bg-{% if dy.stack_canary.severity == 'high' %}danger{% elif dy.stack_canary.severity == 'warning' %}warning{% else %}info{% endif %}">{{dy.stack_canary.severity}}</span>
|
||||
<br/>{{dy.stack_canary.description}}</td>
|
||||
<td><b>{{dy.arc.has_arc}}</b>
|
||||
<br/>
|
||||
<span class="badge bg-{% if dy.arc.severity == 'high' %}danger{% elif dy.arc.severity == 'warning' %}warning{% else %}info{% endif %}">{{dy.arc.severity}}</span>
|
||||
<br/>{{dy.arc.description}}</td>
|
||||
<td><b>{{dy.rpath.has_rpath}}</b>
|
||||
<br/>
|
||||
<span class="badge bg-{% if dy.rpath.severity == 'high' %}danger{% elif dy.rpath.severity == 'warning' %}warning{% else %}info{% endif %}">{{dy.rpath.severity}}</span>
|
||||
<br/>{{dy.rpath.description}}</td>
|
||||
<td><b>{{dy.code_signature.has_code_signature}}</b>
|
||||
<br/>
|
||||
<span class="badge bg-{% if dy.code_signature.severity == 'high' %}danger{% elif dy.code_signature.severity == 'warning' %}warning{% else %}info{% endif %}">{{dy.code_signature.severity}}</span>
|
||||
<br/>{{dy.code_signature.description}}</td>
|
||||
<td><b>{{dy.encrypted.is_encrypted}}</b>
|
||||
<br/>
|
||||
<span class="badge bg-{% if dy.encrypted.severity == 'high' %}danger{% elif dy.encrypted.severity == 'warning' %}warning{% else %}info{% endif %}">{{dy.encrypted.severity}}</span>
|
||||
<br/>{{dy.encrypted.description}}</td>
|
||||
<td><b>{{dy.symbol.is_stripped}}</b>
|
||||
<br/>
|
||||
<span class="badge bg-{% if dy.symbol.severity == 'high' %}danger{% elif dy.symbol.severity == 'warning' %}warning{% else %}info{% endif %}">{{dy.symbol.severity}}</span>
|
||||
<br/>{{dy.symbol.description}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div><!-- /.card -->
|
||||
</div>
|
||||
<!-- end row -->
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<!-- ===========================end dylib analysis ================================== -->
|
||||
{% if app_type not in 'Dylib' %}
|
||||
<a id="file_analysis" class="anchor"></a>
|
||||
<section class="content">
|
||||
<div class="container-fluid">
|
||||
@@ -906,6 +1021,7 @@
|
||||
</div>
|
||||
</section>
|
||||
<!-- ===========================end file analysis ================================== -->
|
||||
{% endif %}
|
||||
{% if virus_total %}
|
||||
<a id="virus_total" class="anchor"></a>
|
||||
<section class="content">
|
||||
@@ -1283,6 +1399,34 @@
|
||||
</div>
|
||||
</section>
|
||||
<!-- ===========================end secrets ================================== -->
|
||||
{% if app_type in 'Dylib' %}
|
||||
<a id="symbols" class="anchor"></a>
|
||||
<section class="content">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<p>
|
||||
<strong><i class="fa fa-th"></i> SYMBOLS</strong>
|
||||
</p>
|
||||
<div class="list-group">
|
||||
<p>
|
||||
{% for val in file_analysis %}
|
||||
{{ val }}<br/>
|
||||
{% endfor %}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- /.card -->
|
||||
</div>
|
||||
<!-- end row -->
|
||||
</div>
|
||||
</section>
|
||||
<!-- ===========================end symbols (only for dylibs) ================================== -->
|
||||
{% endif %}
|
||||
{% if app_type not in 'Dylib' %}
|
||||
<a id="libraries" class="anchor"></a>
|
||||
<section class="content">
|
||||
<div class="container-fluid">
|
||||
@@ -1334,9 +1478,11 @@
|
||||
</div>
|
||||
</section>
|
||||
<!-- ===========================end files ================================== -->
|
||||
{% endif %}
|
||||
<!-- end of contents -->
|
||||
</div>
|
||||
|
||||
{% if app_type not in 'Dylib' %}
|
||||
<!-- Modal -->
|
||||
|
||||
<div class="modal" id="mplist" tabindex="-1" role="dialog">
|
||||
@@ -1358,7 +1504,7 @@
|
||||
</div>
|
||||
<!-- /.modal-dialog -->
|
||||
</div>
|
||||
|
||||
{% endif %}
|
||||
<div class="modal" id="mstrings" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog modal-xl">
|
||||
<div class="modal-content">
|
||||
|
||||
Reference in New Issue
Block a user