Are disconnected RDP sessions ticking time bombs in your network?

I think we’ve all been there before – you log on to a server remotely via RDP, and do the needful – but don’t immediately log off. But then you get distracted by a phone call, an email, a chat, or a good old-fashioned physical interaction with another human being.  So when it comes time clock out for the night, you shut down your computer or log off. Or maybe you’ve been working on a laptop and your VPN got interrupted. The result is the same – previously active RDP sessions are automatically disconnected. Or maybe one (not you, of course) purposely disconnects their session so that the next login process is faster.

Well whatever may be the reason, a key issue with disconnected RDP sessions is that, by default, they remain connected indefinitely until the server gets rebooted, you login again (because you forgot to do something?), or a coworker notices the offense and politely requests you log off.

At this point you’re probably thinking “OK, yes, but who really cares“? With all the serious stuff that’s going on around you, you’re probably wondering why the heck you suddenly need to worry about disconnected RDP sessions? Well, disconnected RDP sessions can be problematic for a number of reasons:


Potential Issues

Confusion

It can be confusing when you’re logging on to a server and notice that another user is logged on, even if it’s just a disconnected session. Are they doing important stuff? Can the other user by logged off? Or you’re trying to reboot a server, but the infamous “Other users are currently logged on … ” message pops up. Assuming you’re polite, you’ll reach out to the disconnected fellow to ensure that the user isn’t running any important apps. In short, it’s a pain. And we all have enough pain as it is, do we not?

Security

So this is the fun one and, I admit, the real reason for this blog post (no, it was not the confusion). Turns out that any local administrator has the ability to hijack any disconnected session on the server he or she is an administrator on. And while this doesn’t sound like a huge problem at first (if the user is already an admin, then he can do anything blah blah), there is a very specific scenario where this is a significant security risk. Being an admin on a machine is a great privilege, but hardly the key to the kingdom. If a domain admin (or even worse, an Enterprise Admin!) however logs on to the same host and disconnects their session, then the local admin can promote himself/herself to domain admin at no cost (EventSentry would of course detect this) by simply taking over the disconnected session. The process involves only a handful of commands, one of which is creating a temporary service (EventSentry would detect this also).

Resource Consumption

Processes from disconnected sessions do continue to run and consume some processing power, which can be an issue in environments with limited computing power or cloud environments where you pay for CPU time. It’s unlikely to have a large impact but it could add up for larger deployments.

I’m hoping that we can (almost) all agree now that disconnected sessions have a number of drawbacks, so let’s move on to ways to address this.


Solutions

User Education

While there are ways to forcefully log off idle users (see below), I think it’s important to make your admins aware that disconnected sessions pose a security risk, but also communicate to your users in general that disconnected sessions should be avoided when necessary. While this won’t solve the problem entirely, it will hopefully get some of your users to be more aware.

Notifications

EventSentry users can utilize filter timers to get notified when a user has been logged on to a server too long. Those not familiar with filter timers can learn more about them here. In short, filter timers work with events that are usually logged in pairs – such as a process start/process end, logon/logoff, service stop/service start etc – and can notify you when such an event pair is incomplete. For example, a logon is recorded but not followed by a logoff within a certain amount of time (the time period is configurable).

So in this scenario, a user’s logon event would start a filter timer that essentially puts the original event on hold. It won’t forward it to a notification just yet – only if the timer expires and no subsequent event has deleted the timer. A later logoff event (that is linked to the logon event via some property of the event like a login ID) would end the filter timer. If the logoff event does not happen (or happen too late), EventSentry will release the pending (logon) event and can send a notification to let either the team or the user know (if the login pattern matches email addresses) that the filter time has expired.

BONUS: If the Windows username can be matched to an email address (e.g. username is john.doe and the email address is john.doe@mycorp.com), then EventSentry can even send an email directly to the user (opposed to a generic email) reminding them to log off. Even better, the email can be customized and provide specific instructions. See KB 465 for instructions on how to configure this in EventSentry. The screenshot below shows such a customized email.

Disclaimer: This method doesn’t necessarily distinguish between users who are logged on and users who have disconnected sessions. It detects that a user has had a Windows session active for an extended time period. In most cases this should work reasonably well to detect disconnected sessions as well. especially with longer timer periods (since most users don’t need to be logged on to a server for many hours).

Forcibly logging off disconnected sessions

When you tried your best the nice way using education and emails and it’s just not working, well, then it turns out that Windows allows you to use the nuclear button – logging off disconnected user sessions after a certain time. Since this can be configured via group policy, implementing this is fairly easy. Simply create a new group policy and assign it to appropriate OU(s) and configure the following setting:

Local Computer Policy
   |-- Computer Configuration
       |-- Administrative Templates
           |-- Windows Components
               |-- Remote Desktop Services
                   |-- Remote Desktop Session Host
                       |-- Session Time Limits

