-
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. |
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.