ADFS Threat Detection Module 

Threat Detection Module for Microsoft Active Directory Federation Service (ADFS). The module allows or blocks user authentication requests at the point where the user provides the credentials but before AD FS evaluates them. The module leverages the user risk level determined by Azure AD Identity Protection to block or allow authentication for the user based on the user’s risk score. It allows to blocks authentication requests for risky IPs when AD FS receives the authentication request before the user enters credentials. The module once registered with AD FS runs in line with AD FS authentication process. 

The new Risk Assessment Model introduced with AD FS 2019 allows using “SecureMfaThreatDetectionModule” to provide additional capability to secure your ADFS environment from attacks such as password spray attacks. More details how Pluggable Risk Assessment Module works can be found here

Features

Unlicensed version

Licensed version (additional features)

 

When using user risk assessment feature with Azure Identity Protection you must meet bellow prerequisites:

Deploy SecureMFA Threat Detection Module into ADFS Farm 

Preparation steps

Before you can start registering “SecureMfaThreatDetectionModule” into your ADFS farm you must complete bellow steps. All commands must be executed in elevated PowerShell (PS) command prompt.

1) Deploy latest “SecureMfaThreatDetectionModule” PowerShell module from Microsoft PSGallery using bellow PS command:

Install-Module -Name SecureMFA_TDM -Repository PSGallery -Scope AllUsers

NOTE: As of April 2020, the PowerShell Gallery no longer supports lower than 1.2 TLS protocol. Hence if your servers don’t have GPO changes to reflect this requirement you may need manually to enforce TLS 1.2 for PowerShell session by using bellow command

[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12

If your ADFS server doesn’t have access to the Internet you can pull PowerShell module from Windows client which has Internet access using bellow PS command:

Find-Module -Name "SecureMFA_TDM" -Repository "PSGallery" | Save-Module -Path "C:\"

Copy C:\SecureMFA_TDM folder from client’s computer into ADFS server “C:\Program Files\WindowsPowerShell\Modules\SecureMFA_TDM”

As alternative you can download “SecureMFA_TDM” nupkg file manually from https://www.powershellgallery.com/ website. Rename nupkg file’s extension into ZIP. Unzip content into a folder “SecureMFA_TDM” and place it into PS Modules default location on the server. That will work the same way as pulling package with native windows PS Tools.

2) Within “C:\Program Files\WindowsPowerShell\Modules\SecureMFA_TDM” directory update “SecureMfaThreatDetectionModule.json” file.

    - If you will use blocks authentication requests for risky IPs feature you need to configure blockip_ sections.

    - If you will use risk level assessment by Azure AD Identity Protection to allow or block users based on their risk score level you will need to add your Azure account details: tenantname, clientid and clientsecret from your environment.

 3) If you need to generate verbose logs in windows events for troubleshooting reasons change verboselog value from “false” to “true”. Please note that verbose logging can affect your servers’ performance, use it only for troubleshooting reasons. Don’t enable “verboselog” in production environments as it may reveal configuration secrets

4) If ADFS servers (Not Web Application Servers) cannot access the internet directly for the Azure AD Identity Protection API endpoint, you need to configure your proxy server settings.

Below is a sample of a SecureMfaThreatDetectionModule.json file

{

  "company": "MyCompany",

  "serialkey": "m00000000",

  "subscriptionid": "1000000000000000000000001", 

  "user_risk_assessment_enable": "false",

  "user_risk_assessment_mode": "AzureIdentityProtection",

  "az_tenantname": "709755e6-234c-470c-9993-xxxxxxxxxxxx",

  "az_app_clientid": "b5ee9d27-fba1-46f0-a337-xxxxxxxxxxxx",

  "az_app_clientsecret": "-wmQrk~UJrZ_BLD~7XU~09y0MQ4OGdcS2o",

  "az_block_risklevel_high": "true",

  "az_block_risklevel_medium": "false",

  "az_block_risklevel_low": "false",

  "az_block_risklevel_notevaluated": "false",

  "blockip_extranet_enable": "false",

  "blockip_extranet_mode": "deny",

  "blockip_extranet_networks": "105.201.220.1/105.201.220.120;184.200.20.12/184.200.20.12;",

  "blockip_intranet_enable": "false",

  "blockip_intranet_mode": "allow",

  "blockip_intranet_networks": "10.0.0.0/10.255.255.255;172.16.0.0/172.16.255.255;192.168.0.0/192.168.255.255;ee80:e1::/ee80:e1::ffff",

  "api_timeout": "5000",

  "api_on_error_continue": "true", 

  "proxy_enable": "false",

  "proxy_server": "proxy.adatum.labnet",

  "proxy_port": "8080",

  "verboselog": "false"

}

 

