How To Send Email Notification On Failed or Successful RDP Logon

This article was posted more than 1 year ago. Please keep in mind that the information on this page may be outdated, insecure, or just plain wrong today.

In server 2008, it’s quite easy to attach a task to an event. In EventViewer, simply right-click on the event and choose Attach Task to This Event. This is fine and dandy but sometimes, we want a little more granular control over when to actually fire the task; e.g., I typically want to only notify myself via email of failed RDP logons, not ALL failed logons.

It’s a little more complicated to get only the events you want based on some details inside the actual event – the Event Data.

Note: In Server 2008 the EventID for a Successful Logon is 4624 and for a Failed Logon is 4625. There are multiple Logon Types and you can reference them at this link from MSDN and adapt this guide to your liking. Look at the Logon Type section specifically.

These are the two XPath filters I’ll be working with and I will show you how to create 2 tasks based on 2 separate events:

1. Failed RDP Logon
2. Successful RDP Logon

XML for Failed RDP Logon

Note:  After doing some testing, it seems that LogonType 10 isn’t working for notification but LogonType 3 is.  I’m still looking at why this is the case since this is RDP logon I’m testing.

<QueryList>
  <Query Id="0" Path="Security">
    <Select Path="Security">*[System[(EventID=4625)]] and *[EventData[Data[@Name='LogonType'] and (Data=10)]]</Select>
  </Query>
</QueryList>

XML for Successful RDP Logon

<QueryList>
  <Query Id="0" Path="Security">
    <Select Path="Security">*[System[(EventID=4624)]] and *[EventData[Data[@Name='LogonType'] and (Data=10)]]</Select>
  </Query>
</QueryList>

1. Failed RDP Logon Task Setup

Launch Event Viewer (Start -> eventvwr.msc [ENTER])

Right Click “Security” event log and choose “Attach Task to this Log”
Give the task a meaningful name
Click Next on this screen
Choose an action to perform when our event is triggered. In this guide I’m going to be sending an email notification to myself so I chose ‘Send an e-mail.’
Fill in the details for the e-mail action settings portion of the wizard.
Make sure to check ‘Open the properties dialog…’ so that we can customize this action when we click finish.
Check to make sure you have this action set to run even if a user is not logged on and also that it is set to run at highest privilege level.
On the ‘Triggers’ tab, highlight the trigger and click ‘Edit’
Check ‘Custom’ then click ‘New Event Filter’
Click on the ‘XML’ tab and check ‘Edit query manually’
Paste in the Failed RDP Logon code from up above into this box. Of course you can substitute the eventid or the logontype if you know what you want in there already. =)
Click OK to close this dialog
Click ‘Ok’ again to close the properties for this task action
When you clicked ok, you may be prompted to enter the credentials of the user that will be running this task. Enter your credentials and click OK. (Typically the system administrator credentials)
Congrats, you just setup an email notification task when RDP logon fails.

2. Successful RDP Logon

Repeat all the steps from 1. Failed RDP Logon, simply replacing the XML EventID with 4624.

Manage Event Viewer Tasks

You can see all your Event Viewer Tasks by launching Task Scheduler (Start -> taskschd.msc [ENTER]) and choosing the Event Viewer Tasks folder.

Event Viewer Tasks

Hopefully this helps anyone out there that has been wondering how to do this.  Got any more tips?  Let  me know.  I’d be interested to know if you can specify variables in the email output – like other XPath variables that could be set or something.  Maybe even a specific event tag so that instead of directly emailing, you could write a short script that runs and will get the full details (Event Data) of the event and then embed it in an email.

I have found (Oh, glorious Google:  Ref 1, Ref 2) how to reference Event Data in the emails.  This makes me excited and now I’m cooking up commands also for dynamically adding IPs to RDP firewall for this case.

So, to be able to reference a variable from the Event Data, you need to add a ValueQueries element (MSDN link) to your Task.  The easiest way to do this is to export your task as XML and open it with notepad to edit it.  First, let’s look at some of the data options available to use as variables.  To get these, I like to find an event in the event viewer and look at the XML and look at the Data elements.

