Part 1: Blocking Bad Hosts – Finding Them, Easily

Download Script:

While troubleshooting some issues on an OWA Front-End server, I went over to the security log to see if the authentication attempts were getting past this box. The problem I found was the log was so full of failed logon attempts it was difficult to filter out what I was looking for. In a twelve hour period, there were thousands of 529 events in the security log. Now, I know this is nothing new, but I found a few patterns. I manually exported the log to a CSV, parsed out all the source ip addresses and opened it up in Excel. What I found was that 98.7% of failed logon attempts were made by just four different ip addresses.  (I recommend using MaxMind’s GeoIP Address Locator for help in determining where the source addresses are located.)

The easy fix is to setup an IPSec policy to block all traffic coming from those addresses.  And I did just that.  There are many different methods to blocking bad hosts.  And, anybody who has dealt with this knows, they will come back.  Just from different addresses.

One other piece I was able to get from this is that there are several of our users whom have fat fingered their passwords within ActiveSync (or they just have it setup wrong).  And then there are those users who have forgotten their domain, e-mail address, username, etc.

This is step one in my project to automate the blocking of bad hosts.  My goal is to build an automated method for blocking hosts with a high percentage of bad logon attempts within n hours.  And, even if I can’t get it 100% automated, this first whack at it took my bad logon attempts from 800 per hour to 25 per hour; nearly a 97% improvement!  And, a lot less crap to sift through when troubleshooting real issues.

So, my first goal is to automate a method of extracting the data I want from the event log.  (The script can be found below.)  The most important piece of data is the source ip address.  However, the other pieces of data I decided to extract could be helpful in determining the legitimacy of the logon failure.

To get the data, simply download the script and run “cscript /nologo get-bad-hosts.vbs > bad-hosts.csv” (w/o the quotes).  You can then open the file in Excel, do a quick pivot-table and identify the source addresses with the most hits.  At that point, you can add the host (or address block) to an ipsec policy to block all traffic from that address.

In my next post, I’ll explain the details in setting up a quick ipsec policy to block the bad guys.  Cheers!

' VBScript Source File
' NAME: get-bad-hosts.vbs
' AUTHOR: Andrew J Healey
' WEB:
' DATE  : Dec 21 2009
' COMMENT: This script will return data in csv format for use in determining
'	hack, lockout or bad logon attempts
' PROCESS: 1) query event log for event id 529; 2) parse data to return
'	useful data; 3) output to screen
' USAGE: cscript /nologo get-bad-hosts.vbs c:bad-hosts.csv

Option Explicit
On Error Resume Next

Dim strComputer, objWMIService, colLoggedEvents
Dim objEvent, objRegEx, colMatches, strMatch
Dim strUserName, strWorkstation, strIPAddress, strDomain

'Change to name of computer to query remote machine
strComputer = "."
Set objWMIService = GetObject("winmgmts:" _
    & "{impersonationLevel=impersonate}!\" & strComputer & "rootcimv2")

'Event 529 in the security log are "Failure Audit" for a "Logon/Logoff"
Set colLoggedEvents = objWMIService.ExecQuery _
        ("Select * from Win32_NTLogEvent Where Logfile = 'Security' and " _
            & "EventCode = '529'")

'Write header to screen
wscript.echo chr(34) & "User Name" & chr(34) & "," & _
			 chr(34) & "Workstation" & chr(34) & "," & _
			 chr(34) & "Source IP" & chr(34) & "," & _
			 chr(34) & "Windows Domain" & chr(34) & "," & _
			 chr(34) & "Date/Time" & chr(34)

'Loop through all events matching criteria above: 529 in sec log
For Each objEvent in colLoggedEvents
	'Use regex to parse any ip addresses from event
	' if no address found, goto next record
	Set objRegEx = CreateObject("VBScript.RegExp")
	objRegEx.Global = True
	objRegEx.IgnoreCase = True

	'Will return the line of the user name that was used
	objRegEx.Pattern = "(tUser Name:.*n)"
	Set colMatches = objRegEx.Execute(objEvent.Message)
	For Each strMatch in colMatches
	   strUserName = strMatch.Value

	'Will return the line of the workstation name that was used
	objRegEx.Pattern = "(tWorkstation Name:.*n)"
	Set colMatches = objRegEx.Execute(objEvent.Message)
	For Each strMatch in colMatches
	   strWorkstation = strMatch.Value

	'Will return the line of the source ip address
	objRegEx.Pattern = "(tSource Network Address:.*n)"
	Set colMatches = objRegEx.Execute(objEvent.Message)
	For Each strMatch in colMatches
	   strIPAddress = strMatch.Value

	'Will return the line of the domain that was used
	objRegEx.Pattern = "tDomain:(.*n)"
	Set colMatches = objRegEx.Execute(objEvent.Message)
	For Each strMatch in colMatches
	   strDomain = strMatch.Value

	'Output clean csv line for easy reading in spreadsheet program
	wscript.echo chr(34) & GetCleanText(strUserName) & chr(34) & "," & _
				 chr(34) & GetCleanText(strWorkstation) & chr(34) & "," & _
				 chr(34) & GetCleanText(strIpAddress) & chr(34) & "," & _
				 chr(34) & GetCleanText(strDomain) & chr(34) & "," & _
				 chr(34) & WMIDateStringToDate(objEvent.TimeWritten) & chr(34)

Private Function GetCleanText(byVal strText)
	'This will cleanup the text and return only necessary data
	' I'm sure there is a better way to do this :)
	dim tmp,txt
	tmp = Split(strText,":")
	txt = tmp(1)
	txt = Replace(txt,vbTab,"")
	txt = Replace(txt,vbCrLf,"")
	txt = Trim(txt)
	GetCleanText = txt
End Function

Private Function WMIDateStringToDate(byVal dtmEventDate)
	'Borrowed from the "Hey, Scripting Guy! BLOG"
	' --> Search: WMIDateStringToDate
    WMIDateStringToDate = CDate(Mid(dtmEventDate, 5, 2) & "/" & _
        Mid(dtmEventDate, 7, 2) & "/" & Left(dtmEventDate, 4) _
            & " " & Mid (dtmEventDate, 9, 2) & ":" & _
                Mid(dtmEventDate, 11, 2) & ":" & Mid(dtmEventDate, _
                    13, 2))
End Function


  1. For those that get a vbs error after copying/pasting the above script – you have to first do a quick search/replace of ‘&’ with ‘&’

  2. aagh my above comment got mangled. You need to replace ampersand-a-m-p-semicolon with the ampersand symbol

Comments are closed.