We all know it’s key to have your security hygiene in order, a large part of that is your multi factor authentication deployment. Having all users use MFA these days is a no-brainer, but not all types of MFA are made equal. For example; MFA via text-message is generally considered unsafe.
But even the entered OTP codes are somewhat unsafe when compared to physical tokens or push-messages. Spear-phishers are now using proxy solutions or simply social engineering to extract the OTP code from users.
I have the philosophy that everything should be secure by design. So I like to offer my clients the most secure solutions. This does mean we also keep an eye on how they configure multi-factor authentication. When we do this for them we enforce that they use the push message option, and don’t use unsafe alternatives. We disable these alternatives in the portal too so that they can’t be setup directly.
This doesn’t always work out. sometimes the client sets up their own MFA, we take over a client or sometimes people just make mistakes. This is why we monitor the actual MFA type they’ve set up and try to assist the client in choosing the more secure alternative. To do that, we use the following script I hope it helps you in securing your environment too.
All Tenants Script
So this script checks if the primary option is set to an unsafe one, and alerts if it is. You could also use it to detect if MFA is configured, and what methods are used.
As always we use the Secure Application Model so we can run this script headless.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
$ApplicationId = 'xxxx-xxxx-xxxx-xxxx-xxx'
$ApplicationSecret = 'YOURSECRET' | Convertto-SecureString -AsPlainText -Force
$TenantID = 'xxxxxx-xxxx-xxx-xxxx--xxx'
$RefreshToken = 'LongResourcetoken'
$ExchangeRefreshToken = 'LongExchangeToken'
$credential = New-Object System.Management.Automation.PSCredential($ApplicationId, $ApplicationSecret)
$aadGraphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.windows.net/.default' -ServicePrincipal -Tenant $tenantID
$graphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.microsoft.com/.default' -ServicePrincipal -Tenant $tenantID
Connect-MsolService -AdGraphAccessToken $aadGraphToken.AccessToken -MsGraphAccessToken $graphToken.AccessToken
$customers = Get-MsolPartnerContract -All
$MFAType = foreach ($customer in $customers) {
$users = Get-MsolUser -TenantId $customer.tenantid -all
foreach ($user in $users) {
$primaryMFA = if ($null -ne $user.StrongAuthenticationUserDetails) { ($user.StrongAuthenticationMethods | Where-Object { $_.IsDefault -eq $true }).methodType } else { "MFA Disabled" }
$SecondaryMFA = if ($null -ne $user.StrongAuthenticationUserDetails) { ($user.StrongAuthenticationMethods | Where-Object { $_.IsDefault -eq $false }).methodType } else { "No Secondary Option enabled" }
[PSCustomObject]@{
"DisplayName" = $user.DisplayName
"user" = $user.UserPrincipalName
"Primary MFA" = $primaryMFA
"Secondary MFA" = $SecondaryMFA
}
}
}
$UnSafeMFAUsers = $MFAType | Where-Object { $_.'Primary MFA' -like "*SMS*" -or $_.'Primary MFA' -like "_voice_" -or $\_.'Primary MFA' -like "_OTP_" }
if (!$UnSafeMFAUsers) {
$UnSafeMFAUsers = "Healthy"
}
|
Single Tenant Script
This script pretty much does the same as above, but for a single tenant.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
$ApplicationId = 'xxxx-xxxx-xxxx-xxxx-xxx'
$ApplicationSecret = 'YOURSECRET' | Convertto-SecureString -AsPlainText -Force
$TenantID = 'xxxxxx-xxxx-xxx-xxxx--xxx'
$RefreshToken = 'LongResourcetoken'
$ExchangeRefreshToken = 'LongExchangeToken'
$TenantToCheck = 'tenant.onmicrosoft.com'
$credential = New-Object System.Management.Automation.PSCredential($ApplicationId, $ApplicationSecret)
$aadGraphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.windows.net/.default' -ServicePrincipal -Tenant $tenantID
$graphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.microsoft.com/.default' -ServicePrincipal -Tenant $tenantID
Connect-MsolService -AdGraphAccessToken $aadGraphToken.AccessToken -MsGraphAccessToken $graphToken.AccessToken
$customers = Get-MsolPartnerContract -All | where-object {$_.DefaultDomainName -eq $TenantToCheck}
$MFAType = foreach ($customer in $customers) {
$users = Get-MsolUser -TenantId $customer.tenantid -all
foreach ($user in $users) {
$primaryMFA = if ($null -ne $user.StrongAuthenticationUserDetails) { ($user.StrongAuthenticationMethods | Where-Object { $_.IsDefault -eq $true }).methodType } else { "MFA Disabled" }
$SecondaryMFA = if ($null -ne $user.StrongAuthenticationUserDetails) { ($user.StrongAuthenticationMethods | Where-Object { $_.IsDefault -eq $false }).methodType } else { "No Secondary Option enabled" }
[PSCustomObject]@{
"DisplayName" = $user.DisplayName
"user" = $user.UserPrincipalName
"Primary MFA" = $primaryMFA
"Secondary MFA" = $SecondaryMFA
}
}
}
$UnSafeMFAUsers = $MFAType | Where-Object { $_.'Primary MFA' -like "*SMS*" -or $_.'Primary MFA' -like "*voice*" -or $_.'Primary MFA' -like "*OTP*" }
if (!$UnSafeMFAUsers) {
$UnSafeMFAUsers = "Healthy"
}
|
Non Secure App Model script
So if you aren’t a Microsoft Partner but still want to execute this script, just use the following script.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
Connect-MsolService
$customers = Get-MsolPartnerContract -All | where-object {$_.DefaultDomainName -eq $TenantToCheck}
$MFAType = foreach ($customer in $customers) {
$users = Get-MsolUser -TenantId $customer.tenantid -all
foreach ($user in $users) {
$primaryMFA = if ($null -ne $user.StrongAuthenticationUserDetails) { ($user.StrongAuthenticationMethods | Where-Object { $_.IsDefault -eq $true }).methodType } else { "MFA Disabled" }
$SecondaryMFA = if ($null -ne $user.StrongAuthenticationUserDetails) { ($user.StrongAuthenticationMethods | Where-Object { $_.IsDefault -eq $false }).methodType } else { "No Secondary Option enabled" }
[PSCustomObject]@{
"DisplayName" = $user.DisplayName
"user" = $user.UserPrincipalName
"Primary MFA" = $primaryMFA
"Secondary MFA" = $SecondaryMFA
}
}
}
$UnSafeMFAUsers = $MFAType | Where-Object { $_.'Primary MFA' -like "*SMS*" -or $_.'Primary MFA' -like "_voice_" -or $\_.'Primary MFA' -like "_OTP_" }
if (!$UnSafeMFAUsers) {
$UnSafeMFAUsers = "Healthy"
}
|
And that’s it! as always, Happy PowerShelling!