Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[EFR] QA Request #2306

Merged
merged 6 commits into from
Dec 16, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion mobsf/MobSF/init.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

logger = logging.getLogger(__name__)

VERSION = '3.8.6'
VERSION = '3.8.7'
BANNER = """
__ __ _ ____ _____ _____ ___
| \/ | ___ | |__/ ___|| ___|_ _|___ / ( _ )
Expand Down
5 changes: 4 additions & 1 deletion mobsf/MobSF/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,10 @@
# App Compare
re_path(r'^compare/(?P<hash1>[0-9a-f]{32})/(?P<hash2>[0-9a-f]{32})/$',
shared_func.compare_apps),

# Relative Shared & Dynamic Library scan
re_path(r'^scan_library/(?P<checksum>[0-9a-f]{32})$',
shared_func.scan_library,
name='scan_library'),
# Dynamic Analysis
re_path(r'^android/dynamic_analysis/$',
dz.android_dynamic_analysis,
Expand Down
17 changes: 17 additions & 0 deletions mobsf/MobSF/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import sqlite3
import unicodedata
import threading
from pathlib import Path
from distutils.version import StrictVersion

import distro
Expand Down Expand Up @@ -491,6 +492,8 @@ def update_local_db(db_name, url, local_file):
else:
logger.info('%s Database is up-to-date', db_name)
return update
except requests.exceptions.ReadTimeout:
logger.warning('Failed to download %s DB.', db_name)
except Exception:
logger.exception('[ERROR] %s DB Update', db_name)
return update
Expand Down Expand Up @@ -758,6 +761,20 @@ def replace(value, arg):
return value.replace(what, to)


def relative_path(value):
"""Show relative path to two parents."""
if '/' in value:
sep = '/'
elif '\\\\' in value:
sep = '\\\\'
elif '\\' in value:
sep = '\\'
if value.count(sep) < 2:
return value
path = Path(value)
return path.relative_to(path.parent.parent).as_posix()


def pretty_json(value):
"""Pretty print JSON."""
try:
Expand Down
5 changes: 4 additions & 1 deletion mobsf/StaticAnalyzer/views/android/jar_aar.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,10 @@ def common_analysis(request, app_dic, rescan, api, analysis_type):
'certificate_summary': {},
}
app_dic['real_name'] = ''
elf_dict = library_analysis(app_dic['app_dir'], 'elf')
elf_dict = library_analysis(
app_dic['app_dir'],
app_dic['md5'],
'elf')
tracker = Trackers.Trackers(
app_dic['app_dir'],
app_dic['tools_dir'])
Expand Down
5 changes: 4 additions & 1 deletion mobsf/StaticAnalyzer/views/android/so.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,10 @@ def so_analysis(request, app_dic, rescan, api):
'certificate_summary': {},
}
app_dic['real_name'] = ''
elf_dict = library_analysis(app_dic['app_dir'], 'elf')
elf_dict = library_analysis(
app_dic['app_dir'],
app_dic['md5'],
'elf')
# File Analysis is used to store symbols from so
app_dic['certz'] = get_symbols(
elf_dict['elf_symbols'])
Expand Down
7 changes: 6 additions & 1 deletion mobsf/StaticAnalyzer/views/android/static_analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
is_md5,
key,
print_n_send_error_response,
relative_path,
)
from mobsf.StaticAnalyzer.models import (
RecentScansDB,
Expand Down Expand Up @@ -92,6 +93,7 @@

register.filter('key', key)
register.filter('android_component', android_component)
register.filter('relative_path', relative_path)


def static_analyzer(request, checksum, api=False):
Expand Down Expand Up @@ -211,7 +213,10 @@ def static_analyzer(request, checksum, api=False):
# apktool should run before this
get_icon_apk(apk, app_dic)

elf_dict = library_analysis(app_dic['app_dir'], 'elf')
elf_dict = library_analysis(
app_dic['app_dir'],
app_dic['md5'],
'elf')
cert_dic = cert_info(
apk,
app_dic,
Expand Down
5 changes: 4 additions & 1 deletion mobsf/StaticAnalyzer/views/common/a.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,10 @@ def a_analysis(request, app_dict, rescan, api):
'framework_analysis': {},
}
# Analyze static library
slib = library_analysis(app_dict['bin_dir'], 'ar')
slib = library_analysis(
app_dict['bin_dir'],
app_dict['md5_hash'],
'ar')
bin_dict['bin_info']['arch'] = slib['ar_a']
bin_dict['dylib_analysis'] = slib['ar_analysis']
# Store Symbols in File Analysis
Expand Down
125 changes: 108 additions & 17 deletions mobsf/StaticAnalyzer/views/common/binary/elf.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,33 @@
# !/usr/bin/python
# coding=utf-8
import shutil
import subprocess

import lief

from mobsf.StaticAnalyzer.views.common.binary.strings import (
strings_on_binary,
)


NA = 'Not Applicable'
NO_RELRO = 'No RELRO'
PARTIAL_RELRO = 'Partial RELRO'
FULL_RELRO = 'Full RELRO'
INFO = 'info'
WARNING = 'warning'
HIGH = 'high'


def nm_is_debug_symbol_stripped(elf_file):
"""Check if debug symbols are stripped using OS utility."""
# https://linux.die.net/man/1/nm
out = subprocess.check_output(
[shutil.which('nm'), '--debug-syms', elf_file],
stderr=subprocess.STDOUT)
return b'no debug symbols' in out


class ELFChecksec:
def __init__(self, elf_file, so_rel):
self.elf_path = elf_file.as_posix()
Expand All @@ -20,13 +41,13 @@ def checksec(self):
return
is_nx = self.is_nx()
if is_nx:
severity = 'info'
severity = INFO
desc = (
'The binary has NX bit set. This marks a '
'memory page non-executable making attacker '
'injected shellcode non-executable.')
else:
severity = 'high'
severity = HIGH
desc = (
'The binary does not have NX bit set. NX bit '
'offer protection against exploitation of memory corruption '
Expand All @@ -40,15 +61,15 @@ def checksec(self):
}
has_canary = self.has_canary()
if has_canary:
severity = 'info'
severity = INFO
desc = (
'This binary has a stack canary value '
'added to the stack so that it will be overwritten by '
'a stack buffer that overflows the return address. '
'This allows detection of overflows by verifying the '
'integrity of the canary before function return.')
else:
severity = 'high'
severity = HIGH
desc = (
'This binary does not have a stack '
'canary value added to the stack. Stack canaries '
Expand All @@ -62,9 +83,47 @@ def checksec(self):
'severity': severity,
'description': desc,
}
relro = self.relro()
if relro == NA:
severity = INFO
desc = ('RELRO checks are not applicable for '
'Flutter/Dart binaries')
elif relro == FULL_RELRO:
severity = INFO
desc = (
'This shared object has full RELRO '
'enabled. RELRO ensures that the GOT cannot be '
'overwritten in vulnerable ELF binaries. '
'In Full RELRO, the entire GOT (.got and '
'.got.plt both) is marked as read-only.')
elif relro == PARTIAL_RELRO:
severity = WARNING
desc = (
'This shared object has partial RELRO '
'enabled. RELRO ensures that the GOT cannot be '
'overwritten in vulnerable ELF binaries. '
'In partial RELRO, the non-PLT part of the GOT '
'section is read only but .got.plt is still '
'writeable. Use the option -z,relro,-z,now to '
'enable full RELRO.')
else:
severity = HIGH
desc = (
'This shared object does not have RELRO '
'enabled. The entire GOT (.got and '
'.got.plt both) are writable. Without this compiler '
'flag, buffer overflows on a global variable can '
'overwrite GOT entries. Use the option '
'-z,relro,-z,now to enable full RELRO and only '
'-z,relro to enable partial RELRO.')
elf_dict['relocation_readonly'] = {
'relro': relro,
'severity': severity,
'description': desc,
}
rpath = self.rpath()
if rpath:
severity = 'high'
severity = HIGH
desc = (
'The binary has RPATH set. In certain cases, '
'an attacker can abuse this feature to run arbitrary '
Expand All @@ -75,7 +134,7 @@ def checksec(self):
'compiler option -rpath to remove RPATH.')
rpt = rpath.rpath
else:
severity = 'info'
severity = INFO
desc = (
'The binary does not have run-time search path '
'or RPATH set.')
Expand All @@ -87,7 +146,7 @@ def checksec(self):
}
runpath = self.runpath()
if runpath:
severity = 'high'
severity = HIGH
desc = (
'The binary has RUNPATH set. In certain cases, '
'an attacker can abuse this feature and or modify '
Expand All @@ -99,7 +158,7 @@ def checksec(self):
'option --enable-new-dtags,-rpath to remove RUNPATH.')
rnp = runpath.runpath
else:
severity = 'info'
severity = INFO
desc = (
'The binary does not have RUNPATH set.')
rnp = runpath
Expand All @@ -110,14 +169,14 @@ def checksec(self):
}
fortified_functions = self.fortify()
if fortified_functions:
severity = 'info'
severity = INFO
desc = ('The binary has the '
f'following fortified functions: {fortified_functions}')
else:
if self.is_dart():
severity = 'info'
severity = INFO
else:
severity = 'warning'
severity = WARNING
desc = ('The binary does not have any '
'fortified functions. Fortified functions '
'provides buffer overflow checks against '
Expand All @@ -133,10 +192,10 @@ def checksec(self):
}
is_stripped = self.is_symbols_stripped()
if is_stripped:
severity = 'info'
severity = INFO
desc = 'Symbols are stripped.'
else:
severity = 'warning'
severity = WARNING
desc = 'Symbols are available.'
elf_dict['symbol'] = {
'is_stripped': is_stripped,
Expand Down Expand Up @@ -176,6 +235,34 @@ def has_canary(self):
pass
return False

def relro(self):
try:
gnu_relro = lief.ELF.SEGMENT_TYPES.GNU_RELRO
bind_now_flag = lief.ELF.DYNAMIC_FLAGS.BIND_NOW
flags_tag = lief.ELF.DYNAMIC_TAGS.FLAGS
flags1_tag = lief.ELF.DYNAMIC_TAGS.FLAGS_1
now_flag = lief.ELF.DYNAMIC_FLAGS_1.NOW

if self.is_dart():
return NA

if not self.elf.get(gnu_relro):
return NO_RELRO

flags = self.elf.get(flags_tag)
bind_now = flags and bind_now_flag in flags

flags1 = self.elf.get(flags1_tag)
now = flags1 and now_flag in flags1

if bind_now or now:
return FULL_RELRO
else:
return PARTIAL_RELRO
except lief.not_found:
pass
return NO_RELRO

def rpath(self):
try:
rpath = lief.ELF.DYNAMIC_TAGS.RPATH
Expand All @@ -191,10 +278,14 @@ def runpath(self):
return False

def is_symbols_stripped(self):
for i in self.elf.static_symbols:
if i:
return False
return True
try:
return nm_is_debug_symbol_stripped(
self.elf_path)
except Exception:
for i in self.elf.static_symbols:
if i:
return False
return True

def fortify(self):
fortified_funcs = []
Expand Down
Loading
Loading