Remote Desktop Protocol (RDP) is a widely used feature in Windows to access a computer remotely. However, it can also be a target for malicious activities, such as brute force attacks. In this article, we will create a Windows Service that monitors the Windows Event Log for multiple failed RDP authentication attempts within a specific time frame, sends an email alert to the server owner, and blocks the IP address temporarily.
Prerequisites
- Familiarity with C# and the .NET Framework
- Visual Studio installed on your system
- Administrative access to the target Windows machine
Step 1: Create a new Windows Service project
- Launch Visual Studio and create a new project by selecting "Windows Service (.NET Framework)" template.
- Name the project "RDPMonitorService" and click "Create".
Step 2: Add required NuGet packages
- Right-click on the project and select "Manage NuGet Packages".
- Install the following packages:
- System.Configuration.ConfigurationManager
- MailKit
Step 3: Create a custom EventLogWatcher class
Create a new class named "EventLogWatcher" to handle monitoring and processing events from the Windows Event Log:
using System;
using System.Diagnostics.Eventing.Reader;
using System.Threading;
public class EventLogWatcher
{
private EventLogQuery _query;
private EventLogReader _reader;
private CancellationTokenSource _cancellationTokenSource;
public EventLogWatcher(string logName, string xpathFilter)
{
_query = new EventLogQuery(logName, PathType.LogName, xpathFilter);
_reader = new EventLogReader(_query);
_cancellationTokenSource = new CancellationTokenSource();
}
public void Start(Func eventHandler)
{
if (eventHandler == null) throw new ArgumentNullException(nameof(eventHandler));
ThreadPool.QueueUserWorkItem(async _ =>
{
while (!_cancellationTokenSource.Token.IsCancellationRequested)
{
var eventInstance = await _reader.ReadEventAsync().ConfigureAwait(false);
if (eventInstance != null)
{
if (eventHandler(eventInstance)) break;
}
}
});
}
public void Stop()
{
_cancellationTokenSource.Cancel();
}
}
Step 4: Implement the RDP monitoring logic
- Open the "Service1.cs" file and rename the class to "RDPMonitorService".
- Implement the required logic as follows:
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Threading.Tasks;
using MailKit.Net.Smtp;
using MimeKit;
public partial class RDPMonitorService : ServiceBase
{
private const int FailedLoginThreshold = 3;
private const int TimeFrameMinutes = 5;
private const int BlockDurationHours = 24;
private EventLogWatcher _eventLogWatcher;
private Dictionary> _failedAttempts;
public RDPMonitorService()
{
InitializeComponent();
_failedAttempts = new Dictionary>();
}
protected override void OnStart(string[] args)
{
_eventLogWatcher = new EventLogWatcher("Security", "*[System[Provider[@Name='Microsoft-Windows-Security-Auditing'] and (EventID=4625)]]");
_eventLogWatcher.Start(ProcessEvent);
}
protected override void OnStop()
{
_eventLogWatcher.Stop();
}
private bool ProcessEvent(EventRecord eventRecord)
{
var ipAddress = eventRecord.Properties[19].Value.ToString();
var timestamp = eventRecord.TimeCreated.GetValueOrDefault();
if (!_failedAttempts.ContainsKey(ipAddress))
{
_failedAttempts[ipAddress] = new List();
}
_failedAttempts[ipAddress].Add(timestamp);
// Remove attempts older than the specified time frame
_failedAttempts[ipAddress] = _failedAttempts[ipAddress].Where(attempt => (timestamp - attempt).TotalMinutes <= TimeFrameMinutes).ToList();
if (_failedAttempts[ipAddress].Count >= FailedLoginThreshold)
{
// Trigger email and block the IP address
Task.Run(() => SendEmail(ipAddress));
Task.Run(() => BlockIPAddress(ipAddress));
// Clear the tracked attempts for the blocked IP address
_failedAttempts[ipAddress].Clear();
}
return false;
}
private async Task SendEmail(string ipAddress)
{
var emailMessage = new MimeMessage
{
Subject = "RDP Brute Force Alert",
Body = new TextPart("plain")
{
Text = $"Multiple failed RDP login attempts were detected from IP address: {ipAddress}. The IP address has been temporarily blocked for {BlockDurationHours} hours."
}
};
emailMessage.From.Add(new MailboxAddress("RDPMonitor Service", "monitor@wgrow.com"));
emailMessage.To.Add(new MailboxAddress("Server Owner", "owner@wgrow.com"));
using (var client = new SmtpClient())
{
await client.ConnectAsync("smtp.wgrow.com", 587, false);
await client.AuthenticateAsync("username", "password");
await client.SendAsync(emailMessage);
await client.DisconnectAsync(true);
}
}
private void BlockIPAddress(string ipAddress)
{
var ruleName = $"RDPBlock_{ipAddress.Replace('.', '_')}";
// Add the firewall rule
var startInfo = new ProcessStartInfo
{
FileName = "netsh",
Arguments = $"advfirewall firewall add rule name={ruleName} dir=in action=block remoteip={ipAddress} protocol=TCP localport=3389",
RedirectStandardOutput = true,
UseShellExecute = false,
CreateNoWindow = true
};
Process.Start(startInfo)?.WaitForExit();
// Schedule a task to unblock the IP address after the specified duration
startInfo = new ProcessStartInfo
{
FileName = "schtasks",
Arguments = $"/Create /TN \"Unblock_{ruleName}\" /TR \"netsh advfirewall firewall delete rule name={ruleName}\" /SC ONCE /ST \"{DateTime.Now.AddHours(BlockDurationHours).ToString("HH:mm")}\" /F",
RedirectStandardOutput = true,
UseShellExecute = false,
CreateNoWindow = true
};
Process.Start(startInfo)?.WaitForExit();
}
}
Step 5: Install and test the service
- Build the RDPMonitorService project.
- Open a command prompt with administrative privileges and navigate to the directory containing the compiled RDPMonitorService.exe.
- Install the service using the following command: `sc create RDPMonitorService binPath= "C:\path\to\RDPMonitorService.exe"`
- Start the service using the command: `sc start RDPMonitorService`
Step 6: Logging and Monitoring
To keep track of the service's activity and performance, it's important to implement logging. In this example, we will use the built-in Windows Event Log to log relevant information.
- Add a new EventLog component to the RDPMonitorService class and name it "eventLog".
- Set the "Log" property of the eventLog component to "Application".
- Set the "Source" property of the eventLog component to "RDPMonitorService".
Now, you can use the eventLog.WriteEntry()
method to log messages throughout the RDPMonitorService. For example:
private void Log(string message, EventLogEntryType entryType = EventLogEntryType.Information)
{
eventLog.WriteEntry(message, entryType);
}
Call the Log()
method in the appropriate places in your code to log relevant information, such as when an email is sent, when an IP address is blocked, or when an error occurs.
Step 7: Adjusting Sensitivity and Performance
Depending on the size and scale of your environment, you may need to adjust the service's sensitivity and performance. For example, you can change the FailedLoginThreshold, TimeFrameMinutes, and BlockDurationHours constants to fine-tune the service's behavior.
Additionally, consider the following improvements:
- Implement caching: If the service is monitoring a high volume of events, caching recently seen IP addresses and their timestamps can help reduce the processing overhead.
- Optimize the EventLogWatcher: Adjust the EventLogQuery's BatchSize property to improve the service's performance on systems with a high volume of events.
- Use asynchronous programming: If the service needs to handle a large number of concurrent tasks, such as sending multiple emails simultaneously, consider using the async/await pattern to improve responsiveness.
Step 8: Securing the Service
To ensure the RDPMonitorService runs securely, follow these best practices:
- Run the service with the least privileges required: Avoid running the service as LocalSystem or Administrator. Create a dedicated user account with the minimum required permissions for the service to function correctly.
- Protect sensitive information: Store sensitive information such as email credentials securely using encryption or the Windows Data Protection API (DPAPI).
By following these steps, you can effectively monitor, fine-tune, and secure the RDPMonitorService to protect your RDP sessions from brute force attacks. It's important to remember that this service is only one layer of security, and additional measures such as strong password policies, multi-factor authentication, and regular security audits should be implemented to keep your systems secure.