Friendly view of an event showing the data types

Let’s go back to what I was wanting to do originally – notify via email when a failed RDP logon occurs.  Now, I not only want to just send myself an email but I want to include some extra information.  In this case, I want to include the IpAddress as well as the TargetUserName.  This will tell me which IP address the logon came from as well as what user name the remote system tried to use.  Awesome!

So, fire up task scheduler (Start -> taskschd.msc [ENTER]) and navigate to the event viewer tasks item in the left pane.  Then, right-click the task we created earlier (Failed RDP Logon) and choose Export.

Save it somewhere convenient and then right-click and choose Edit (or open with notepad).

We need to add a ValueQueries element in the EventTrigger element.

You will want to add the ValueQueries element in the EventTrigger element.  Of course if you aren’t following what I’m doing, change them to something you’d rather see from your own available event data values.

       <ValueQueries>
        <Value name="IpAddress">Event/EventData/Data[@Name="IpAddress"]</Value>
    <Value name="TargetUserName">Event/EventData/Data[@Name="TargetUserName"]</Value>
       </ValueQueries>

Now that we have setup the variables, let’s add them to our email message.  This can be done from in this exported task easily.  Scroll to the bottom and look for the following block of code:

<Actions context="Author">

In that, modify your Body element to reflect your variables. Note: Variables are CAsE-SenSiTive!

In Conclusion

So in conclusion there are some really cool “tricks” we can do here to help monitor our systems.  Another cool thing would be to, instead of email notification, simply take the $(IpAddress) and pass it to a command such as, say,:

netsh advfirewall firewall add rule name="Block $(IpAddress) - Bad Dude" dir=in protocol=any action=block remoteip=$(IpAddress)

Now, how cool would that be?  =)

Show 15 Comments

