Conditional Access policy blocks authentication transfer
At least one enabled Conditional Access policy should target the authentication transfer flow for all users and all resources and set the grant control to Block. Authentication transfer lets a user move an authenticated session from one device to another (for example, by scanning a QR code), which can be abused to move a session onto an attacker-controlled device.
Risk
If authentication transfer is not blocked, an attacker who can present a transfer prompt (e.g., a malicious QR code) may capture an authenticated session and gain access to the victim's resources without needing their credentials or MFA.
Run this check with Prowler
Run in Prowler Cloudprowler m365 --checks entra_conditional_access_policy_authentication_transfer_blockedRecommendation
Create a Conditional Access policy that blocks the authentication transfer flow for all users and all resources, excluding only documented break-glass accounts.
Remediation
Other
Check Code
from prowler.lib.check.models import Check, CheckReportM365
from prowler.providers.m365.services.entra.entra_client import entra_client
from prowler.providers.m365.services.entra.entra_service import (
ConditionalAccessGrantControl,
ConditionalAccessPolicyState,
TransferMethod,
)
class entra_conditional_access_policy_authentication_transfer_blocked(Check):
"""Check if at least one Conditional Access policy blocks authentication transfer.
This check ensures that at least one enabled Conditional Access policy targets
the authentication transfer flow and blocks access, preventing an authenticated
session from being seamlessly transferred to another (potentially attacker
controlled) device.
- PASS: An enabled Conditional Access policy blocks authentication transfer.
- FAIL: No Conditional Access policy restricts authentication transfer.
"""
def execute(self) -> list[CheckReportM365]:
"""Execute the check to verify authentication transfer is blocked by a Conditional Access policy.
Returns:
A list of reports containing the result of the check.
"""
findings = []
for policy in entra_client.conditional_access_policies.values():
report = CheckReportM365(
metadata=self.metadata(),
resource=policy,
resource_name=policy.display_name,
resource_id=policy.id,
)
report.status = "FAIL"
report.status_extended = f"Conditional Access Policy '{policy.display_name}' does not block authentication transfer."
authentication_flows = policy.conditions.authentication_flows
blocks_authentication_transfer = (
policy.state != ConditionalAccessPolicyState.DISABLED
and "All" in policy.conditions.user_conditions.included_users
and "All"
in policy.conditions.application_conditions.included_applications
and not policy.conditions.application_conditions.excluded_applications
and authentication_flows
and TransferMethod.AUTHENTICATION_TRANSFER
in authentication_flows.transfer_methods
and ConditionalAccessGrantControl.BLOCK
in policy.grant_controls.built_in_controls
)
if blocks_authentication_transfer:
if policy.state == ConditionalAccessPolicyState.ENABLED_FOR_REPORTING:
report.status = "FAIL"
report.status_extended = f"Conditional Access Policy '{policy.display_name}' reports authentication transfer but does not block it."
else:
report.status = "PASS"
report.status_extended = f"Conditional Access Policy '{policy.display_name}' blocks authentication transfer."
findings.append(report)
return findings