Defender anti-malware policy has admin notifications enabled for internal users sending malware
Microsoft Defender for Office 365 anti-malware policies are checked for admin notifications on malware detected from internal senders, ensuring a valid notification address is defined (EnableInternalSenderAdminNotifications and InternalSenderAdminAddress).
Effective settings across default and custom policies are considered.
Risk
Without these notifications, malware sent from internal accounts can persist unnoticed, delaying response and containment. This undermines integrity of email, enables lateral movement and outbound propagation, and can cause domain reputation damage and blocklisting, affecting availability.
Run this check with Prowler
Run in Prowler Cloudprowler m365 --checks defender_malware_policy_notifications_internal_users_malware_enabledRecommendation
Enable and maintain admin alerts for internal-sender malware and route to a monitored mailbox or SOC list (EnableInternalSenderAdminNotifications and InternalSenderAdminAddress).
Ensure coverage via policy precedence, integrate with SIEM, and apply least privilege and defense in depth to limit impact.
Remediation
CLI
Set-MalwareFilterPolicy -Identity Default -EnableInternalSenderAdminNotifications $true -InternalSenderAdminAddress "<ADMIN_EMAIL>"Other
Check Code
from typing import List
from prowler.lib.check.models import Check, CheckReportM365
from prowler.providers.m365.services.defender.defender_client import defender_client
class defender_malware_policy_notifications_internal_users_malware_enabled(Check):
"""
Check if notifications for internal users sending malware are enabled in the Defender anti-malware policy.
Attributes:
metadata: Metadata associated with the check (inherited from Check).
"""
def execute(self) -> List[CheckReportM365]:
"""
Execute the check to verify if notifications for internal users sending malware are enabled.
This method evaluates the Defender anti-malware policies to ensure internal sender notifications
are properly configured.
Returns:
List[CheckReportM365]: A list of reports with the results of the check.
"""
findings = []
if defender_client.malware_policies:
# Only Default Defender Malware Policy exists
if not defender_client.malware_rules:
policy = defender_client.malware_policies[0]
report = CheckReportM365(
metadata=self.metadata(),
resource=policy,
resource_name=policy.identity,
resource_id=policy.identity,
)
if self._are_notifications_enabled(policy):
# Case 1: Default policy exists and has notifications enabled
report.status = "PASS"
report.status_extended = f"{policy.identity} is the only policy and notifications for internal users sending malware are enabled."
else:
# Case 5: Default policy exists but doesn't have notifications enabled
report.status = "FAIL"
report.status_extended = f"{policy.identity} is the only policy and notifications for internal users sending malware are not enabled."
findings.append(report)
# Multiple Defender Malware Policies exist
else:
default_policy_well_configured = False
for policy in defender_client.malware_policies:
# Preset security policies are scoped by protection policy rules, not filter rules
if (
not policy.is_default
and policy.identity not in defender_client.malware_rules
):
continue
report = CheckReportM365(
metadata=self.metadata(),
resource=policy,
resource_name=policy.identity,
resource_id=policy.identity,
)
if policy.is_default:
if not self._are_notifications_enabled(policy):
# Case 4: Default policy not configured
report.status = "FAIL"
report.status_extended = (
f"{policy.identity} is the default policy and notifications for internal users sending malware are not enabled, "
"but it could be overridden by another well-configured Custom Policy."
)
findings.append(report)
else:
# Case 2: Default policy is properly configured
report.status = "PASS"
report.status_extended = (
f"{policy.identity} is the default policy and notifications for internal users sending malware are enabled, "
"but it could be overridden by another misconfigured Custom Policy."
)
default_policy_well_configured = True
findings.append(report)
else:
if not self._are_notifications_enabled(policy):
included_resources = []
if defender_client.malware_rules[policy.identity].users:
included_resources.append(
f"users: {', '.join(defender_client.malware_rules[policy.identity].users)}"
)
if defender_client.malware_rules[policy.identity].groups:
included_resources.append(
f"groups: {', '.join(defender_client.malware_rules[policy.identity].groups)}"
)
if defender_client.malware_rules[policy.identity].domains:
included_resources.append(
f"domains: {', '.join(defender_client.malware_rules[policy.identity].domains)}"
)
included_resources_str = "; ".join(included_resources)
if default_policy_well_configured:
# Case 3: Default policy is configured, custom one isn't
report.status = "FAIL"
report.status_extended = (
f"Custom Malware policy {policy.identity} is not properly configured and includes {included_resources_str}, "
f"with priority {defender_client.malware_rules[policy.identity].priority} (0 is the highest). "
"However, the default policy is properly configured, so entities not included by this custom policy could be correctly protected."
)
findings.append(report)
else:
# Case 5: Neither default nor custom policy is properly configured
report.status = "FAIL"
report.status_extended = (
f"Custom Malware policy {policy.identity} is not properly configured and includes {included_resources_str}, "
f"with priority {defender_client.malware_rules[policy.identity].priority} (0 is the highest). "
"Also, the default policy is not properly configured, so entities not included by this custom policy could not be correctly protected."
)
findings.append(report)
else:
included_resources = []
if defender_client.malware_rules[policy.identity].users:
included_resources.append(
f"users: {', '.join(defender_client.malware_rules[policy.identity].users)}"
)
if defender_client.malware_rules[policy.identity].groups:
included_resources.append(
f"groups: {', '.join(defender_client.malware_rules[policy.identity].groups)}"
)
if defender_client.malware_rules[policy.identity].domains:
included_resources.append(
f"domains: {', '.join(defender_client.malware_rules[policy.identity].domains)}"
)
included_resources_str = "; ".join(included_resources)
if default_policy_well_configured:
# Case 2: Both default and custom policies are properly configured
report.status = "PASS"
report.status_extended = (
f"Custom Malware policy {policy.identity} is properly configured and includes {included_resources_str}, "
f"with priority {defender_client.malware_rules[policy.identity].priority} (0 is the highest). "
"Also, the default policy is properly configured, so entities not included by this custom policy could still be correctly protected."
)
findings.append(report)
else:
# Case 6: Default policy not configured, custom policy is
report.status = "PASS"
report.status_extended = (
f"Custom Malware policy {policy.identity} is properly configured and includes {included_resources_str}, "
f"with priority {defender_client.malware_rules[policy.identity].priority} (0 is the highest). "
"However, the default policy is not properly configured, so entities not included by this custom policy could not be correctly protected."
)
findings.append(report)
return findings
def _are_notifications_enabled(self, policy) -> bool:
if (
not policy.is_default
and policy.identity in defender_client.malware_rules
and defender_client.malware_rules[policy.identity].state.lower()
!= "enabled"
):
return False
return (
policy.enable_internal_sender_admin_notifications
and policy.internal_sender_admin_address
)