Any configuration changes in SecureMfaThreatDetectionModule.json file requires Update-SecureMfaThreatDetectionModuleConfig to be executed on ADFS servers (Not Web Application Servers). This will update running config without restarting ADFS service.

SecureMFA API OTP Provider Installation

Before a “SecureMfaThreatDetectionModule” will be invoked by AD FS, it must be registered in the system. with PowerShell command which performs the necessary installation actions including installation in the GAC, and registration in AD FS farm. 

Primary ADFS node

Bellow command will install OTP authentication provider on the MAIN ADFS node.

Install-SecureMfaThreatDetectionModule 

Other ADFS node(s)

Bellow command will install OTP authentication provider on OTHER ADFS node(s).

Install-SecureMfaThreatDetectionModule  -NotMainNode

NOTE: If you are using federation server farm that uses Windows Internal Database, you must start installation using the primary federation server of the farm as a MAIN node. Installation needs to be executed on ADFS farm server (not web application proxy servers).

Verification

To verify if “SecureMFA Threat Detection Module  ” has been installed successfully.

Open elevated PowerShell (PS) command prompt on one of AD FS servers and run bellow command. This will return registered Threat Detection modules in ADFS farm. 

Get-AdfsThreatDetectionModule

Application

User risk assessment

This feature allows to block or allow user authentication requests at the point where the user provides the credentials but before AD FS evaluates them. It leverages the user risk level determined by Azure AD Identity Protection to block or allow authentication for the user based on the user risk score.

You will need to enable it and add your Azure account details: tenantname (use Azure TenantId value), clientid and clientsecret from your environment as bellow

  "user_risk_assessment_enable": "true",

  "user_risk_assessment_mode": "AzureIdentityProtection",

  "az_tenantname": "709755e6-234c-470c-9993-xxxxxxxxxxxx",

  "az_app_clientid": "b5ee9d27-fba1-46f0-a337-xxxxxxxxxxxx",

  "az_app_clientsecret": "-wmQrk~UJrZ_BLD~7XU~09y0MQ4OGdcS2o",

 To get Azure account details perform the following steps as Administrator in Azure PowerShell. You will need to have Az and AzureAD modules installed to run bellow PS commands:

1)    To get Azure AD tenant name: 

Connect-AzAccount

Get-AzTenant | select TenantId

2)  To get the App Client ID and Client secret values we first need to register a new app in Azure Active Directory. To do so, execute PS command for new app registration. Note down the “"az_app_clientid" and "az_app_clientsecret" values as those are displayed one time only.

$TenantId = " Insert Directory ID here"

Connect-AzureAD -TenantId $TenantId

$appName = "securemfatdm"

$myApp = New-AzureADApplication -DisplayName $appName -AvailableToOtherTenants $false

write-host "az_app_clientid:"$myApp.AppId

$myAppSecret = New-AzureADApplicationPasswordCredential -CustomKeyIdentifier "SecureMFA TDM Client Key" -ObjectId $myApp.ObjectId -EndDate ((Get-Date).AddMonths(6))

write-host "az_app_clientsecret:"$myAppSecret.Value

3)  Update SecureMfaThreatDetectionModule.json with TenantId, clientid and clientsecret details. 

4)      Though we have registered the client app in Azure Active Directory, we also need to provide it permission to call the Microsoft Graph API

#Get Application

$aadApplication = get-AzureADApplication | where {$_.DisplayName -eq 'securemfatdm'}

 

#Get Service Principal of Microsoft Graph Resource API

$graphSP =  Get-AzureADServicePrincipal -All $true | Where-Object {$_.DisplayName -eq "Microsoft Graph"}

 

#Initialize RequiredResourceAccess for Microsoft Graph Resource API

$requiredGraphAccess = New-Object Microsoft.Open.AzureAD.Model.RequiredResourceAccess

$requiredGraphAccess.ResourceAppId = $graphSP.AppId

