-
Notifications
You must be signed in to change notification settings - Fork 1.7k
🔨 Merge the MobSF scanner #12501
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
base: dev
Are you sure you want to change the base?
🔨 Merge the MobSF scanner #12501
Conversation
🔴 Risk threshold exceeded.This pull request involves configuration changes for scanner support, primarily updating MobSF Scan integration in the settings, with a potential information injection risk in the MobSF report processing that may require additional input validation and sanitization.
|
Vulnerability | Configured Codepaths Edit |
---|---|
Description | Sensitive edits detected for this file. Sensitive file paths and allowed authors can be configured in .dryrunsecurity.yaml . |
⚠️ Configuration Change Risk in dojo/settings/settings.dist.py
Vulnerability | Configuration Change Risk |
---|---|
Description | The suggested vulnerability highlights a potential reduction in security monitoring capabilities due to the removal of scanner configurations for Nexpose and SonarQube. However, this is not a security vulnerability in the traditional sense, but rather an operational configuration change. The addition of the MobSF Scan configuration suggests an intentional update to the scanner ecosystem. This does not represent a security risk that requires remediation through code changes. |
django-DefectDojo/dojo/settings/settings.dist.py
Lines 1262 to 1267 in 1a999ff
"Dockle Scan": ["title", "description", "vuln_id_from_tool"], | |
"Dependency Track Finding Packaging Format (FPF) Export": ["component_name", "component_version", "vulnerability_ids"], | |
"Horusec Scan": ["title", "description", "file_path", "line"], | |
"Tenable Scan": ["title", "severity", "vulnerability_ids", "cwe", "description"], | |
"Nexpose Scan": ["title", "severity", "vulnerability_ids", "cwe"], | |
# possible improvement: in the scanner put the library name into file_path, then dedup on cwe + file_path + severity |
⚠️ Configuration Change Risk in dojo/settings/settings.dist.py
Vulnerability | Configuration Change Risk |
---|---|
Description | Similar to the previous hunk, this is an intentional configuration update for scanner support. The removal of existing scanner configurations and addition of MobSF Scan appears to be a deliberate modification to the application's scanner integration. This does not constitute a security vulnerability requiring immediate action. |
django-DefectDojo/dojo/settings/settings.dist.py
Lines 1322 to 1328 in 1a999ff
"HCLAppScan XML": ["title", "description"], | |
"HCL AppScan on Cloud SAST XML": ["title", "file_path", "line", "severity"], | |
"KICS Scan": ["file_path", "line", "severity", "description", "title"], | |
"MobSF Scan": ["title", "description", "severity", "file_path"], | |
"MobSF Scorecard Scan": ["title", "description", "severity"], | |
"OSV Scan": ["title", "description", "severity"], | |
"Snyk Code Scan": ["vuln_id_from_tool", "file_path"], |
⚠️ Configuration Change Risk in dojo/settings/settings.dist.py
Vulnerability | Configuration Change Risk |
---|---|
Description | Continuing the pattern of previous hunks, this configuration change is part of a coordinated update to the scanner support in the application. The changes do not introduce a security risk that would require mitigation. |
django-DefectDojo/dojo/settings/settings.dist.py
Lines 1380 to 1385 in 1a999ff
"Cloudsploit Scan": True, | |
"SonarQube Scan": False, | |
"Dependency Check Scan": True, | |
"Tenable Scan": True, | |
"Nexpose Scan": True, | |
"NPM Audit Scan": True, |
⚠️ Configuration Change Risk in dojo/settings/settings.dist.py
Vulnerability | Configuration Change Risk |
---|---|
Description | Consistent with the previous hunks, this configuration modification is part of a planned update to the application's scanner integration. It does not represent a security vulnerability. |
django-DefectDojo/dojo/settings/settings.dist.py
Lines 1488 to 1493 in 1a999ff
"Crunch42 Scan": DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL, | |
"Dependency Track Finding Packaging Format (FPF) Export": DEDUPE_ALGO_HASH_CODE, | |
"Horusec Scan": DEDUPE_ALGO_HASH_CODE, | |
"SonarQube Scan detailed": DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL, | |
"SonarQube Scan": DEDUPE_ALGO_HASH_CODE, | |
"SonarQube API Import": DEDUPE_ALGO_HASH_CODE, |
⚠️ Potential Information Injection in dojo/tools/mobsf/api_report_json.py
Vulnerability | Potential Information Injection |
---|---|
Description | The code constructs descriptions by directly incorporating fields from an external MobSF report without comprehensive sanitization. While the use of html2text() provides some mitigation, there's a potential risk of information disclosure or content manipulation if the input is maliciously crafted. The code should implement additional input validation and sanitization to prevent potential risks. |
django-DefectDojo/dojo/tools/mobsf/api_report_json.py
Lines 1 to 388 in 1a999ff
from datetime import datetime | |
from html2text import html2text | |
from dojo.models import Finding | |
class MobSFapireport: | |
def get_findings(self, data, test): | |
dupes = {} | |
find_date = datetime.now() | |
test_description = "" | |
if "name" in data: | |
test_description = "**Info:**\n" | |
if "packagename" in data: | |
test_description = "{} **Package Name:** {}\n".format(test_description, data["packagename"]) | |
if "mainactivity" in data: | |
test_description = "{} **Main Activity:** {}\n".format(test_description, data["mainactivity"]) | |
if "pltfm" in data: | |
test_description = "{} **Platform:** {}\n".format(test_description, data["pltfm"]) | |
if "sdk" in data: | |
test_description = "{} **SDK:** {}\n".format(test_description, data["sdk"]) | |
if "min" in data: | |
test_description = "{} **Min SDK:** {}\n".format(test_description, data["min"]) | |
if "targetsdk" in data: | |
test_description = "{} **Target SDK:** {}\n".format(test_description, data["targetsdk"]) | |
if "minsdk" in data: | |
test_description = "{} **Min SDK:** {}\n".format(test_description, data["minsdk"]) | |
if "maxsdk" in data: | |
test_description = "{} **Max SDK:** {}\n".format(test_description, data["maxsdk"]) | |
test_description = f"{test_description}\n**File Information:**\n" | |
if "name" in data: | |
test_description = "{} **Name:** {}\n".format(test_description, data["name"]) | |
if "md5" in data: | |
test_description = "{} **MD5:** {}\n".format(test_description, data["md5"]) | |
if "sha1" in data: | |
test_description = "{} **SHA-1:** {}\n".format(test_description, data["sha1"]) | |
if "sha256" in data: | |
test_description = "{} **SHA-256:** {}\n".format(test_description, data["sha256"]) | |
if "size" in data: | |
test_description = "{} **Size:** {}\n".format(test_description, data["size"]) | |
if "urls" in data: | |
curl = "" | |
for url in data["urls"]: | |
for durl in url["urls"]: | |
curl = f"{durl}\n" | |
if curl: | |
test_description = f"{test_description}\n**URL's:**\n {curl}\n" | |
if "bin_anal" in data: | |
test_description = "{} \n**Binary Analysis:** {}\n".format(test_description, data["bin_anal"]) | |
test.description = html2text(test_description) | |
mobsf_findings = [] | |
# Mobile Permissions | |
if "permissions" in data: | |
# for permission, details in data["permissions"].items(): | |
if isinstance(data["permissions"], list): | |
for details in data["permissions"]: | |
mobsf_item = { | |
"category": "Mobile Permissions", | |
"title": details.get("name", ""), | |
"severity": self.getSeverityForPermission(details.get("status")), | |
"description": "**Permission Type:** " + details.get("name", "") + " (" + details.get("status", "") + ")\n\n**Description:** " + details.get("description", "") + "\n\n**Reason:** " + details.get("reason", ""), | |
"file_path": None, | |
} | |
mobsf_findings.append(mobsf_item) | |
else: | |
for permission, details in list(data["permissions"].items()): | |
mobsf_item = { | |
"category": "Mobile Permissions", | |
"title": permission, | |
"severity": self.getSeverityForPermission(details.get("status", "")), | |
"description": "**Permission Type:** " + permission + "\n\n**Description:** " + details.get("description", ""), | |
"file_path": None, | |
} | |
mobsf_findings.append(mobsf_item) | |
# Insecure Connections | |
if "insecure_connections" in data: | |
for details in data["insecure_connections"]: | |
insecure_urls = "" | |
for url in details.split(","): | |
insecure_urls = insecure_urls + url + "\n" | |
mobsf_item = { | |
"category": None, | |
"title": "Insecure Connections", | |
"severity": "Low", | |
"description": insecure_urls, | |
"file_path": None, | |
} | |
mobsf_findings.append(mobsf_item) | |
# Certificate Analysis | |
if "certificate_analysis" in data: | |
if data["certificate_analysis"] != {}: | |
certificate_info = data["certificate_analysis"]["certificate_info"] | |
for details in data["certificate_analysis"]["certificate_findings"]: | |
if len(details) == 3: | |
mobsf_item = { | |
"category": "Certificate Analysis", | |
"title": details[2], | |
"severity": details[0].title(), | |
"description": details[1] + "\n\n**Certificate Info:** " + certificate_info, | |
"file_path": None, | |
} | |
mobsf_findings.append(mobsf_item) | |
elif len(details) == 2: | |
mobsf_item = { | |
"category": "Certificate Analysis", | |
"title": details[1], | |
"severity": details[0].title(), | |
"description": details[1] + "\n\n**Certificate Info:** " + certificate_info, | |
"file_path": None, | |
} | |
mobsf_findings.append(mobsf_item) | |
# Manifest Analysis | |
if "manifest_analysis" in data: | |
if data["manifest_analysis"] != {} and isinstance(data["manifest_analysis"], dict): | |
if data["manifest_analysis"]["manifest_findings"]: | |
for details in data["manifest_analysis"]["manifest_findings"]: | |
mobsf_item = { | |
"category": "Manifest Analysis", | |
"title": details["title"], | |
"severity": details["severity"].title(), | |
"description": details["description"] + "\n\n " + details["name"], | |
"file_path": None, | |
} | |
mobsf_findings.append(mobsf_item) | |
else: | |
for details in data["manifest_analysis"]: | |
mobsf_item = { | |
"category": "Manifest Analysis", | |
"title": details["title"], | |
"severity": details["stat"].title(), | |
"description": details["desc"] + "\n\n " + details["name"], | |
"file_path": None, | |
} | |
mobsf_findings.append(mobsf_item) | |
# Code Analysis | |
if "code_analysis" in data: | |
if data["code_analysis"] != {}: | |
if data["code_analysis"].get("findings"): | |
for details in data["code_analysis"]["findings"]: | |
metadata = data["code_analysis"]["findings"][details] | |
mobsf_item = { | |
"category": "Code Analysis", | |
"title": details, | |
"severity": metadata["metadata"]["severity"].title(), | |
"description": metadata["metadata"]["description"], | |
"file_path": None, | |
} | |
mobsf_findings.append(mobsf_item) | |
else: | |
for details in data["code_analysis"]: | |
metadata = data["code_analysis"][details] | |
if metadata.get("metadata"): | |
mobsf_item = { | |
"category": "Code Analysis", | |
"title": details, | |
"severity": metadata["metadata"]["severity"].title(), | |
"description": metadata["metadata"]["description"], | |
"file_path": None, | |
} | |
mobsf_findings.append(mobsf_item) | |
# Binary Analysis | |
if "binary_analysis" in data: | |
if isinstance(data["binary_analysis"], list): | |
for details in data["binary_analysis"]: | |
for binary_analysis_type in details: | |
if binary_analysis_type != "name": | |
mobsf_item = { | |
"category": "Binary Analysis", | |
"title": details[binary_analysis_type]["description"].split(".")[0], | |
"severity": details[binary_analysis_type]["severity"].title(), | |
"description": details[binary_analysis_type]["description"], | |
"file_path": details["name"], | |
} | |
mobsf_findings.append(mobsf_item) | |
elif data["binary_analysis"].get("findings"): | |
for details in data["binary_analysis"]["findings"].values(): | |
# "findings":{ | |
# "Binary makes use of insecure API(s)":{ | |
# "detailed_desc":"The binary may contain the following insecure API(s) _memcpy\n, _strlen\n", | |
# "severity":"high", | |
# "cvss":6, | |
# "cwe":"CWE-676: Use of Potentially Dangerous Function", | |
# "owasp-mobile":"M7: Client Code Quality", | |
# "masvs":"MSTG-CODE-8" | |
# }, | |
mobsf_item = { | |
"category": "Binary Analysis", | |
"title": details["detailed_desc"], | |
"severity": details["severity"].title(), | |
"description": details["detailed_desc"], | |
"file_path": None, | |
} | |
mobsf_findings.append(mobsf_item) | |
else: | |
for details in data["binary_analysis"].values(): | |
# "Binary makes use of insecure API(s)":{ | |
# "detailed_desc":"The binary may contain the following insecure API(s) _vsprintf.", | |
# "severity":"high", | |
# "cvss":6, | |
# "cwe":"CWE-676 - Use of Potentially Dangerous Function", | |
# "owasp-mobile":"M7: Client Code Quality", | |
# "masvs":"MSTG-CODE-8" | |
# } | |
mobsf_item = { | |
"category": "Binary Analysis", | |
"title": details["detailed_desc"], | |
"severity": details["severity"].title(), | |
"description": details["detailed_desc"], | |
"file_path": None, | |
} | |
mobsf_findings.append(mobsf_item) | |
# specific node for Android reports | |
if "android_api" in data: | |
# "android_insecure_random": { | |
# "files": { | |
# "u/c/a/b/a/c.java": "9", | |
# "kotlinx/coroutines/repackaged/net/bytebuddy/utility/RandomString.java": "3", | |
# ... | |
# "hu/mycompany/vbnmqweq/gateway/msg/Response.java": "13" | |
# }, | |
# "metadata": { | |
# "id": "android_insecure_random", | |
# "description": "The App uses an insecure Random Number Generator.", | |
# "type": "Regex", | |
# "pattern": "java\\.util\\.Random;", | |
# "severity": "high", | |
# "input_case": "exact", | |
# "cvss": 7.5, | |
# "cwe": "CWE-330 Use of Insufficiently Random Values", | |
# "owasp-mobile": "M5: Insufficient Cryptography", | |
# "masvs": "MSTG-CRYPTO-6" | |
# } | |
# }, | |
for api, details in list(data["android_api"].items()): | |
mobsf_item = { | |
"category": "Android API", | |
"title": details["metadata"]["description"], | |
"severity": details["metadata"]["severity"].title(), | |
"description": "**API:** " + api + "\n\n**Description:** " + details["metadata"]["description"], | |
"file_path": None, | |
} | |
mobsf_findings.append(mobsf_item) | |
# Manifest | |
if "manifest" in data: | |
for details in data["manifest"]: | |
mobsf_item = { | |
"category": "Manifest", | |
"title": details["title"], | |
"severity": details["stat"], | |
"description": details["desc"], | |
"file_path": None, | |
} | |
mobsf_findings.append(mobsf_item) | |
# MobSF Findings | |
if "findings" in data: | |
for title, finding in list(data["findings"].items()): | |
description = title | |
file_path = None | |
if "path" in finding: | |
description += "\n\n**Files:**\n" | |
for path in finding["path"]: | |
if file_path is None: | |
file_path = path | |
description = description + " * " + path + "\n" | |
mobsf_item = { | |
"category": "Findings", | |
"title": title, | |
"severity": finding["level"], | |
"description": description, | |
"file_path": file_path, | |
} | |
mobsf_findings.append(mobsf_item) | |
if isinstance(data, list): | |
for finding in data: | |
mobsf_item = { | |
"category": finding["category"], | |
"title": finding["name"], | |
"severity": finding["severity"], | |
"description": finding["description"] + "\n" + "**apk_exploit_dict:** " + str(finding["apk_exploit_dict"]) + "\n" + "**line_number:** " + str(finding["line_number"]), | |
"file_path": finding["file_object"], | |
} | |
mobsf_findings.append(mobsf_item) | |
for mobsf_finding in mobsf_findings: | |
title = mobsf_finding["title"] | |
sev = self.getCriticalityRating(mobsf_finding["severity"]) | |
description = "" | |
file_path = None | |
if mobsf_finding["category"]: | |
description += "**Category:** " + mobsf_finding["category"] + "\n\n" | |
description += html2text(mobsf_finding["description"]) | |
finding = Finding( | |
title=title, | |
cwe=919, # Weaknesses in Mobile Applications | |
test=test, | |
description=description, | |
severity=sev, | |
references=None, | |
date=find_date, | |
static_finding=True, | |
dynamic_finding=False, | |
nb_occurences=1, | |
) | |
if mobsf_finding["file_path"]: | |
finding.file_path = mobsf_finding["file_path"] | |
dupe_key = sev + title + description + mobsf_finding["file_path"] | |
else: | |
dupe_key = sev + title + description | |
if mobsf_finding["category"]: | |
dupe_key += mobsf_finding["category"] | |
if dupe_key in dupes: | |
find = dupes[dupe_key] | |
if description is not None: | |
find.description += description | |
find.nb_occurences += 1 | |
else: | |
dupes[dupe_key] = finding | |
return list(dupes.values()) | |
def getSeverityForPermission(self, status): | |
""" | |
Convert status for permission detection to severity | |
In MobSF there is only 4 know values for permission, | |
we map them as this: | |
dangerous => High (Critical?) | |
normal => Info | |
signature => Info (it's positive so... Info) | |
signatureOrSystem => Info (it's positive so... Info) | |
""" | |
if status == "dangerous": | |
return "High" | |
return "Info" | |
# Criticality rating | |
def getCriticalityRating(self, rating): | |
criticality = "Info" | |
if rating.lower() == "good": | |
criticality = "Info" | |
elif rating.lower() == "warning": | |
criticality = "Low" | |
elif rating.lower() == "vulnerability": | |
criticality = "Medium" | |
else: | |
criticality = rating.lower().capitalize() | |
return criticality | |
def suite_data(self, suites): | |
suite_info = "" | |
suite_info += suites["name"] + "\n" | |
suite_info += "Cipher Strength: " + str(suites["cipherStrength"]) + "\n" | |
if "ecdhBits" in suites: | |
suite_info += "ecdhBits: " + str(suites["ecdhBits"]) + "\n" | |
if "ecdhStrength" in suites: | |
suite_info += "ecdhStrength: " + str(suites["ecdhStrength"]) | |
suite_info += "\n\n" | |
return suite_info |
We've notified @mtesauro.
All finding details can be found in the DryRun Security Dashboard.
I notice 3 MobSF entries in |
e24e363
to
cf45913
Compare
This pull request has conflicts, please resolve those before we can evaluate the pull request. |
Conflicts have been resolved. A maintainer will review the pull request shortly. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I looked at the mobsfscan github repo and website. It looks like there are two ways to generate reports. One via the CLI, the other via the API (of the SAAS platform?). And we used to have two parsers for them and this PR merges them into 1 parser?
If this is correct, could you add a little of this info to the docs and/or description of the PR?
Could you also add instructions to the upgrade notes about the removal of the Mobsfscan Scan
scan type and the new value to be used (MobSF Scan
)
The thing is that I checked out MobSF and I wanted to import it to Defectdojo, but was confused about two parsers being listed. Thus, I am not really familiar with MobSF, but that PR helps beginners. |
🔴 Risk threshold exceeded.This pull request involves refactoring scan type processing for MobSF and Mobsfscan, with multiple configuration changes in settings files and a potential information disclosure in error reporting that appears to be part of a structured diagnostic process. The changes seem intentional and focused on consolidating scan type handling.
|
Vulnerability | Degradation of Security Data Processing |
---|---|
Description | The changes in the settings files appear to be part of a deliberate refactoring of scan type processing, specifically related to MobSF and Mobsfscan scan types. The removals of specific scan type configurations do not inherently represent a security vulnerability, but rather a planned consolidation of scan type handling. |
django-DefectDojo/dojo/settings/settings.dist.py
Lines 1262 to 1267 in 0cd937c
"Dockle Scan": ["title", "description", "vuln_id_from_tool"], | |
"Dependency Track Finding Packaging Format (FPF) Export": ["component_name", "component_version", "vulnerability_ids"], | |
"Horusec Scan": ["title", "description", "file_path", "line"], | |
"Tenable Scan": ["title", "severity", "vulnerability_ids", "cwe", "description"], | |
"Nexpose Scan": ["title", "severity", "vulnerability_ids", "cwe"], | |
# possible improvement: in the scanner put the library name into file_path, then dedup on cwe + file_path + severity |
⚠️ Degradation of Security Data Processing in dojo/settings/settings.dist.py
Vulnerability | Degradation of Security Data Processing |
---|---|
Description | The changes in the settings files appear to be part of a deliberate refactoring of scan type processing, specifically related to MobSF and Mobsfscan scan types. The removals of specific scan type configurations do not inherently represent a security vulnerability, but rather a planned consolidation of scan type handling. |
django-DefectDojo/dojo/settings/settings.dist.py
Lines 1322 to 1328 in 0cd937c
"HCLAppScan XML": ["title", "description"], | |
"HCL AppScan on Cloud SAST XML": ["title", "file_path", "line", "severity"], | |
"KICS Scan": ["file_path", "line", "severity", "description", "title"], | |
"MobSF Scan": ["title", "description", "severity", "file_path"], | |
"MobSF Scorecard Scan": ["title", "description", "severity"], | |
"OSV Scan": ["title", "description", "severity"], | |
"Snyk Code Scan": ["vuln_id_from_tool", "file_path"], |
⚠️ Potential Information Disclosure in dojo/tools/mobsf/api_report_json.py
Vulnerability | Potential Information Disclosure |
---|---|
Description | While the code does include potentially sensitive information in error descriptions, the context suggests this is part of a structured report generation process. The str() conversion of apk_exploit_dict and line_number is unlikely to expose critical secrets. The method appears designed to provide detailed diagnostic information for security analysis, which is typically intentional in security scanning tools. |
django-DefectDojo/dojo/tools/mobsf/api_report_json.py
Lines 1 to 388 in 0cd937c
from datetime import datetime | |
from html2text import html2text | |
from dojo.models import Finding | |
class MobSFapireport: | |
def get_findings(self, data, test): | |
dupes = {} | |
find_date = datetime.now() | |
test_description = "" | |
if "name" in data: | |
test_description = "**Info:**\n" | |
if "packagename" in data: | |
test_description = "{} **Package Name:** {}\n".format(test_description, data["packagename"]) | |
if "mainactivity" in data: | |
test_description = "{} **Main Activity:** {}\n".format(test_description, data["mainactivity"]) | |
if "pltfm" in data: | |
test_description = "{} **Platform:** {}\n".format(test_description, data["pltfm"]) | |
if "sdk" in data: | |
test_description = "{} **SDK:** {}\n".format(test_description, data["sdk"]) | |
if "min" in data: | |
test_description = "{} **Min SDK:** {}\n".format(test_description, data["min"]) | |
if "targetsdk" in data: | |
test_description = "{} **Target SDK:** {}\n".format(test_description, data["targetsdk"]) | |
if "minsdk" in data: | |
test_description = "{} **Min SDK:** {}\n".format(test_description, data["minsdk"]) | |
if "maxsdk" in data: | |
test_description = "{} **Max SDK:** {}\n".format(test_description, data["maxsdk"]) | |
test_description = f"{test_description}\n**File Information:**\n" | |
if "name" in data: | |
test_description = "{} **Name:** {}\n".format(test_description, data["name"]) | |
if "md5" in data: | |
test_description = "{} **MD5:** {}\n".format(test_description, data["md5"]) | |
if "sha1" in data: | |
test_description = "{} **SHA-1:** {}\n".format(test_description, data["sha1"]) | |
if "sha256" in data: | |
test_description = "{} **SHA-256:** {}\n".format(test_description, data["sha256"]) | |
if "size" in data: | |
test_description = "{} **Size:** {}\n".format(test_description, data["size"]) | |
if "urls" in data: | |
curl = "" | |
for url in data["urls"]: | |
for durl in url["urls"]: | |
curl = f"{durl}\n" | |
if curl: | |
test_description = f"{test_description}\n**URL's:**\n {curl}\n" | |
if "bin_anal" in data: | |
test_description = "{} \n**Binary Analysis:** {}\n".format(test_description, data["bin_anal"]) | |
test.description = html2text(test_description) | |
mobsf_findings = [] | |
# Mobile Permissions | |
if "permissions" in data: | |
# for permission, details in data["permissions"].items(): | |
if isinstance(data["permissions"], list): | |
for details in data["permissions"]: | |
mobsf_item = { | |
"category": "Mobile Permissions", | |
"title": details.get("name", ""), | |
"severity": self.getSeverityForPermission(details.get("status")), | |
"description": "**Permission Type:** " + details.get("name", "") + " (" + details.get("status", "") + ")\n\n**Description:** " + details.get("description", "") + "\n\n**Reason:** " + details.get("reason", ""), | |
"file_path": None, | |
} | |
mobsf_findings.append(mobsf_item) | |
else: | |
for permission, details in list(data["permissions"].items()): | |
mobsf_item = { | |
"category": "Mobile Permissions", | |
"title": permission, | |
"severity": self.getSeverityForPermission(details.get("status", "")), | |
"description": "**Permission Type:** " + permission + "\n\n**Description:** " + details.get("description", ""), | |
"file_path": None, | |
} | |
mobsf_findings.append(mobsf_item) | |
# Insecure Connections | |
if "insecure_connections" in data: | |
for details in data["insecure_connections"]: | |
insecure_urls = "" | |
for url in details.split(","): | |
insecure_urls = insecure_urls + url + "\n" | |
mobsf_item = { | |
"category": None, | |
"title": "Insecure Connections", | |
"severity": "Low", | |
"description": insecure_urls, | |
"file_path": None, | |
} | |
mobsf_findings.append(mobsf_item) | |
# Certificate Analysis | |
if "certificate_analysis" in data: | |
if data["certificate_analysis"] != {}: | |
certificate_info = data["certificate_analysis"]["certificate_info"] | |
for details in data["certificate_analysis"]["certificate_findings"]: | |
if len(details) == 3: | |
mobsf_item = { | |
"category": "Certificate Analysis", | |
"title": details[2], | |
"severity": details[0].title(), | |
"description": details[1] + "\n\n**Certificate Info:** " + certificate_info, | |
"file_path": None, | |
} | |
mobsf_findings.append(mobsf_item) | |
elif len(details) == 2: | |
mobsf_item = { | |
"category": "Certificate Analysis", | |
"title": details[1], | |
"severity": details[0].title(), | |
"description": details[1] + "\n\n**Certificate Info:** " + certificate_info, | |
"file_path": None, | |
} | |
mobsf_findings.append(mobsf_item) | |
# Manifest Analysis | |
if "manifest_analysis" in data: | |
if data["manifest_analysis"] != {} and isinstance(data["manifest_analysis"], dict): | |
if data["manifest_analysis"]["manifest_findings"]: | |
for details in data["manifest_analysis"]["manifest_findings"]: | |
mobsf_item = { | |
"category": "Manifest Analysis", | |
"title": details["title"], | |
"severity": details["severity"].title(), | |
"description": details["description"] + "\n\n " + details["name"], | |
"file_path": None, | |
} | |
mobsf_findings.append(mobsf_item) | |
else: | |
for details in data["manifest_analysis"]: | |
mobsf_item = { | |
"category": "Manifest Analysis", | |
"title": details["title"], | |
"severity": details["stat"].title(), | |
"description": details["desc"] + "\n\n " + details["name"], | |
"file_path": None, | |
} | |
mobsf_findings.append(mobsf_item) | |
# Code Analysis | |
if "code_analysis" in data: | |
if data["code_analysis"] != {}: | |
if data["code_analysis"].get("findings"): | |
for details in data["code_analysis"]["findings"]: | |
metadata = data["code_analysis"]["findings"][details] | |
mobsf_item = { | |
"category": "Code Analysis", | |
"title": details, | |
"severity": metadata["metadata"]["severity"].title(), | |
"description": metadata["metadata"]["description"], | |
"file_path": None, | |
} | |
mobsf_findings.append(mobsf_item) | |
else: | |
for details in data["code_analysis"]: | |
metadata = data["code_analysis"][details] | |
if metadata.get("metadata"): | |
mobsf_item = { | |
"category": "Code Analysis", | |
"title": details, | |
"severity": metadata["metadata"]["severity"].title(), | |
"description": metadata["metadata"]["description"], | |
"file_path": None, | |
} | |
mobsf_findings.append(mobsf_item) | |
# Binary Analysis | |
if "binary_analysis" in data: | |
if isinstance(data["binary_analysis"], list): | |
for details in data["binary_analysis"]: | |
for binary_analysis_type in details: | |
if binary_analysis_type != "name": | |
mobsf_item = { | |
"category": "Binary Analysis", | |
"title": details[binary_analysis_type]["description"].split(".")[0], | |
"severity": details[binary_analysis_type]["severity"].title(), | |
"description": details[binary_analysis_type]["description"], | |
"file_path": details["name"], | |
} | |
mobsf_findings.append(mobsf_item) | |
elif data["binary_analysis"].get("findings"): | |
for details in data["binary_analysis"]["findings"].values(): | |
# "findings":{ | |
# "Binary makes use of insecure API(s)":{ | |
# "detailed_desc":"The binary may contain the following insecure API(s) _memcpy\n, _strlen\n", | |
# "severity":"high", | |
# "cvss":6, | |
# "cwe":"CWE-676: Use of Potentially Dangerous Function", | |
# "owasp-mobile":"M7: Client Code Quality", | |
# "masvs":"MSTG-CODE-8" | |
# }, | |
mobsf_item = { | |
"category": "Binary Analysis", | |
"title": details["detailed_desc"], | |
"severity": details["severity"].title(), | |
"description": details["detailed_desc"], | |
"file_path": None, | |
} | |
mobsf_findings.append(mobsf_item) | |
else: | |
for details in data["binary_analysis"].values(): | |
# "Binary makes use of insecure API(s)":{ | |
# "detailed_desc":"The binary may contain the following insecure API(s) _vsprintf.", | |
# "severity":"high", | |
# "cvss":6, | |
# "cwe":"CWE-676 - Use of Potentially Dangerous Function", | |
# "owasp-mobile":"M7: Client Code Quality", | |
# "masvs":"MSTG-CODE-8" | |
# } | |
mobsf_item = { | |
"category": "Binary Analysis", | |
"title": details["detailed_desc"], | |
"severity": details["severity"].title(), | |
"description": details["detailed_desc"], | |
"file_path": None, | |
} | |
mobsf_findings.append(mobsf_item) | |
# specific node for Android reports | |
if "android_api" in data: | |
# "android_insecure_random": { | |
# "files": { | |
# "u/c/a/b/a/c.java": "9", | |
# "kotlinx/coroutines/repackaged/net/bytebuddy/utility/RandomString.java": "3", | |
# ... | |
# "hu/mycompany/vbnmqweq/gateway/msg/Response.java": "13" | |
# }, | |
# "metadata": { | |
# "id": "android_insecure_random", | |
# "description": "The App uses an insecure Random Number Generator.", | |
# "type": "Regex", | |
# "pattern": "java\\.util\\.Random;", | |
# "severity": "high", | |
# "input_case": "exact", | |
# "cvss": 7.5, | |
# "cwe": "CWE-330 Use of Insufficiently Random Values", | |
# "owasp-mobile": "M5: Insufficient Cryptography", | |
# "masvs": "MSTG-CRYPTO-6" | |
# } | |
# }, | |
for api, details in list(data["android_api"].items()): | |
mobsf_item = { | |
"category": "Android API", | |
"title": details["metadata"]["description"], | |
"severity": details["metadata"]["severity"].title(), | |
"description": "**API:** " + api + "\n\n**Description:** " + details["metadata"]["description"], | |
"file_path": None, | |
} | |
mobsf_findings.append(mobsf_item) | |
# Manifest | |
if "manifest" in data: | |
for details in data["manifest"]: | |
mobsf_item = { | |
"category": "Manifest", | |
"title": details["title"], | |
"severity": details["stat"], | |
"description": details["desc"], | |
"file_path": None, | |
} | |
mobsf_findings.append(mobsf_item) | |
# MobSF Findings | |
if "findings" in data: | |
for title, finding in list(data["findings"].items()): | |
description = title | |
file_path = None | |
if "path" in finding: | |
description += "\n\n**Files:**\n" | |
for path in finding["path"]: | |
if file_path is None: | |
file_path = path | |
description = description + " * " + path + "\n" | |
mobsf_item = { | |
"category": "Findings", | |
"title": title, | |
"severity": finding["level"], | |
"description": description, | |
"file_path": file_path, | |
} | |
mobsf_findings.append(mobsf_item) | |
if isinstance(data, list): | |
for finding in data: | |
mobsf_item = { | |
"category": finding["category"], | |
"title": finding["name"], | |
"severity": finding["severity"], | |
"description": finding["description"] + "\n" + "**apk_exploit_dict:** " + str(finding["apk_exploit_dict"]) + "\n" + "**line_number:** " + str(finding["line_number"]), | |
"file_path": finding["file_object"], | |
} | |
mobsf_findings.append(mobsf_item) | |
for mobsf_finding in mobsf_findings: | |
title = mobsf_finding["title"] | |
sev = self.getCriticalityRating(mobsf_finding["severity"]) | |
description = "" | |
file_path = None | |
if mobsf_finding["category"]: | |
description += "**Category:** " + mobsf_finding["category"] + "\n\n" | |
description += html2text(mobsf_finding["description"]) | |
finding = Finding( | |
title=title, | |
cwe=919, # Weaknesses in Mobile Applications | |
test=test, | |
description=description, | |
severity=sev, | |
references=None, | |
date=find_date, | |
static_finding=True, | |
dynamic_finding=False, | |
nb_occurences=1, | |
) | |
if mobsf_finding["file_path"]: | |
finding.file_path = mobsf_finding["file_path"] | |
dupe_key = sev + title + description + mobsf_finding["file_path"] | |
else: | |
dupe_key = sev + title + description | |
if mobsf_finding["category"]: | |
dupe_key += mobsf_finding["category"] | |
if dupe_key in dupes: | |
find = dupes[dupe_key] | |
if description is not None: | |
find.description += description | |
find.nb_occurences += 1 | |
else: | |
dupes[dupe_key] = finding | |
return list(dupes.values()) | |
def getSeverityForPermission(self, status): | |
""" | |
Convert status for permission detection to severity | |
In MobSF there is only 4 know values for permission, | |
we map them as this: | |
dangerous => High (Critical?) | |
normal => Info | |
signature => Info (it's positive so... Info) | |
signatureOrSystem => Info (it's positive so... Info) | |
""" | |
if status == "dangerous": | |
return "High" | |
return "Info" | |
# Criticality rating | |
def getCriticalityRating(self, rating): | |
criticality = "Info" | |
if rating.lower() == "good": | |
criticality = "Info" | |
elif rating.lower() == "warning": | |
criticality = "Low" | |
elif rating.lower() == "vulnerability": | |
criticality = "Medium" | |
else: | |
criticality = rating.lower().capitalize() | |
return criticality | |
def suite_data(self, suites): | |
suite_info = "" | |
suite_info += suites["name"] + "\n" | |
suite_info += "Cipher Strength: " + str(suites["cipherStrength"]) + "\n" | |
if "ecdhBits" in suites: | |
suite_info += "ecdhBits: " + str(suites["ecdhBits"]) + "\n" | |
if "ecdhStrength" in suites: | |
suite_info += "ecdhStrength: " + str(suites["ecdhStrength"]) | |
suite_info += "\n\n" | |
return suite_info |
⚠️ Degradation of Security Data Processing in dojo/settings/settings.dist.py
Vulnerability | Degradation of Security Data Processing |
---|---|
Description | The changes in the settings files appear to be part of a deliberate refactoring of scan type processing, specifically related to MobSF and Mobsfscan scan types. The removals of specific scan type configurations do not inherently represent a security vulnerability, but rather a planned consolidation of scan type handling. |
django-DefectDojo/dojo/settings/settings.dist.py
Lines 1380 to 1385 in 0cd937c
"Cloudsploit Scan": True, | |
"SonarQube Scan": False, | |
"Dependency Check Scan": True, | |
"Tenable Scan": True, | |
"Nexpose Scan": True, | |
"NPM Audit Scan": True, |
⚠️ Degradation of Security Data Processing in dojo/settings/settings.dist.py
Vulnerability | Degradation of Security Data Processing |
---|---|
Description | The changes in the settings files appear to be part of a deliberate refactoring of scan type processing, specifically related to MobSF and Mobsfscan scan types. The removals of specific scan type configurations do not inherently represent a security vulnerability, but rather a planned consolidation of scan type handling. |
django-DefectDojo/dojo/settings/settings.dist.py
Lines 1488 to 1493 in 0cd937c
"Crunch42 Scan": DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL, | |
"Dependency Track Finding Packaging Format (FPF) Export": DEDUPE_ALGO_HASH_CODE, | |
"Horusec Scan": DEDUPE_ALGO_HASH_CODE, | |
"SonarQube Scan detailed": DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL, | |
"SonarQube Scan": DEDUPE_ALGO_HASH_CODE, | |
"SonarQube API Import": DEDUPE_ALGO_HASH_CODE, |
⚠️ Configured Codepaths Edit in dojo/db_migrations/0230_merge_mobsf.py
Vulnerability | Configured Codepaths Edit |
---|---|
Description | Sensitive edits detected for this file. Sensitive file paths and allowed authors can be configured in .dryrunsecurity.yaml . |
We've notified @mtesauro.
All finding details can be found in the DryRun Security Dashboard.
It is more userfriendly to have only one MobSF scan type to choose. Before parsing MobSF files, you have to analyse which parser is the right one.