Then simply enable the option and configure an appropriate time limit. The available timeout options are actually quite useful and range from 1 minute (which pretty much converts a disconnect with a logoff) all the way up to 5 days (which is better than nothing I suppose, but why even bother at that point?). What option you select here largely depends on how concerned you are about resource usage and sessions being hijacked. I personally would recommend a lower end of 30 minutes up to 8 hours at the maximum.

Validating RDP settings with EventSentry Validation Scripts

EventSentry includes two validation scripts that validate whether RDP session timeouts are enabled:

1. Disconnect sessions after user is idle for X minutes
2. Logoff disconnected sessions after X minutes

Conclusion

Disconnected RDP sessions aren’t an immediate security risk, since they require an intruder to somehow gain admin rights on a domain member machine first. Sophisticated attacks however rarely involve just one step, but usually take advantage of multiple vulnerabilities and exploits. Since there are few to no downsides to notifying or even logging off users after a certain amount of time, I would recommend to follow the recommendations outlined in this blog post.

RDProtector: Automatically blocking malicious IPs from RDP with EventSentry

RDProtector: Automatically blocking malicious IPs from RDP with EventSentry

The recently discovered BlueKeep RDP vulnerability reminds us yet again (as if we needed to be reminded) that monitoring RDP is not a luxury but an absolute necessity.

Many organizations still expose RDP ports to the Internet, making it a prime target for attacks. But even when RDP is only available internally it can still pose a threat – especially for large networks.

So let’s start this off with some very basic best practices:

  • Make sure that RDP access is blocked from the Internet (e.g. only accessible via VPN)
  • RDP should be disabled on hosts where it’s not needed
  • All RDP access should be monitored (see below)

In this post you will see how EventSentry (and EventSentry Light) can be configured to automatically block remote hosts that have failed to log on via RDP after a certain number of times. Utilizing EventSentry offers a number of benefits over other approaches:

  1. It works with any version of Windows, from Windows 2008 to Windows 2019
  2. It works regardless of account lockout policies
  3. The threshold and time period are fully configurable
  4. The default action (block Windows firewall) can be substituted and/or supplemented with other actions

Before we delve into the nitty gritty details I need to level the playing field and explain why blocking remote RDP connection attempts is not as simple as linking event id 4625 with type 10 (failed RDP logon attempt) with an action. See, in the good old days security events logged by Windows mostly meant what they said. Failed logon events logged by Windows always included the correct logon type – all the way back to Windows Server 2003 (back then it was event 529). Having an event that included both the username, IP address and logon type made it straightforward to setup a rule:

If # of failed logons with type 10 of a [user] and/or from [IP address] > [threshold] then do [ABC].

All that changed with the introduction of NLA (Network Level Authentication), where the initial authentication of a RDP session is offloaded to another Windows subsystem, resulting in key information being lost in translation inside Windows.

The result is that starting with Windows 2008 and NLA enabled, event id 4625 always classify failed RDP logon attempts as logon type 3 instead of logon type 10. As a reminder, logon type indicates a network logon – not a RDP logon. It’s consequently impossible to use 4625 events as the sole indicator for a failed RDP logon.

Security Event 4625 with Logon Type 3 (network logon)

How do you know if NLA is enabled? It’s usually pretty simple: If you are prompted for credentials when initiating a RDP connection before you see the Windows logon screen then NLA is enabled.

In an effort to better audit RDP connectivity events, Windows 2008 and later include a new event log, the Microsoft-Windows-RemoteDesktopServices-RdpCoreTS/Operational log, which logs some RDP activity. I say some because it cannot be used to solely detect failed RDP logins. While we have been able to consistently generate events when a remote client connects (event id 131), we have been unable to consistently generate the more important event id 140, which indicates a failed login (which could be used in place of the 4625 event to trigger an action).

Microsoft-Windows-RemoteDesktopServices-RdpCoreTS event 131

So what are we to do? On the one hand we have an event telling us that a RDP connection has been initiated (although not fully logged on yet), and on the other hand we have a failed logon event that is virtually identical to hundreds or thousands of other failed logon events.

Thankfully there is an easy solution with EventSentry’s filter chaining feature, which allows us to correlate events from the security event log with the new RdpCoreTS event log. This allows us to correlate audit failure event 4625 from the Security event log with information event 131 that is logged in the Microsoft-Windows-RemoteDesktopServices-RdpCoreTS/Operational event log

Filter chaining is activated on the package level, and can trigger an action (e.g. email, process, …) when all filters in a package match events in a certain time period. To make sure that the correct types of events are chained (correlated) together, insertion strings sharing the same data can be specified. And since both events include the IP address of the remote host connecting, they can be linked (chained) together if they occur within a certain time frame (e.g. 10 seconds).