$requiredGraphAccess.ResourceAccess = New-Object System.Collections.Generic.List[Microsoft.Open.AzureAD.Model.ResourceAccess]

 

#Set Application Permissions

$ApplicationPermissions = @('IdentityRiskyUser.Read.All')

 

#Add app permissions

ForEach ($permission in $ApplicationPermissions) {

$reqPermission = $null

#Get required app permission

$reqPermission = $graphSP.AppRoles | Where-Object {$_.Value -eq $permission}

if($reqPermission)

{

$resourceAccess = New-Object Microsoft.Open.AzureAD.Model.ResourceAccess

$resourceAccess.Type = "Role"

$resourceAccess.Id = $reqPermission.Id

#Add required app permission

$requiredGraphAccess.ResourceAccess.Add($resourceAccess)

}

else

{

Write-Host "App permission $permission not found in the Graph Resource API" -ForegroundColor Red

}

}

 

#Set Delegated Permissions

$DelegatedPermissions = @('User.Read') 

 

#Add delegated permissions

ForEach ($permission in $DelegatedPermissions) {

$reqPermission = $null

#Get required delegated permission

$reqPermission = $graphSP.Oauth2Permissions | Where-Object {$_.Value -eq $permission}

if($reqPermission)

{

$resourceAccess = New-Object Microsoft.Open.AzureAD.Model.ResourceAccess

$resourceAccess.Type = "Scope"

$resourceAccess.Id = $reqPermission.Id

#Add required delegated permission

$requiredGraphAccess.ResourceAccess.Add($resourceAccess)

}

else

{

Write-Host "Delegated permission $permission not found in the Graph Resource API" -ForegroundColor Red

}

}

 

#Add required resource accesses

$requiredResourcesAccess = New-Object System.Collections.Generic.List[Microsoft.Open.AzureAD.Model.RequiredResourceAccess]

$requiredResourcesAccess.Add($requiredGraphAccess)

 

#Set permissions in existing Azure AD App

$appObjectId=$aadApplication.ObjectId

Set-AzureADApplication -ObjectId $appObjectId -RequiredResourceAccess $requiredResourcesAccess

 5)      Lastly, click go into Azure Portal , select “Azure Active Directory” -> select “App registrations” ->  from all application select “securemfatdm” -> select “API permissions” from the Manage section on the left navigation pane. Select the IdentityRiskyUser.Read.All permission row and click on Grant admin consent for [tenant name]. Click Yes

Configure ADFS Relying Party

User risk assessment for each ADFS relying party is assigned individually. Bellow command will update ADFS RelyingPartyTrust configuration to work with SecureMfa Threat Detection Module by converting existing Access Control Policy to compatible IssuanzeAuthorizationPolicy. 

Update-ADFS_RelyingPartyTrust -RP_Name 'claimapp4' -RPmode 'SecureMFA_TDM'

 Make sure your ADFS farm has at least one additional authentication method enabled.

Configure risk level policies

Risk level policies needs to be configured to allow(false) or deny(true) access to ADFS relying party based on the user’s risk level. It is global parameters and affects all relying parties which are configured for user risk assessment. Azure Identity Protection categorizes risk into three tiers: low, medium, and high. For more details read here. Any user which is not evaluated by the Azure Identity Protection service and doesn’t have any risk score assigned will be allocated “notevaluated” risk level. 

Note: unlicensed module can only evaluate the first 24 users in the Azure Identity Protection database using API calls. If you have more than 24 users evaluated for risk level by Azure, other users will show as “notevaluated” risk level event they have different risk levels associated with their account in Azure. To remove this limitation, you will need to buy an active subscription license for this module.  

Bellow configuration will block high-risk users from accessing RP, all other users will be allowed to log in.

  "az_block_risklevel_high": "true",

  "az_block_risklevel_medium": "false",

  "az_block_risklevel_low": "false",

  "az_block_risklevel_notevaluated": "false", 

When users access ADFS relying party which is protected with SecureMFA Threat Detection Module. A module will retrieve the user’s risk level from Azure and if the user’s risk level is blocked by policy user will see the following error that blocks request before ADFS evaluates the use’s Active Directory credentials. 

Error will be logged into windows event log.

For testing purpose, to make a user risky (with risk level = High) login to Risky users report in Azure Portal as an Administrator. Select the user you want to change the risk level to High and click Confirm user compromised

Block IP