15 Comments

  1. Firstly thanks for this awesome little script, it works a treat however I would like to let you know of 1 small typo in your script below.
    Event/EventData/Data[@Name=”IpAddress”] <— Small V
    Event/EventData/Data[@Name=”TargetUserName”]
    Please amend the Lower Case V in your script, it would appear that Microsoft is sometimes a little pedantic about case. (I thought that was only a Linux thing)
    The correct code is below for anyone else in the meantime:
    Event/EventData/Data[@Name=”IpAddress”]
    Event/EventData/Data[@Name=”TargetUserName”]

    • nick

      Thanks for pointing this out but sorry I can’t see any <— Small V":
      I would like to let you know of 1 small typo in your script below.
      Event/EventData/Data[@Name="IpAddress"] <— Small V
      Event/EventData/Data[@Name="TargetUserName"]
      The correct code is below for anyone else in the meantime:
      Event/EventData/Data[@Name="IpAddress"]
      Event/EventData/Data[@Name="TargetUserName"]
      I really don't see any difference !

  2. One nice feature that I would like would be the ability to have it only send an email once every 2 minutes instead of every event, I just had 30 emails come in, in just 2 minutes. Any ideas on that one?

  3. Content updated, thanks for that catch.
    Now, regarding the burst of emails…
    Once every 2 minutes of all the collected login failures?
    I believe that would be possible. There are a number of ways that I’m thinking this could be _possibly_ done.
    The only problem is that the nature of this task is created from an event so I think there would have to be some logic in there to identify if this task has run already within X minutes, and if so, has it been Y minutes long to generate the new Action (e.g., send another email). I’d have to think about this and do some research.
    One other idea is to go the route of not triggering on every event, but rather, run a task on a scheduled time to collect failed login events from event log. That may be a more viable option but it is not real-time. So you could collect past 5 minutes failed logins every 5 minutes and email it to yourself. Just a few of my thoughts on that last comment.

  4. Hi Rich,
    I’ve come up with a fairly rudimentary way of doing it.
    I’ve basically setup everything just like you suggested but added an extra ACTION to the scheduled task, which is setup to “Start a Program” then I created a batch file that just says “ping -n 100 127.0.0.1” and saved it as “wait.bat” which I used as the program for the second action.
    So basically what happens now is someone tries to guess the password, it logs a logon failure event. The task scheduler job executes and it sends an email and then starts the 100 Second ping.
    If the person attempts to hack again then the scheduled job won’t run because the first job is still running. And by default a scheduled task has the option set to “Do Not Start a New Instance” on the Settings Page.
    If you find a better way let me know but for now this seems to be working great. I will see how many hackers I can trap and foil their plans. We will most likely be implementing this on all our Cloud Servers.
    Brett

  5. Ah, nice workaround. I’ll tinker and see if I can come up with another way built-in to scheduler and let you know this week. Thanks for the follow-up and nice idea. =)

  6. I’m not using the native SMTP server, so I’m having problems sending the email because of the SMTP authentication. I solved by using SMTPMailSender (http://www.snapfiles.com/get/smtpmailsender.html).
    There is still a problem. This mail sender generates a login error creating an infinite loop. So I wrote a batch file that solves this problem and adds a new firewall rule:
    ——————————————————-
    IF %2==alberto@xxxxx.it GOTO FINE
    IF %1==11.222.33.44 GOTO FINE
    c:smtpsendersmtpmailsender.exe -t alberto@xxxxxx.it -f postmaster@xxxxxx.it -s “intrusion detected” -b “Intrusion from %1 with username %2” -username alberto@xxxxx.it -password mypassword -send
    netsh advfirewall firewall add rule name=”Block %1 – bad dude” dir=in protocol=any action=block remoteip=%1
    :FINE
    ——————————————————-
    the batch file is called with parameter 1 $(IpAddress) and parameter 2 $(TargetUserName).
    The second IF has been added to exclude my IP address from being blocked.
    Hope this helps.
    Ciao
    Alberto

    • Nice tip Alberto, thanks. I remember using a simple SMTP server as well. In my environment, I allow relay from this specific host on the SMTP server I’m sending through. Alleviates having to setup another SMTP workaround but this is nice instructions for doing so. Thanks again!

  7. Michael

    Great script.
    Anyone know what the ValueQueries element would be to retrive the computer name of the computer trying to logon?

    • @Michael
      Should be WorkstationName as seen in screenshot on this page captioned Friendly view of an event showing the data types
      The Network Information fields indicate where a remote logon request originated. Workstation name is not always available and may be left blank in some cases.

  8. Amit

    Hi,
    I am trying to get the IpAddress and the TargetUserName , but All i get is
    From IP $(IpAddress) By User $(TargetUserName)
    Any pointers please ?

    • Rich Kreider

      Which server are you using this on and did you make sure to use IpAddress and TargetUserName with CaseSenSitiVity?

  9. nick

    Hi Rich,
    Please can you explain how to auto-add to an already existing firewall rule the failed logon attempts.
    You said to use the:
    (netsh advfirewall firewall add rule name=Block $(IpAddress) – Bad Dude dir=in protocol=any action=block remoteip=$(IpAddress))
    But I presume this creates a new rule for each IP, or am I wrong? a better way is to add the IP’s into the same rule.
    Anyway how to put it in the xml file? where exactly to copy paste it.
    Please explain,
    Thank You so much for this useful post.

    • Rich Kreider

      Yes, this does create a new rule for each IP Address.
      You could look at the “set” verb instead of “add”. See here for more information on this verb: http://technet.microsoft.com/en-us/library/dd734783(v=ws.10).aspx#BKMK_3_set
      1. Use “add” to create a generic container called “Bad Dudes”
      2. Use “set” to add to the “Bad Dudes” container
      netsh advfirewall firewall add rule name=”Bad Dudes” dir=in protocol=any action=block remoteip=FirstIP
      Then, for the actual trigger portion of this you would use “set” to add a remote IP to the “Bad Dudes” rule by substituting “add” with “set” in the above line.
      I guess you could add another “action” to the task scheduler for this. I’m not sure exactly how to do it in the XML but I think this could be easily searched for.

Leave a Reply

Your email address will not be published. Required fields are marked *