04 Sep 2007
Blocking Dictionary Attacks on a Personal FTP site (IIS)
Thanks to some scripts I found on various sources, I was able to piece together this script to detect and block ever-increasing dictionary attacks against the Administrator account on my FTP server. Basically, you start the script using WScript at system load. It hooks itself into WMI events for ftp log file entries. When an entry is created, the log file is scanned for any IP address which has 5 or more bad login attempts in the log file. The code then checks to see if that IP has already been assigned the invalid gateway address, and adds the bad routing instruction for that IP if it has not already done so.
This is an enhancement to scripts I originally found at NetNerds.net and VisiMetrics.com, which provided the base code to create the dead route. I’ve added the following:
- a sink to gracefully handle a Windows shutdown
- a flag file located in the same folder as the script file, which will cause the script to terminate when deleted. It’s a way to kill a specific WScript process, instead of guessing which one of the umpteen WScript processes to remove in the task manager.
- Logging to the NT Application Event Log when starting, stopping, or creating bad route entries for attackers.
All you need to do to make the script run is change the values assigned to three variables.
- fakeGateWayIP = “192.168.1.200” ‘The black hole location in your sub-net to route the packets.
- xMax = 5 ‘Max number of invalid login attempts
- LogFiles = 10 ‘Max number of log files to keep in the folder before deleting them.
The beauty of this script is that it does not persist the bad routing instructions in other than the routing table, meaning that they are lost at the next reboot. If the attack occurs again, the bad route is just re-established after 5-10 attempts from the attacker. Truly a remarkably simple and effective technique. Thanks Chrissy and Tim!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 | REM FtpBlocker.vbs REM Launch once at system startup. Monitors the ftp activity for bad login attempts (Status Code 530). REM If more than X failed login attempts are tried by a client, it's IP is added REM to the routing table, with a fake gateway (dead IP) as the routing target. Designed to thwart REM dictionary attacks. REM This script works with Windows 2000 and later. Dim aIpRoute, bShuttingDown Function FileExists (sFileName) Dim oFSOSet oFSO = CreateObject("Scripting.FileSystemObject") FileExists = oFSO.FileExists(sFileName) set oFSO = Nothing End Function Sub DeleteFile (sFileName, bEvenIfReadOnly) Dim oFSO Set oFSO = CreateObject("Scripting.FileSystemObject") FileDelete = oFSO.DeleteFile(sFileName, bEvenIfReadOnly) set oFSO = Nothing End Sub Function FileToString (sFileName) Dim oFSO, oTS Set oFSO = CreateObject("Scripting.FileSystemObject") Set oTS = oFSO.OpenTextFile(sFileName, 1) FileToString = oTS.ReadAll oTS.Close() set oTS = Nothing set oFSO = Nothing End Function Sub StringToFile (sFileName, sText) Dim oFSO, oTS Set oFSO = CreateObject("Scripting.FileSystemObject") Set oTS = oFSO.OpenTextFile(sFileName, 2, True) oTS.Write sText oTS.Close() set oTS = Nothing set oFSO = Nothing End Sub Function RunOutput(cProgram, nWindowType) REM -- Obtain a Temporary File Name Dim oFS Set oFS = CreateObject("Scripting.FileSystemObject") Dim cFile cFile = oFS.GetSpecialFolder(2).Path & "\" & oFS.GetTempName REM -- Execute the command and redirect the output to the file Dim oShell Set oShell = CreateObject( "WScript.Shell" ) oShell.Run cProgram & " >" & cFile, nWindowType, True Set oShell = Nothing REM -- Read output file and return Dim oFile Set oFile = oFS.OpenTextFile(cFile, 1, True) RunOutput = oFile.ReadAll() oFile.Close REM -- Delete Temporary File oFS.DeleteFile cFile Set oFS = Nothing End Function Function GetRoutingEntries() REM Capture the content of the current routing table. aRaw = Split (RunOutput("%COMSPEC% /C ROUTE PRINT", 0), vbCrLf) Set o = new RegExp o.Pattern = "((\d)+\.){3}(\d)+" o.Global = True o.MultiLine = False o.IgnoreCase = False aIP = Empty For Each s In aRaw Set oMatches = o.Execute (s) If oMatches.Count = 4 Then If IsArray (aIP) Then ReDim Preserve aIP (UBound (aIP, 1) + 1) Else ReDim aIP (0) End If aIP (UBound (aIP, 1)) = oMatches.Item(0) & "|" & oMatches.Item(2) End If Next GetRoutingEntries = aIP End Function REM -- Log the startup to the event log. Set oWsh = WScript.CreateObject("WScript.Shell") oWsh.LogEvent 0, "FtpBlocker530.vbs begins" Set oWsh = WScript.CreateObject("WScript.Shell") REM Load a snapshot of the IP's in the routing table, and use this as the baseline for determining REM if the IP is in the list already. aIpRoute = GetRoutingEntries() bShuttingDown = False REM Push Event Viewer Alert Set objWMIService = GetObject("winmgmts:{(security)}!root/cimv2") Set eventSink = wscript.CreateObject("WbemScripting.SWbemSink", "EVSINK100_") strWQL = "Select * from __InstanceCreationEvent where TargetInstance isa 'Win32_NTLogEvent' and TargetInstance.SourceName = 'MSFTPSVC' and TargetInstance.EventCode = 100" objWMIService.ExecNotificationQueryAsync eventSink,strWQL Set objWMIService1 = GetObject("winmgmts:{(security)}!root/cimv2") Set eventSink1 = wscript.CreateObject("WbemScripting.SWbemSink", "EVSINK6006_") strWQL = "Select * from __InstanceCreationEvent where TargetInstance isa 'Win32_NTLogEvent' and TargetInstance.SourceName = 'eventlog' and TargetInstance.EventCode = 6006" objWMIService.ExecNotificationQueryAsync eventSink1,strWQL REM Create a flag file named FtpBlocker530.flg REM When this file is not on the system, it is a signal to terminate this application. StringToFile "FtpBlocker530.flg", "*** MARKER FILE ONLY ***" REM Keep it going forever, or until the marker file is missing. While (FileExists ("FtpBlocker530.flg") And Not bShuttingDown) Wscript.Sleep(1000) Wend If Not bShuttingDown Then Set oWsh = WScript.CreateObject("WScript.Shell") oWsh.LogEvent 0, "FtpBlocker530.vbs stopped by file removal" Set oWsh = Nothing Else DeleteFile "FtpBlocker530.flg", True End If Set eventSink = Nothing Set eventSink1 = Nothing Set objWMIService = Nothing Set objWMIService1 = Nothing Sub EVSINK6006_OnObjectReady(objObject, objAsyncContext) bShuttingDown = True End Sub Sub EVSINK100_OnObjectReady(objObject, objAsyncContext) Set objDictionary = CreateObject("Scripting.Dictionary") Set objFSO = CreateObject("Scripting.FileSystemObject") Set objLog = CreateObject("MSWC.IISLog") Set WshShell = WScript.CreateObject("WScript.Shell") fakeGateWayIP = "192.168.1.200" 'This is a black hole location on your subnet to route the packets. xMax = 5 'Max number of invalid login attempts xLogFiles = 10 'Max number of log files to keep in the folder before deleting them Set objFolder = objFSO.GetFolder(objFSO.GetSpecialFolder(0).Path & "\system32\LogFiles\MSFTPSVC1\") Set objFiles = objFolder.Files For Each fileName In objFiles lastFile = fileName Set f = objFSO.GetFile(fileName) If f.DateCreated <= Date - xLogFiles Then objFSO.DeleteFile FileName, True Set f = Nothing Next Set objFiles = Nothing Set objFolder = Nothing objLog.OpenLogFile lastFile, 1, "MSFTPSVC", 1, 0 While NOT objLog.AtEndOfLog objLog.ReadLogRecord clientIP = trim(objLog.ClientIP) xStatus = trim(objLog.ProtocolStatus) If xStatus = "530" AND NOT (clientIP = fakeGateWayIP) then If objDictionary.Exists(ClientIP) Then objDictionary.Item(clientIP) = cStr(Clng(objDictionary.Item(clientIP)) + 1) Else objDictionary.Add clientIP,"1" End If End If Wend objLog.CloseLogFiles 1 xChange = False For Each xClient in objDictionary.Keys xTest = False If Clng(objDictionary.Item(xClient)) < xMax then objDictionary.Remove(xClient) Else xTest = Not IsArray (aIpRoute) If Not xTest Then xTest = True For Each ip In aIpRoute If Split (ip, "|")(0) = xClient And Split (ip, "|")(1) = fakeGateWayIP Then xTest = False ' Client already exists in the routing table with the fakeGateWayIP as the gateway Exit For End If Next End If If xTest Then xChange = True WshShell.Run "%COMSPEC% /C ROUTE ADD " & xClient & " MASK 255.255.255.255 " & fakeGateWayIP, 0, True WshShell.LogEvent 1, "BAD ROUTE(s) assigned: FTP max of " & xMax & " occurrences for 530 Event(s) exceeded for " & xClient End If End If Next Set WshShell = Nothing Set objLog = Nothing Set objFSO = Nothing Set objDictionary = Nothing If xChange Then aIpRoute = GetRoutingEntries() End If End Sub |
Leave a Comment
You must be logged in to post a comment.