[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:
Ajin Abraham
2023-08-18 17:53:45 -07:00
committed by GitHub
parent b1be6f54da
commit e08524fe81
31 changed files with 933 additions and 420 deletions
+1
View File
@@ -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
View File
@@ -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 = """
__ __ _ ____ _____ _____ _____
| \/ | ___ | |__/ ___|| ___|_ _|___ /|___ |
+3
View File
@@ -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
View File
@@ -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
+8
View File
@@ -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'))
+13 -3
View File
@@ -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)
+10
View File
@@ -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')
+8 -20
View File
@@ -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
+1
View File
@@ -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,
)
+3 -38
View File
@@ -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(
+16 -40
View File
@@ -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)
+153
View File
@@ -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)
+70 -103
View File
@@ -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)
+112
View File
@@ -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,
+2 -2
View File
@@ -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;
+5 -2
View File
@@ -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}}&amp;checksum={{e.MD5}}&amp;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 %}
+18 -2
View File
@@ -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>
+94 -8
View File
@@ -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> &nbsp; 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 }}&amp;name={{ file_name }}&amp;type=ipa&amp;rescan=1" ><i class="fa fa-sync"></i> Rescan</a>
<a role="button" class="btn btn-info" href="../static_analyzer_ios/?checksum={{ md5 }}&amp;name={{ file_name }}&amp;type={% if app_type in 'Dylib' %}dylib{% else %}ipa{% endif %}&amp;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&amp;md5={{ md5 }}&amp;type=ios" class="btn btn-warning" role="button"><i class="fa fa-code"></i> View Class Dump</a>
<a href="../generate_downloads/?hash={{ md5 }}&amp;file_type=ipa" class="btn btn-warning"> <i class="fa fa-download"></i> Download IPA</a>
{% endif %}
<a href="../generate_downloads/?hash={{ md5 }}&amp;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">