This feature allows to blocks or allows authentication requests for risky IPs when AD FS receives the authentication request before the user enters credentials.  You can enable extranet or intranet IP request blocks separately. Blockip operational mode can be only “deny” or “allow”

Bellow configuration will block access for IPs that get to ADFS via ADFS proxy server. SecureMFA TDM will block only IPs that are part of specified network ranges, all other IPs will be allowed.

"blockip_extranet_enable": "true",

"blockip_extranet_mode": "deny",

"blockip_extranet_networks": "105.201.220.1/105.201.220.120;184.200.20.12/184.200.20.12;",

 Bellow configuration will allow access for IPs that get to ADFS via internal ADFS servers. SecureMFA TDM will allow only IPs that are part of specified network ranges, all other IPs will be blocked.

"blockip_intranet_enable": "true",

"blockip_intranet_mode": "allow",

"blockip_intranet_networks": "10.0.0.0/10.255.255.255;172.16.0.0/172.16.255.255;192.168.0.0/192.168.255.255;ee80:e1::/ee80:e1::ffff", 

Note: unlicensed module can only evaluate first network range for External and Internal traffic. To remove this limitation, you will need to buy an active subscription license for this module.   

When users are blocked at request level they only can see blank page as ADFS doesn’t allow to load any services for restricted IP

Error will be logged into windows event log.

Network ranges 

To cover CIDR block range for network use following rules:

For IPv4 addressing – host range (For example 10.1.1.0/24 translates into host range: 10.1.1.1/10.1.1.254)

For IPv6 addressing – network range (For example fe80::/64 translates into network range: fe80:0000:0000:0000:0000:0000:0000:0000/ fe80:0000:0000:0000:ffff:ffff:ffff:ffff or you can use a valid short IPv6 address text representation ( defined by rfc5952) : fe80::/fe80::ffff

Below is a sample of valid configuration

"blockip_intranet_networks": “10.0.0.0/10.255.255.255;172.16.0.0/172.16.255.255;192.168.0.0/192.168.255.255;ee80:e1::/ee80:e1::ffff”

 API Settings 

API queries are used for the User risk assessment feature to query Azure identity protection graph API endpoint to retrieve user risk levels. NOTE, API endpoint is accessed by ADFS Servers (not web application proxy servers). Most likely ADFS servers will need to access API endpoint via corporate proxy if it is outside the corporate network. To allow access via proxy server ensure "proxy_enable" is set to "true". Otherwise, you’ll have to set system settings for default proxy settings. 

Below is a sample of a JSON configuration file that has time out value (ms) to terminate TCP sessions which take a long time to complete and continue on error value which specifies if Module should allow user access if API will fail to query Azure due to network or proxy errors or block it:

  "api_timeout": "5000",

  "api_on_error_continue": "false",

Proxy Configuration

API endpoint is accessed by ADFS Servers (not web application proxy servers). Most likely ADFS servers will need to access API endpoint via corporate proxy if it is outside corporate network. Proxy authentication is done under ADFS service account.

SecureMfaThreatDetectionModule.json config settings to enable proxy:

"proxy_enable": "true",

"proxy_server": "proxy.adatum.labnet",

"proxy_port": "8080",

As alternative you can configure system settings to enforce server to send messages via corporate proxy.

Logs

The below system events in Windows Event log may be used to verify and troubleshoot SecureMfa TDM Provider:

Log Name: AD FS/Admin

All provider related logs are stored in Windows Application Event logs and some data in SQL table or AD Attributes.

Windows Application Event:

Source: Secure MFA TDM

Event ID 5550: Successful Configuration Events

Event ID 5551: Failed Configuration Events

Event ID 5552: Successful Provider Events

Event ID 5553: Failed Provider Events

Event ID 5556: Successful Threat Detection Events

Event ID 5557: Failed Threat Detection Events

 Updates

Configuration changes in SecureMfaThreatDetectionModule.json can be applied without service restart by using following PS command

Update-SecureMfaThreatDetectionModuleConfig

PS command needs to be executed on ADFS servers (Not Web Application Servers). It will update running config without restarting ADFS service.

 Upgrade

Deployment of a new version can be done by pulling latest version from PowerShell Gallery by using bellow command:

Install-Module -Name SecureMFA_TDM -Repository PSGallery -Scope AllUsers -Froce

You’ll need to repeating all deployment steps as it was done for original installation.