• +65 6652 3398
  • info@wgrow.com
  • 114 Lavender St, #07-51 CT Hub 2

wGrow - Team Notes

Sharing Expertise: Tech Insights and Case Studies

Creating a Windows Service to Monitor Remote Desktop Authentication Attempts and Trigger Alerts

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

  1. Launch Visual Studio and create a new project by selecting "Windows Service (.NET Framework)" template.
  2. Name the project "RDPMonitorService" and click "Create".

Step 2: Add required NuGet packages

  1. Right-click on the project and select "Manage NuGet Packages".
  2. 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

  1. Open the "Service1.cs" file and rename the class to "RDPMonitorService".
  2. 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

  1. Build the RDPMonitorService project.
  2. Open a command prompt with administrative privileges and navigate to the directory containing the compiled RDPMonitorService.exe.
  3. Install the service using the following command: `sc create RDPMonitorService binPath= "C:\path\to\RDPMonitorService.exe"`
  4. 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.

  1. Add a new EventLog component to the RDPMonitorService class and name it "eventLog".
  2. Set the "Log" property of the eventLog component to "Application".
  3. 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:

  1. 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.
  2. Optimize the EventLogWatcher: Adjust the EventLogQuery's BatchSize property to improve the service's performance on systems with a high volume of events.
  3. 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:

  1. 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.
  2. 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.


 

Related

Case Study: Deployment of Bluetooth Components for Airpufying Equipment Management

Case Study: Deployment of Bluetooth Components for Airpufying Equipment Management

Deploy Bluetooth components into the equipment and design a Server-Based System with a Web Interface...

Read More >
Case Study: Virtualizing a Legacy Windows Server 2000 Application for Improved Security and Maintainability

Case Study: Virtualizing a Legacy Windows Server 2000 Application for Improved Security and Maintainability

In this case study, we describe the process of virtualizing a legacy Windows Server 2000 application...

Read More >
Implementing a Global Chemical Compliance Check System for a Multinational Corporation

Implementing a Global Chemical Compliance Check System for a Multinational Corporation

In the complex world of global chemical imports, multinational corporations face the challenge of na...

Read More >
Creating a Desktop Application to Backup Gmail Emails and Restore to Gmail or Yahoo Email using C#

Creating a Desktop Application to Backup Gmail Emails and Restore to Gmail or Yahoo Email using C#

Backing up important emails from your Gmail account is essential to ensure data security and availab...

Read More >
Exploring Reflection in C#: Dynamically Accessing Object Properties and Database Operations

Exploring Reflection in C#: Dynamically Accessing Object Properties and Database Operations

Reflection is a powerful feature in C# that allows us to inspect and interact with the metadata of t...

Read More >
Streamlining Monthly Billing Reports for a Singapore Energy Company: A .NET-Based Solution

Streamlining Monthly Billing Reports for a Singapore Energy Company: A .NET-Based Solution

Efficient and accurate billing processes are crucial to the financial health of a business. In the c...

Read More >
Contact Us
  • Our Address:
    114 Lavender Street, #07-51, CT Hub 2, Singapore 338729
    Malaysia Johor - 99-01 Jalan Adda 3/1 Taman Adda Height 81100 Johor Bahru Johor, Malaysia
  • Phone Number:
    +65 6652 3398
  • WhatsApp:
    WhatsApp Us
  • Email:
    info@wgrow.com