When linking events via insertion strings it’s important that the strings match exactly, any deviation will break the chain. This turns out to be a potential issue since event id 131 doesn’t just log the remote IP address but also the remote source port in a single string (e.g. 192.168.1.1:33544). Event 4625 also logs the remote IP and source port, but in different fields.

To address this, EventSentry includes a feature that can override existing insertion strings (or create new ones if none already exist) which comes in handy in this scenario. In the case of the event 131 we can use a RegEx pattern to simply remove the remote port from the string so that we only end up with the IP address – as the only insertion string.

Transforming or creating insertion strings (meta data) using RegEx expressions

The diagram below visualizes how the failed RDP login detection works with EventSentry. When an unsuccessful login via RDP occurs (1), whether or not NLA is enabled (2) determines which type of 4625 event will be logged by Windows. The RDP subsystem logs event 131 either way (3), but we utilize it when NLA is active. Without NLA we simply utilize event 4625 (4) as the trigger for one or more actions, whereas with NLA being active we need to evaluate two different events.

With NLA enabled, event id 131 is evaluated first (5). Since event 131 is logged regardless of whether the subsequent authentication is successful or not, it needs to be correlated with a potential subsequent 4625 security event (8). In order to correlate those two events based on the IP address however, the remote port needs to be removed from event 131 so that only the IP address remains (6). Once event 131 is registered and reformatted, EventSentry will look for subsequent 4625 events (8) with a matching IP address (7).


Note: Since blocking every failed RDP-based authentication could lock out legitimate users that enter an incorrect password by accident, it’s highly recommend to add a threshold for event 4625 (8). When downloaded from EventSentry, our 4625 filter has a default threshold of 3 in 1 minute per IP address. This means that hosts will be blocked if an incorrect password is specified 4 times within 1 minute (from the same IP address, that’s what insertion string 20 is for).

Filter threshold configuration

Correlating multiple events is the nature of a filter chaining package (9), which requires that all events listed in the package match during a specified time interval. Once all filters (131 + 4625 in this case) match, EventSentry will log event id 10650 to the application event log, specifying the name of the filter chaining package along with the time span and insertion string(s), the ip address in this case (10). That event is then used as the trigger for one or more actions (11), such as blocking the remote IP using the Windows firewall and/or for sending an email alert.

Blocking an IP address with Windows Firewall is easy and can be done with the netsh.exe command, for example:

%SYSTEMROOT%\system32\netsh.exe advfirewall firewall add rule name="$STR3 $YEAR-$MONTH-$DAY -- automatic block by EventSentry" dir=in interface=any action=block remoteip=$STR3/32

$YEAR, $MONTH and $DAY are variables that are generally always available in EventSentry, and $STR3 is the third insertion string from whichever event triggered the action. In our example we trigger netsh from event id 10650, which specifies the IP address in its insertion string %3:

The filter chain for event log package %1 is complete.

Duration: %2 second(s)
Insertion Strings (if any): %3

Below is the actual event as it would be found in the EventSentry event viewer. You can view the insertions strings with the EventSentry management console under Tools -> Utilities -> Event Message Browser or with the EventSentry SysAdmin Tools.

After we put everything together in EventSentry we end up with the following:

1. A Filter Chaining Package (“RDProtector”) which logs the above event when it detects failed RDP logons
2. A filter that triggers the firewall blocking from event 10650 (“Block Failed RDP IP”)
3. An action (“Block IP with Windows Firewall”) that calls netsh.exe to block an IP address

Newer EventSentry installations include the RDProtector package out of the box, but the package can also be downloaded through the Tools -> Download Packages feature. Keep in mind that both the “Corrective Actions” package and the “Block IP with Windows Firewall” action need to be created manually, their respective configuration is shown below.

The process command line (“Arguments”) should be: advfirewall firewall add rule name=”$STR3 $YEAR-$MONTH-$DAY — automatic block by EventSentry” dir=in interface=any action=block remoteip=$STR3/32

Rules added to the Windows firewall are perpetual of course, which – depending on the number of blocks – may result in a large number of Windows firewall rules. A somewhat easy work-around would be to launch a script that:

1. Creates the firewall rule
2. Waits a certain amount of time (e.g. 5 min)
3. Deletes the firewall rule again

A script with a 3-minute timeout would look slimilar to this:

advfirewall firewall add rule name="$STR3 $YEAR-$MONTH-$DAY -- automatic block by EventSentry" dir=in interface=any action=block remoteip=$STR3/32

timeout /t 180

advfirewall firewall delete rule name="$STR3 $YEAR-$MONTH-$DAY -- automatic block by EventSentry" dir=in

Stay safe out there.