mirror of
https://github.com/MobSF/Mobile-Security-Framework-MobSF.git
synced 2026-10-12 02:06:59 +00:00
Improve error handling and logging in MalwareDomainCheck (#2555)
* Improve error handling and logging in MalwareDomainCheck --------- Co-authored-by: Saurabh <[email protected]> Co-authored-by: Ajin Abraham <[email protected]>
This commit is contained in:
co-authored by
Saurabh
Ajin Abraham
parent
4b004f3817
commit
457d1e61f3
@@ -2,6 +2,7 @@
|
|||||||
# Module for Malware Analysis
|
# Module for Malware Analysis
|
||||||
import io
|
import io
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
import re
|
import re
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from socket import (
|
from socket import (
|
||||||
@@ -107,13 +108,28 @@ class MalwareDomainCheck:
|
|||||||
self.IP2Loc.close()
|
self.IP2Loc.close()
|
||||||
|
|
||||||
def malware_check(self):
|
def malware_check(self):
|
||||||
|
"""Check domains against malware database."""
|
||||||
try:
|
try:
|
||||||
mal_db = self.malwaredomainlist
|
mal_db = self.malwaredomainlist
|
||||||
|
|
||||||
|
if not mal_db.exists():
|
||||||
|
logger.warning('Malware domain list not found: %s', mal_db)
|
||||||
|
return
|
||||||
|
|
||||||
|
if not os.access(mal_db, os.R_OK):
|
||||||
|
logger.error('Insufficient permissions to read: %s', mal_db)
|
||||||
|
return
|
||||||
|
|
||||||
with io.open(mal_db,
|
with io.open(mal_db,
|
||||||
mode='r',
|
mode='r',
|
||||||
encoding='utf8',
|
encoding='utf8',
|
||||||
errors='ignore') as flip:
|
errors='ignore') as flip:
|
||||||
entry_list = flip.readlines()
|
entry_list = flip.readlines()
|
||||||
|
|
||||||
|
if not entry_list:
|
||||||
|
logger.warning('Malware domain database is empty')
|
||||||
|
return
|
||||||
|
|
||||||
for entry in entry_list:
|
for entry in entry_list:
|
||||||
enlist = entry.split('","')
|
enlist = entry.split('","')
|
||||||
if len(enlist) > 5:
|
if len(enlist) > 5:
|
||||||
@@ -129,17 +145,38 @@ class MalwareDomainCheck:
|
|||||||
and (len(dmn_url) > 1))
|
and (len(dmn_url) > 1))
|
||||||
or details_dict['ip'].startswith(domain)):
|
or details_dict['ip'].startswith(domain)):
|
||||||
self.result[domain] = details_dict
|
self.result[domain] = details_dict
|
||||||
|
except FileNotFoundError:
|
||||||
|
logger.error('Malware domain database file not found: %s', mal_db)
|
||||||
|
except PermissionError:
|
||||||
|
logger.error('Permission denied accessing malware database: %s', mal_db)
|
||||||
|
except IOError:
|
||||||
|
logger.exception('I/O error reading malware database')
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.exception('[ERROR] Performing Malware check')
|
logger.exception('[ERROR] Performing Malware check')
|
||||||
|
|
||||||
def maltrail_check(self):
|
def maltrail_check(self):
|
||||||
|
"""Check domains against maltrail database."""
|
||||||
try:
|
try:
|
||||||
mal_db = self.maltrail
|
mal_db = self.maltrail
|
||||||
|
|
||||||
|
if not mal_db.exists():
|
||||||
|
logger.warning('Maltrail database not found: %s', mal_db)
|
||||||
|
return
|
||||||
|
|
||||||
|
if not os.access(mal_db, os.R_OK):
|
||||||
|
logger.error('Insufficient permissions to read: %s', mal_db)
|
||||||
|
return
|
||||||
|
|
||||||
with io.open(mal_db,
|
with io.open(mal_db,
|
||||||
mode='r',
|
mode='r',
|
||||||
encoding='utf8',
|
encoding='utf8',
|
||||||
errors='ignore') as flip:
|
errors='ignore') as flip:
|
||||||
entry_list = flip.read().splitlines()
|
entry_list = flip.read().splitlines()
|
||||||
|
|
||||||
|
if not entry_list:
|
||||||
|
logger.warning('Maltrail database is empty')
|
||||||
|
return
|
||||||
|
|
||||||
for domain in self.domainlist:
|
for domain in self.domainlist:
|
||||||
if domain in entry_list:
|
if domain in entry_list:
|
||||||
self.result[domain] = {
|
self.result[domain] = {
|
||||||
@@ -148,36 +185,71 @@ class MalwareDomainCheck:
|
|||||||
'desc': 'Malicious Domain tagged by Maltrail',
|
'desc': 'Malicious Domain tagged by Maltrail',
|
||||||
'bad': 'yes',
|
'bad': 'yes',
|
||||||
}
|
}
|
||||||
|
except FileNotFoundError:
|
||||||
|
logger.error('Maltrail database file not found: %s', mal_db)
|
||||||
|
except PermissionError:
|
||||||
|
logger.error('Permission denied accessing maltrail database: %s', mal_db)
|
||||||
|
except IOError:
|
||||||
|
logger.exception('I/O error reading maltrail database')
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.exception('[ERROR] Performing Maltrail Check')
|
logger.exception('[ERROR] Performing Maltrail Check')
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
if is_internet_available():
|
"""Update malware databases with connection checks."""
|
||||||
self.update_maltrail_db()
|
try:
|
||||||
else:
|
if is_internet_available():
|
||||||
logger.warning('Internet not available. '
|
logger.info('Checking for malware database updates')
|
||||||
'Skipping Malware Database Update.')
|
self.update_maltrail_db()
|
||||||
|
else:
|
||||||
|
logger.warning('Internet not available. '
|
||||||
|
'Skipping Malware Database Update.')
|
||||||
|
except Exception:
|
||||||
|
logger.exception('Failed to update malware databases')
|
||||||
|
|
||||||
def scan(self, checksum, urls):
|
def scan(self, checksum, urls):
|
||||||
|
"""Scan method."""
|
||||||
if not settings_enabled('DOMAIN_MALWARE_SCAN'):
|
if not settings_enabled('DOMAIN_MALWARE_SCAN'):
|
||||||
logger.info('Domain Malware check disabled in settings')
|
logger.info('Domain Malware check disabled in settings')
|
||||||
return self.result
|
return self.result
|
||||||
|
|
||||||
msg = 'Performing Malware check on extracted domains'
|
msg = 'Performing Malware check on extracted domains'
|
||||||
urls = [u.lower() for u in urls]
|
urls = [u.lower() for u in urls]
|
||||||
append_scan_status(checksum, msg)
|
append_scan_status(checksum, msg)
|
||||||
self.domainlist = get_domains(urls)
|
|
||||||
if self.domainlist:
|
try:
|
||||||
|
self.domainlist = get_domains(urls)
|
||||||
|
if not self.domainlist:
|
||||||
|
logger.info('No domains found to analyze')
|
||||||
|
return self.result
|
||||||
|
|
||||||
|
logger.info('Analyzing %d domains', len(self.domainlist))
|
||||||
self.update()
|
self.update()
|
||||||
self.malware_check()
|
self.malware_check()
|
||||||
self.maltrail_check()
|
self.maltrail_check()
|
||||||
self.gelocation()
|
self.gelocation()
|
||||||
|
|
||||||
|
# Log analysis results
|
||||||
|
malicious_count = sum(1 for domain in self.result.values()
|
||||||
|
if domain.get('bad') == 'yes')
|
||||||
|
logger.info(
|
||||||
|
'Malware analysis completed: %d malicious domains found',
|
||||||
|
malicious_count)
|
||||||
|
|
||||||
|
except Exception as exp:
|
||||||
|
logger.exception('Critical error during domain malware check')
|
||||||
|
append_scan_status(checksum, 'Domain malware check failed', repr(exp))
|
||||||
|
|
||||||
return self.result
|
return self.result
|
||||||
|
|
||||||
|
|
||||||
# Helper Functions
|
# Helper Functions
|
||||||
|
|
||||||
def verify_domain(checkeddom):
|
def verify_domain(checkeddom):
|
||||||
|
"""Verify domain."""
|
||||||
try:
|
try:
|
||||||
|
if not checkeddom:
|
||||||
|
return False
|
||||||
|
|
||||||
if (len(checkeddom) > 2
|
if (len(checkeddom) > 2
|
||||||
and '.' in checkeddom
|
and '.' in checkeddom
|
||||||
and (checkeddom.endswith('.') is False
|
and (checkeddom.endswith('.') is False
|
||||||
@@ -187,6 +259,7 @@ def verify_domain(checkeddom):
|
|||||||
return False
|
return False
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.exception('[ERROR] Verifying Domain')
|
logger.exception('[ERROR] Verifying Domain')
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def get_netloc(url):
|
def get_netloc(url):
|
||||||
@@ -202,24 +275,37 @@ def get_netloc(url):
|
|||||||
return domain
|
return domain
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.exception('[ERROR] Extracting Domain form URL')
|
logger.exception('[ERROR] Extracting Domain form URL')
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def sanitize_domain(domain):
|
def sanitize_domain(domain):
|
||||||
"""Sanitize domain to be RFC1034 compliant."""
|
"""Sanitize domain to be RFC1034 compliant."""
|
||||||
domain = domain.split('_')[0]
|
try:
|
||||||
domain = re.sub(r'[^\w^\.^\-]', '', domain)
|
if not domain:
|
||||||
if domain.startswith('-'):
|
return domain
|
||||||
domain = sanitize_domain(domain[1:])
|
|
||||||
elif domain.endswith('-'):
|
domain = domain.split('_')[0]
|
||||||
domain = sanitize_domain(domain[:-1])
|
domain = re.sub(r'[^\w^\.^\-]', '', domain)
|
||||||
|
if domain.startswith('-'):
|
||||||
|
domain = sanitize_domain(domain[1:])
|
||||||
|
elif domain.endswith('-'):
|
||||||
|
domain = sanitize_domain(domain[:-1])
|
||||||
|
except Exception:
|
||||||
|
logger.exception('[ERROR] Sanitizing domain')
|
||||||
return domain
|
return domain
|
||||||
|
|
||||||
|
|
||||||
def get_domains(urls):
|
def get_domains(urls):
|
||||||
"""Get Domains."""
|
"""Get Domains."""
|
||||||
|
domains = set()
|
||||||
try:
|
try:
|
||||||
domains = set()
|
if not urls:
|
||||||
|
return domains
|
||||||
|
|
||||||
for url in urls:
|
for url in urls:
|
||||||
|
if not url:
|
||||||
|
continue
|
||||||
|
|
||||||
parse_uri = urlparse(url)
|
parse_uri = urlparse(url)
|
||||||
if not parse_uri.scheme:
|
if not parse_uri.scheme:
|
||||||
url = '//' + url
|
url = '//' + url
|
||||||
@@ -228,6 +314,6 @@ def get_domains(urls):
|
|||||||
'{uri.hostname}'.format(uri=parse_uri))
|
'{uri.hostname}'.format(uri=parse_uri))
|
||||||
if verify_domain(domain):
|
if verify_domain(domain):
|
||||||
domains.add(domain)
|
domains.add(domain)
|
||||||
return domains
|
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.exception('[ERROR] Extracting Domain form URL')
|
logger.exception('[ERROR] Extracting Domain form URL')
|
||||||
|
return domains
|
||||||
|
|||||||
Reference in New Issue
Block a user