Using Conditional Access Policies to Block Tor Exit Nodes in Entra ID
Background
Built into Microsoft Azure / Entra ID is the concept of conditional access policies which, if used sensibly, can be very powerful mechanisms for restricting access to your environment, yet they are relatively under utilised (in our opinion). With most breaches we see these days stemming from account compromise, and with most current adversarial toolkits having mechanisms for circumventing MFA, the importance of conditional access policies shouldn't be underestimated. These policies can be configured not just to prevent access to your MS 365 environment, but also anything that your users authenticate to via Entra ID.
Since we've dealt with numerous incidents where adversaries masked their location by tunneling through the TOR network, and because most organisations using Microsoft 365 don't have legitimate use cases for users accessing their environment via TOR, here is a quick 101 on how to set up a conditional access policy that block access from TOR exit nodes. Obviously this can be tweaked to apply to other lists too, if you are using our AiTM feed then just swap out the list of Tor IPs with our AiTM named location feed and you’ll be blocking authentication from AiTM toolkits we have identified.
Firstly, you'll find conditional access policies within your Azure portal here:
https://entra.microsoft.com/#view/Microsoft_AAD_ConditionalAccess/ConditionalAccessBlade/~/Overview
Or if you are searching in the search bar you'll find them named "Microsoft Entra Conditional Access"
In the policy we are going to create you want to be familiar with "Named locations", these are essentially lists of entries that you can use in your policies and they can be made up of countries or IP addresses/ranges. We're going to make use of the IP ranges locations to create our list and then, once we have created our named location, we'll create a conditional access policy to block all access from that location.
Creating a New Named Location
If you haven’t done so already browse to your conditional access page in Microsoft Entra ID and select “IP ranges location” to create a named location that includes a series of IP address ranges:
We get an option to manually add IPv4 and IPv6 ranges, or we can upload them as a list.
As of right now there are just over 1200 exit nodes, so we're not going to add these manually. Conveniently the Tor Project provides an easily accessible list of exit nodes here:
https://check.torproject.org/torbulkexitlist
Because named locations take lists of IP ranges, not lists of IP addresses, we need to convert this into a list of ranges. Each IP address in our Tor list is a /32 so we just need to add a "/32" to the end of each line. This can be done quite easily from the command line.
On Windows systems there are a couple of options that spring to mind:
for /f "delims=" %i in (filename) do echo %i/32 >> newfile
or using powershell:
Get-Content filename | ForEach-Object { $_ + "/32" } | Set-Content newfile
If you're on a Linux/Mac then sed will likely be the easiest approach for you:
sed -i 's/$/\/32/' filename
This should leave you with a file containing something like the following:
194.26.192.64/32
171.25.193.25/32
80.67.167.81/32
192.42.116.187/32
198.98.51.189/32
89.58.26.216/32
109.70.100.4/32
149.56.22.133/32
...
..
.
Then it is simply a case of uploading it and you'll be left with a list like the following:
Be aware that the IPs of Tor exit nodes is not static and does change over time, you should therefore ensure that you periodically update this named location.
Don't check the "Mark as trusted location", as this is not and we don’t inadvertently want to use it as one in other policies. Once you hit "Create" you should see your new named location appear in your named locations list.
The next step is to put this to actual use and create a conditional access policy which utilises this named location.
Creating a Conditional Access Policy
The next thing to do is to create the conditional access policy, so hit "policies" and then "New policy":
We have a bunch of options in creating our new policy, the first thing is the assignment (i.e. who we want to apply the policy to). Do take heed of the warning that Microsoft share and ensure that you don't lock yourself out. You can be far more granular than we have been and select individual users or groups if you wish:
We want our policy to apply to all cloud apps - so this will cover Microsoft 365 as well as any third party apps that our users authenticate to with their Entra ID account. If you wish to be more specific and not include some apps you can select apps individually.
Next, in the "Network" option is where we want to apply our Tor named location list to the policy, so chose "Selected networks and locations" and select the Tor exit nodes named location you created earlier and hit select:
The "conditions" section has several more options which allow you to be a lot more granular in how you apply your policy, so if you want to specify specific devices that this does/doesn't apply to etc. you can do that here. For this policy we're not touching this, but the network condition we just set automatically applies.
The "Grant" is a very important part of the policy, it is the place where you can inadvertently lock yourself out of your environment so make sure you understand the logic here. It is also the place where we see a lot of initial confusion. The simplest way of thinking about the grant is that, in our case, the grant applies to the named location that we have included. So if we select "block" then we will be blocking the contents of the named location. If we specify "grant" then we will be granting access to the IPs in that named location. Since we don't want the Tor IPs to be logging in we select "block".
It is also at this point that Microsoft will probably give you the option to exclude your current user from the policy just to ensure you don't lock yourself out. We do recommend following this guidance and ensuring that you have excluded at least yourself from this policy, even if you plan to test this in “Report-only” mode. Our typical preference is that, wherever we have block lists, we also apply an exclude list just in case.
We're not going to take advantage of any of the "Session" options, so we'll skip that.
The next piece is the "Enable policy" option. We'd typically advise starting in "Report-only" mode to ensure that you haven't inadvertently got your logic wrong and blocked your access - you can enable the policy in enforcing mode at a later date.
When a your conditional access policy is created and “on” any user attempting to log in from your block list (the Tor exit nodes list) will be met with this screen an will be unable to login:
If your policy is in “Report-only” mode then users will not be blocked from logging in, but the result of applying the policy will be reported in the logs for you to see. This leads us on to something that is not particularly clear with Microsoft Conditional access polices - what does success and/or failure in terms of a policy means? Is blocking a login from a Tor IP a success, or a failure?… The best way to figure this out is to jump into the logs.
Conditional Access Logs
You'll find your authentication logs here:
https://portal.azure.com/#view/Microsoft_AAD_ConditionalAccess/ConditionalAccessBlade/~/signInLogs
These show all authentication attempts against your environment and allow you to drill down into additional detail. Be warned, these logs often take about 20 minutes to update (no, we’re not sure how Microsoft consider that acceptable either), so the process of ensuring your policies are working as expected can be a very time consuming task as you wait 20 minutes between each change in order to check its effectiveness. It also means that if you inadvertently lock everyone out of your organisation you may not know about it for 20 minutes! It may also take several minutes for your conditional access policies to be applied, so bear this in mind and give Microsoft time to catch up when you run your tests.
This lag time is something you really want to bear in mind if you are using external tools to review the authentication logs in your environment as they will very likely also be impacted by this lag (the correct answer to this is not to stop using external tools, it’s for Microsoft to rectify this unacceptable delay).
Rants over, when you look at your logs there are a bunch of tabs available to you. For our purposes there are a few that are of particular interest:
Status - this tells us whether the authentication succeeded, failed, or was interrupted
Conditional Access - this tells us which conditional access policies were applied and if so whether they succeeded or failed
There is also additional depth if you click on the event. Of particular interest are the following tabs:
Conditional Access
Report-only
“Report-only” will tell you whether a conditional access policy that is operating in “Report-only” mode has succeeded, failed, or was not applied. The “Conditional Access” tab will do the same but is for policies that are enabled.
For the policy we have just created, whether in “Report-only” mode or if your policy is “on”, we will mostly see “Not applied” listed in the Result column. The reason for this is because our policy has not been applied, it is only applied when the condition we have specified is met - i.e. a user is authenticating from a Tor exit nodes IP address. Most of the time our users (presumably) won’t be logging in via Tor and so most of the time our policy won’t be applied because the conditions will not be met.
It’s when a policy is applied that Microsoft’s terminology can make things a little confusing. In these cases the “Result” we see is not the result of the conditional access policy being applied, but the result of the authentication. So a failure means that authentication failed and success means that authentication succeeded rather than it referring to the success or failure of a policy (after all is blocking an IP from logging in a success or a failure!?).
If that is still not particularly clear here is a mini example from our test environment:
In the logs above our “Test Policy 1” policy blocks logins from a particular list of IP addresses, the result of this policy is a failure which means that the policy has been applied and the result of it being applied means that the user has failed to login. Essentially the conditions of the conditional access policy have been met and so the grant (in this case “Block”) has been applied.
Test Policy 1 failed the authentication early on, so the next policy “Require multifactor authentication” also failed.
The “Block access from Tor exit nodes” policy is listed as “Not Applied” because it has not been applied, the condition was not met for it to be - our user was not authenticating form a Tor exit node.
When this same user authenticates from an IP that is not in our “Test Policy 1” blocklist, and which is not a Tor exit node IP, our logs look like this:
In this case neither “Test Policy 1” nor “Block access from Tor exit nodes” has been applied because the IP address being used to log in was not in either of the named locations used in those lists (the authentication did not meet their conditions). We do, however, have a conditional access policy that requires multifactor authentication and this has been applied. Because the user has entered valid credentials and met the multi-factor authentication requirement they have successfully logged in, hence we see “Success” as the result.
Besides information on the success/failure of your conditional access policies there is loads of very interesting information in authentication logs, if you don’t spend time trawling through them on occasions I strongly recommend doing so.
Blocklist vs Allowlist
Obviously what we have taken you through here is how to use conditional access policies in order to apply a blocklist (a list of IPs that we want to block access from). If you can we’d always suggest applying an allowlist, i.e. a list of IP addresses allowed to log in - meaning anything not on that list cannot log in. The reason for this is that IP addresses can easily be changed and blocklists circumvented. Unfortunately, for most organisations, applying allowlists is far from a trivial task.
So whilst we’d advise allowlists as your default approach if you can, having seen certain characteristics present in most breaches, we know that certain blocklists would have prevented the the vast majority of the breaches we have seen, or at least provided a better means of detection. A blocklist of Tor exit nodes is definitely one of those lists that might have saved a lot of organisations, another list is our list of AiTM infrastructure which you can subscribe to here and which we’ll also keep up to date for you too.