Part 1: Blocking Bad Hosts - Finding Them, Easily

Down­load Script: get-bad-hosts.zip

While trou­bleshoot­ing some issues on an OWA Front-End serv­er, I went over to the secu­ri­ty log to see if the authen­ti­ca­tion attempts were get­ting past this box. The prob­lem I found was the log was so full of failed logon attempts it was dif­fi­cult to fil­ter out what I was look­ing for. In a twelve hour peri­od, there were thou­sands of 529 events in the secu­ri­ty log. Now, I know this is noth­ing new, but I found a few pat­terns. I man­u­al­ly export­ed the log to a CSV, parsed out all the source ip address­es and opened it up in Excel. What I found was that 98.7% of failed logon attempts were made by just four dif­fer­ent ip address­es.  (I rec­om­mend using MaxMind’s GeoIP Address Loca­tor for help in deter­min­ing where the source address­es are locat­ed.)

The easy fix is to set­up an IPSec pol­i­cy to block all traf­fic com­ing from those address­es.  And I did just that.  There are many dif­fer­ent meth­ods to block­ing bad hosts.  And, any­body who has dealt with this knows, they will come back.  Just from dif­fer­ent address­es.

One oth­er piece I was able to get from this is that there are sev­er­al of our users whom have fat fin­gered their pass­words with­in ActiveSync (or they just have it set­up wrong).  And then there are those users who have for­got­ten their domain, e-mail address, user­name, etc.

This is step one in my project to auto­mate the block­ing of bad hosts.  My goal is to build an auto­mat­ed method for block­ing hosts with a high per­cent­age of bad logon attempts with­in n hours.  And, even if I can’t get it 100% auto­mat­ed, this first whack at it took my bad logon attempts from 800 per hour to 25 per hour; near­ly a 97% improve­ment!  And, a lot less crap to sift through when trou­bleshoot­ing real issues.

So, my first goal is to auto­mate a method of extract­ing the data I want from the event log.  (The script can be found below.)  The most impor­tant piece of data is the source ip address.  How­ev­er, the oth­er pieces of data I decid­ed to extract could be help­ful in deter­min­ing the legit­i­ma­cy of the logon fail­ure.

To get the data, sim­ply down­load 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 piv­ot-table and iden­ti­fy the source address­es with the most hits.  At that point, you can add the host (or address block) to an ipsec pol­i­cy to block all traf­fic from that address.

In my next post, I’ll explain the details in set­ting up a quick ipsec pol­i­cy to block the bad guys.  Cheers!

'==========================================================================
' VBScript Source File
' NAME: get-bad-hosts.vbs
' AUTHOR: Andrew J Healey
' WEB: https://www.healey.io/
' 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
	Next

	'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
	Next

	'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
	Next

	'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
	Next

	'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)
Next

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

6 comments

  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 com­ment got man­gled. You need to replace amper­sand-a-m-p-semi­colon with the amper­sand sym­bol

Comments are closed.